mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 22:58:11 +08:00
Desktop: Prefer Vulkan as the WGPU backend on Linux (#4270)
* Manual wgpu adapter selection * Fall back to remaining adapters when the preferred one fails to initialize
This commit is contained in:
@@ -1,20 +1,18 @@
|
|||||||
use crate::wrapper::{WgpuContext, WgpuContextBuilder, WgpuFeatures};
|
use crate::wrapper::{WgpuContext, WgpuContextBuilder, WgpuFeatures};
|
||||||
|
|
||||||
pub(super) async fn create_wgpu_context() -> WgpuContext {
|
pub(super) async fn create_wgpu_context() -> WgpuContext {
|
||||||
let wgpu_context_builder = WgpuContextBuilder::new().with_features(WgpuFeatures::IMMEDIATES);
|
let mut wgpu_context_builder = WgpuContextBuilder::new().with_features(WgpuFeatures::IMMEDIATES);
|
||||||
|
|
||||||
|
// TODO: make this configurable via cli flags instead
|
||||||
|
if let Some(index) = std::env::var("GRAPHITE_WGPU_ADAPTER").ok().and_then(|s| s.parse().ok()) {
|
||||||
|
tracing::info!("Overriding WGPU adapter selection with adapter index {index}");
|
||||||
|
wgpu_context_builder = wgpu_context_builder.with_selection(index);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add a cli flag to list adapters and exit instead of always printing
|
// TODO: add a cli flag to list adapters and exit instead of always printing
|
||||||
println!("\nAvailable WGPU adapters:\n{}", wgpu_context_builder.available_adapters_fmt().await);
|
println!("\nAvailable WGPU adapters:\n{}", wgpu_context_builder.available_adapters_fmt().await);
|
||||||
|
|
||||||
// TODO: make this configurable via cli flags instead
|
let wgpu_context = wgpu_context_builder.build().await.expect("Failed to create WGPU context");
|
||||||
let wgpu_context = match std::env::var("GRAPHITE_WGPU_ADAPTER").ok().and_then(|s| s.parse().ok()) {
|
|
||||||
None => wgpu_context_builder.build().await,
|
|
||||||
Some(adapter_index) => {
|
|
||||||
tracing::info!("Overriding WGPU adapter selection with adapter index {adapter_index}");
|
|
||||||
wgpu_context_builder.build_with_adapter_selection(|_| Some(adapter_index)).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.expect("Failed to create WGPU context");
|
|
||||||
|
|
||||||
// TODO: add a cli flag to list adapters and exit instead of always printing
|
// TODO: add a cli flag to list adapters and exit instead of always printing
|
||||||
println!("Using WGPU adapter: {:?}", wgpu_context.adapter.get_info());
|
println!("Using WGPU adapter: {:?}", wgpu_context.adapter.get_info());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
pub use graph_craft::application_io::resource::MmapResourceStorage;
|
pub use graph_craft::application_io::resource::MmapResourceStorage;
|
||||||
pub use graphite_editor::consts::{DOUBLE_CLICK_MILLISECONDS, FILE_EXTENSION};
|
pub use graphite_editor::consts::{DOUBLE_CLICK_MILLISECONDS, FILE_EXTENSION};
|
||||||
|
pub use wgpu_executor::WgpuBackends;
|
||||||
pub use wgpu_executor::WgpuContext;
|
pub use wgpu_executor::WgpuContext;
|
||||||
pub use wgpu_executor::WgpuContextBuilder;
|
pub use wgpu_executor::WgpuContextBuilder;
|
||||||
pub use wgpu_executor::WgpuExecutor;
|
pub use wgpu_executor::WgpuExecutor;
|
||||||
|
|||||||
@@ -18,12 +18,14 @@ impl Context {
|
|||||||
pub struct ContextBuilder {
|
pub struct ContextBuilder {
|
||||||
backends: Backends,
|
backends: Backends,
|
||||||
features: Features,
|
features: Features,
|
||||||
|
selection: Option<usize>,
|
||||||
}
|
}
|
||||||
impl ContextBuilder {
|
impl ContextBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
backends: Backends::all(),
|
backends: Backends::all(),
|
||||||
features: Features::empty(),
|
features: Features::empty(),
|
||||||
|
selection: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_backends(mut self, backends: Backends) -> Self {
|
pub fn with_backends(mut self, backends: Backends) -> Self {
|
||||||
@@ -34,21 +36,34 @@ impl ContextBuilder {
|
|||||||
self.features = features;
|
self.features = features;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub fn with_selection(mut self, index: usize) -> Self {
|
||||||
|
self.selection = Some(index);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
impl ContextBuilder {
|
impl ContextBuilder {
|
||||||
pub async fn build(self) -> Option<Context> {
|
pub async fn build(self) -> Option<Context> {
|
||||||
self.build_with_adapter_selection_inner(None::<fn(&[Adapter]) -> Option<usize>>).await
|
let instance = self.build_instance();
|
||||||
}
|
let mut adapters = enumerate_sorted(&instance, self.backends).await;
|
||||||
pub async fn build_with_adapter_selection<S>(self, select: S) -> Option<Context>
|
|
||||||
where
|
if let Some(index) = self.selection
|
||||||
S: Fn(&[Adapter]) -> Option<usize>,
|
&& index >= adapters.len()
|
||||||
{
|
{
|
||||||
self.build_with_adapter_selection_inner(Some(select)).await
|
let selected_adapter = adapters.remove(index);
|
||||||
|
adapters.insert(0, selected_adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
for adapter in adapters {
|
||||||
|
if let Some((device, queue)) = self.request_device(&adapter).await {
|
||||||
|
return Some(Context { device, queue, adapter, instance });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
pub async fn available_adapters_fmt(&self) -> impl std::fmt::Display {
|
pub async fn available_adapters_fmt(&self) -> impl std::fmt::Display {
|
||||||
let instance = self.build_instance();
|
let instance = self.build_instance();
|
||||||
fmt::AvailableAdaptersFormatter(instance.enumerate_adapters(self.backends).await)
|
fmt::AvailableAdaptersFormatter(enumerate_sorted(&instance, self.backends).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
@@ -67,6 +82,7 @@ impl ContextBuilder {
|
|||||||
..wgpu::InstanceDescriptor::new_without_display_handle()
|
..wgpu::InstanceDescriptor::new_without_display_handle()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
async fn request_adapter(&self, instance: &Instance) -> Option<Adapter> {
|
async fn request_adapter(&self, instance: &Instance) -> Option<Adapter> {
|
||||||
let request_adapter_options = wgpu::RequestAdapterOptions {
|
let request_adapter_options = wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||||
@@ -88,38 +104,35 @@ impl ContextBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
impl ContextBuilder {
|
async fn enumerate_sorted(instance: &Instance, backends: Backends) -> Vec<Adapter> {
|
||||||
async fn build_with_adapter_selection_inner<S>(self, select: Option<S>) -> Option<Context>
|
let mut adapters = instance.enumerate_adapters(backends).await;
|
||||||
where
|
adapters.sort_by_key(adapter_priority);
|
||||||
S: Fn(&[Adapter]) -> Option<usize>,
|
adapters
|
||||||
{
|
}
|
||||||
let instance = self.build_instance();
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
fn adapter_priority(adapter: &Adapter) -> (u8, u8) {
|
||||||
let selected_adapter = if let Some(select) = select {
|
let info = adapter.get_info();
|
||||||
self.select_adapter(&instance, select).await
|
let backend = if cfg!(target_os = "linux") {
|
||||||
} else if cfg!(target_os = "windows") {
|
match info.backend {
|
||||||
self.select_adapter(&instance, |adapters: &[Adapter]| adapters.iter().position(|a| a.get_info().backend == wgpu::Backend::Dx12))
|
wgpu::Backend::Vulkan => 0,
|
||||||
.await
|
_ => 1,
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let adapter = if let Some(adapter) = selected_adapter { adapter } else { self.request_adapter(&instance).await? };
|
|
||||||
|
|
||||||
let (device, queue) = self.request_device(&adapter).await?;
|
|
||||||
Some(Context { device, queue, adapter, instance })
|
|
||||||
}
|
|
||||||
async fn select_adapter<S>(&self, instance: &Instance, select: S) -> Option<Adapter>
|
|
||||||
where
|
|
||||||
S: Fn(&[Adapter]) -> Option<usize>,
|
|
||||||
{
|
|
||||||
let mut adapters = instance.enumerate_adapters(self.backends).await;
|
|
||||||
let selected_index = select(&adapters)?;
|
|
||||||
if selected_index >= adapters.len() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
Some(adapters.remove(selected_index))
|
} else if cfg!(target_os = "windows") {
|
||||||
}
|
match info.backend {
|
||||||
|
wgpu::Backend::Dx12 => 0,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let device_type = match info.device_type {
|
||||||
|
wgpu::DeviceType::DiscreteGpu => 0,
|
||||||
|
wgpu::DeviceType::IntegratedGpu => 1,
|
||||||
|
wgpu::DeviceType::VirtualGpu => 2,
|
||||||
|
wgpu::DeviceType::Cpu => 3,
|
||||||
|
wgpu::DeviceType::Other => 4,
|
||||||
|
};
|
||||||
|
(backend, device_type)
|
||||||
}
|
}
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
mod fmt {
|
mod fmt {
|
||||||
|
|||||||
Reference in New Issue
Block a user