mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Extend WgpuSetup, egui_kittest now prefers software rasterizers for testing (#5506)
This commit is contained in:
@@ -23,8 +23,10 @@ pub use wgpu;
|
||||
/// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`].
|
||||
mod renderer;
|
||||
|
||||
mod setup;
|
||||
|
||||
pub use renderer::*;
|
||||
use wgpu::{Adapter, Device, Instance, Queue, TextureFormat};
|
||||
pub use setup::{NativeAdapterSelectorMethod, WgpuSetup, WgpuSetupCreateNew, WgpuSetupExisting};
|
||||
|
||||
/// Helpers for capturing screenshots of the UI.
|
||||
pub mod capture;
|
||||
@@ -40,8 +42,8 @@ use epaint::mutex::RwLock;
|
||||
/// An error produced by egui-wgpu.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum WgpuError {
|
||||
#[error("Failed to create wgpu adapter, no suitable adapter found.")]
|
||||
NoSuitableAdapterFound,
|
||||
#[error("Failed to create wgpu adapter, no suitable adapter found: {0}")]
|
||||
NoSuitableAdapterFound(String),
|
||||
|
||||
#[error("There was no valid format for the surface at all.")]
|
||||
NoSurfaceFormatsAvailable,
|
||||
@@ -67,8 +69,9 @@ pub struct RenderState {
|
||||
///
|
||||
/// This is not available on web.
|
||||
/// On web, we always select WebGPU is available, then fall back to WebGL if not.
|
||||
// TODO(gfx-rs/wgpu#6665): Remove layer of `Arc` here once we update to wgpu 24
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub available_adapters: Arc<[wgpu::Adapter]>,
|
||||
pub available_adapters: Arc<[Arc<wgpu::Adapter>]>,
|
||||
|
||||
/// Wgpu device used for rendering, created from the adapter.
|
||||
pub device: Arc<wgpu::Device>,
|
||||
@@ -83,6 +86,75 @@ pub struct RenderState {
|
||||
pub renderer: Arc<RwLock<Renderer>>,
|
||||
}
|
||||
|
||||
async fn request_adapter(
|
||||
instance: &wgpu::Instance,
|
||||
power_preference: wgpu::PowerPreference,
|
||||
compatible_surface: Option<&wgpu::Surface<'_>>,
|
||||
_available_adapters: &[Arc<wgpu::Adapter>],
|
||||
) -> Result<Arc<wgpu::Adapter>, WgpuError> {
|
||||
profiling::function_scope!();
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference,
|
||||
compatible_surface,
|
||||
// We don't expose this as an option right now since it's fairly rarely useful:
|
||||
// * only has an effect on native
|
||||
// * fails if there's no software rasterizer available
|
||||
// * can achieve the same with `native_adapter_selector`
|
||||
force_fallback_adapter: false,
|
||||
})
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if _available_adapters.is_empty() {
|
||||
log::info!("No wgpu adapters found");
|
||||
} else if _available_adapters.len() == 1 {
|
||||
log::info!(
|
||||
"The only available wgpu adapter was not suitable: {}",
|
||||
adapter_info_summary(&_available_adapters[0].get_info())
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"No suitable wgpu adapter found out of the {} available ones: {}",
|
||||
_available_adapters.len(),
|
||||
describe_adapters(_available_adapters)
|
||||
);
|
||||
}
|
||||
|
||||
WgpuError::NoSuitableAdapterFound("`request_adapters` returned `None`".to_owned())
|
||||
})?;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log::debug!(
|
||||
"Picked wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if _available_adapters.len() == 1 {
|
||||
log::debug!(
|
||||
"Picked the only available wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"There were {} available wgpu adapters: {}",
|
||||
_available_adapters.len(),
|
||||
describe_adapters(_available_adapters)
|
||||
);
|
||||
log::debug!(
|
||||
"Picked wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
}
|
||||
|
||||
// On wasm, depending on feature flags, wgpu objects may or may not implement sync.
|
||||
// It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
Ok(Arc::new(adapter))
|
||||
}
|
||||
|
||||
impl RenderState {
|
||||
/// Creates a new [`RenderState`], containing everything needed for drawing egui with wgpu.
|
||||
///
|
||||
@@ -100,96 +172,73 @@ impl RenderState {
|
||||
|
||||
// This is always an empty list on web.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let available_adapters = instance.enumerate_adapters(wgpu::Backends::all());
|
||||
let available_adapters = {
|
||||
let backends = if let WgpuSetup::CreateNew(create_new) = &config.wgpu_setup {
|
||||
create_new.instance_descriptor.backends
|
||||
} else {
|
||||
wgpu::Backends::all()
|
||||
};
|
||||
|
||||
instance
|
||||
.enumerate_adapters(backends)
|
||||
// TODO(gfx-rs/wgpu#6665): Remove layer of `Arc` here once we update to wgpu 24.
|
||||
.into_iter()
|
||||
.map(Arc::new)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let (adapter, device, queue) = match config.wgpu_setup.clone() {
|
||||
WgpuSetup::CreateNew {
|
||||
supported_backends: _,
|
||||
WgpuSetup::CreateNew(WgpuSetupCreateNew {
|
||||
instance_descriptor: _,
|
||||
power_preference,
|
||||
native_adapter_selector: _native_adapter_selector,
|
||||
device_descriptor,
|
||||
} => {
|
||||
trace_path,
|
||||
}) => {
|
||||
let adapter = {
|
||||
profiling::scope!("request_adapter");
|
||||
instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
request_adapter(instance, power_preference, compatible_surface, &[]).await
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(native_adapter_selector) = _native_adapter_selector {
|
||||
native_adapter_selector(&available_adapters, compatible_surface)
|
||||
.map_err(WgpuError::NoSuitableAdapterFound)
|
||||
} else {
|
||||
request_adapter(
|
||||
instance,
|
||||
power_preference,
|
||||
compatible_surface,
|
||||
force_fallback_adapter: false,
|
||||
})
|
||||
&available_adapters,
|
||||
)
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if available_adapters.is_empty() {
|
||||
log::info!("No wgpu adapters found");
|
||||
} else if available_adapters.len() == 1 {
|
||||
log::info!(
|
||||
"The only available wgpu adapter was not suitable: {}",
|
||||
adapter_info_summary(&available_adapters[0].get_info())
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"No suitable wgpu adapter found out of the {} available ones: {}",
|
||||
available_adapters.len(),
|
||||
describe_adapters(&available_adapters)
|
||||
);
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
WgpuError::NoSuitableAdapterFound
|
||||
})?
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log::debug!(
|
||||
"Picked wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if available_adapters.len() == 1 {
|
||||
log::debug!(
|
||||
"Picked the only available wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"There were {} available wgpu adapters: {}",
|
||||
available_adapters.len(),
|
||||
describe_adapters(&available_adapters)
|
||||
);
|
||||
log::debug!(
|
||||
"Picked wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
);
|
||||
}
|
||||
|
||||
let trace_path = std::env::var("WGPU_TRACE");
|
||||
let (device, queue) = {
|
||||
profiling::scope!("request_device");
|
||||
adapter
|
||||
.request_device(
|
||||
&(*device_descriptor)(&adapter),
|
||||
trace_path.ok().as_ref().map(std::path::Path::new),
|
||||
)
|
||||
.request_device(&(*device_descriptor)(&adapter), trace_path.as_deref())
|
||||
.await?
|
||||
};
|
||||
|
||||
// On wasm, depending on feature flags, wgpu objects may or may not implement sync.
|
||||
// It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
(Arc::new(adapter), Arc::new(device), Arc::new(queue))
|
||||
(adapter, Arc::new(device), Arc::new(queue))
|
||||
}
|
||||
WgpuSetup::Existing {
|
||||
WgpuSetup::Existing(WgpuSetupExisting {
|
||||
instance: _,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
} => (adapter, device, queue),
|
||||
}) => (adapter, device, queue),
|
||||
};
|
||||
|
||||
let surface_formats = {
|
||||
profiling::scope!("get_capabilities");
|
||||
compatible_surface.map_or_else(
|
||||
|| vec![TextureFormat::Rgba8Unorm],
|
||||
|| vec![wgpu::TextureFormat::Rgba8Unorm],
|
||||
|s| s.get_capabilities(&adapter).formats,
|
||||
)
|
||||
};
|
||||
@@ -219,20 +268,17 @@ impl RenderState {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn describe_adapters(adapters: &[wgpu::Adapter]) -> String {
|
||||
fn describe_adapters(adapters: &[Arc<wgpu::Adapter>]) -> String {
|
||||
if adapters.is_empty() {
|
||||
"(none)".to_owned()
|
||||
} else if adapters.len() == 1 {
|
||||
adapter_info_summary(&adapters[0].get_info())
|
||||
} else {
|
||||
let mut list_string = String::new();
|
||||
for adapter in adapters {
|
||||
if !list_string.is_empty() {
|
||||
list_string += ", ";
|
||||
}
|
||||
list_string += &format!("{{{}}}", adapter_info_summary(&adapter.get_info()));
|
||||
}
|
||||
list_string
|
||||
adapters
|
||||
.iter()
|
||||
.map(|a| format!("{{{}}}", adapter_info_summary(&a.get_info())))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,62 +291,6 @@ pub enum SurfaceErrorAction {
|
||||
RecreateSurface,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum WgpuSetup {
|
||||
/// Construct a wgpu setup using some predefined settings & heuristics.
|
||||
/// This is the default option. You can customize most behaviours overriding the
|
||||
/// supported backends, power preferences, and device description.
|
||||
///
|
||||
/// This can also be configured with the environment variables:
|
||||
/// * `WGPU_BACKEND`: `vulkan`, `dx11`, `dx12`, `metal`, `opengl`, `webgpu`
|
||||
/// * `WGPU_POWER_PREF`: `low`, `high` or `none`
|
||||
CreateNew {
|
||||
/// Backends that should be supported (wgpu will pick one of these).
|
||||
///
|
||||
/// For instance, if you only want to support WebGL (and not WebGPU),
|
||||
/// you can set this to [`wgpu::Backends::GL`].
|
||||
///
|
||||
/// By default on web, WebGPU will be used if available.
|
||||
/// WebGL will only be used as a fallback,
|
||||
/// and only if you have enabled the `webgl` feature of crate `wgpu`.
|
||||
supported_backends: wgpu::Backends,
|
||||
|
||||
/// Power preference for the adapter.
|
||||
power_preference: wgpu::PowerPreference,
|
||||
|
||||
/// Configuration passed on device request, given an adapter
|
||||
device_descriptor:
|
||||
Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> + Send + Sync>,
|
||||
},
|
||||
|
||||
/// Run on an existing wgpu setup.
|
||||
Existing {
|
||||
instance: Arc<Instance>,
|
||||
adapter: Arc<Adapter>,
|
||||
device: Arc<Device>,
|
||||
queue: Arc<Queue>,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WgpuSetup {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::CreateNew {
|
||||
supported_backends,
|
||||
power_preference,
|
||||
device_descriptor: _,
|
||||
} => f
|
||||
.debug_struct("AdapterSelection::Standard")
|
||||
.field("supported_backends", &supported_backends)
|
||||
.field("power_preference", &power_preference)
|
||||
.finish(),
|
||||
Self::Existing { .. } => f
|
||||
.debug_struct("AdapterSelection::Existing")
|
||||
.finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for using wgpu with eframe or the egui-wgpu winit feature.
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuConfiguration {
|
||||
@@ -352,42 +342,8 @@ impl Default for WgpuConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
|
||||
desired_maximum_frame_latency: None,
|
||||
|
||||
// By default, create a new wgpu setup. This will create a new instance, adapter, device and queue.
|
||||
// This will create an instance for the supported backends (which can be configured by
|
||||
// `WGPU_BACKEND`), and will pick an adapter by iterating adapters based on their power preference. The power
|
||||
// preference can also be configured by `WGPU_POWER_PREF`.
|
||||
wgpu_setup: WgpuSetup::CreateNew {
|
||||
// Add GL backend, primarily because WebGPU is not stable enough yet.
|
||||
// (note however, that the GL backend needs to be opted-in via the wgpu feature flag "webgl")
|
||||
supported_backends: wgpu::util::backend_bits_from_env()
|
||||
.unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),
|
||||
|
||||
power_preference: wgpu::util::power_preference_from_env()
|
||||
.unwrap_or(wgpu::PowerPreference::HighPerformance),
|
||||
device_descriptor: Arc::new(|adapter| {
|
||||
let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl {
|
||||
wgpu::Limits::downlevel_webgl2_defaults()
|
||||
} else {
|
||||
wgpu::Limits::default()
|
||||
};
|
||||
|
||||
wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
required_features: wgpu::Features::default(),
|
||||
required_limits: wgpu::Limits {
|
||||
// When using a depth buffer, we have to be able to create a texture
|
||||
// large enough for the entire surface, and we want to support 4k+ displays.
|
||||
max_texture_dimension_2d: 8192,
|
||||
..base_limits
|
||||
},
|
||||
memory_hints: wgpu::MemoryHints::default(),
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
wgpu_setup: Default::default(),
|
||||
on_surface_error: Arc::new(|err| {
|
||||
if err == wgpu::SurfaceError::Outdated {
|
||||
// This error occurs when the app is minimized on Windows.
|
||||
@@ -468,8 +424,14 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String {
|
||||
summary += &format!(", driver_info: {driver_info:?}");
|
||||
}
|
||||
if *vendor != 0 {
|
||||
// TODO(emilk): decode using https://github.com/gfx-rs/wgpu/blob/767ac03245ee937d3dc552edc13fe7ab0a860eec/wgpu-hal/src/auxil/mod.rs#L7
|
||||
summary += &format!(", vendor: 0x{vendor:04X}");
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
summary += &format!(", vendor: {} (0x{vendor:04X})", parse_vendor_id(*vendor));
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
summary += &format!(", vendor: 0x{vendor:04X}");
|
||||
}
|
||||
}
|
||||
if *device != 0 {
|
||||
summary += &format!(", device: 0x{device:02X}");
|
||||
@@ -477,3 +439,20 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String {
|
||||
|
||||
summary
|
||||
}
|
||||
|
||||
/// Tries to parse the adapter's vendor ID to a human-readable string.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn parse_vendor_id(vendor_id: u32) -> &'static str {
|
||||
match vendor_id {
|
||||
wgpu::hal::auxil::db::amd::VENDOR => "AMD",
|
||||
wgpu::hal::auxil::db::apple::VENDOR => "Apple",
|
||||
wgpu::hal::auxil::db::arm::VENDOR => "ARM",
|
||||
wgpu::hal::auxil::db::broadcom::VENDOR => "Broadcom",
|
||||
wgpu::hal::auxil::db::imgtec::VENDOR => "Imagination Technologies",
|
||||
wgpu::hal::auxil::db::intel::VENDOR => "Intel",
|
||||
wgpu::hal::auxil::db::mesa::VENDOR => "Mesa",
|
||||
wgpu::hal::auxil::db::nvidia::VENDOR => "NVIDIA",
|
||||
wgpu::hal::auxil::db::qualcomm::VENDOR => "Qualcomm",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
237
crates/egui-wgpu/src/setup.rs
Normal file
237
crates/egui-wgpu/src/setup.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum WgpuSetup {
|
||||
/// Construct a wgpu setup using some predefined settings & heuristics.
|
||||
/// This is the default option. You can customize most behaviours overriding the
|
||||
/// supported backends, power preferences, and device description.
|
||||
///
|
||||
/// By default can also be configured with various environment variables:
|
||||
/// * `WGPU_BACKEND`: `vulkan`, `dx12`, `metal`, `opengl`, `webgpu`
|
||||
/// * `WGPU_POWER_PREF`: `low`, `high` or `none`
|
||||
/// * `WGPU_TRACE`: Path to a file to output a wgpu trace file.
|
||||
///
|
||||
/// Each instance flag also comes with an environment variable (for details see [`wgpu::InstanceFlags`]):
|
||||
/// * `WGPU_VALIDATION`: Enables validation (enabled by default in debug builds).
|
||||
/// * `WGPU_DEBUG`: Generate debug information in shaders and objects (enabled by default in debug builds).
|
||||
/// * `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER`: Whether wgpu should expose adapters that run on top of non-compliant adapters.
|
||||
/// * `WGPU_GPU_BASED_VALIDATION`: Enable GPU-based validation.
|
||||
CreateNew(WgpuSetupCreateNew),
|
||||
|
||||
/// Run on an existing wgpu setup.
|
||||
Existing(WgpuSetupExisting),
|
||||
}
|
||||
|
||||
impl Default for WgpuSetup {
|
||||
fn default() -> Self {
|
||||
Self::CreateNew(WgpuSetupCreateNew::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WgpuSetup {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::CreateNew(create_new) => f
|
||||
.debug_tuple("WgpuSetup::CreateNew")
|
||||
.field(create_new)
|
||||
.finish(),
|
||||
Self::Existing { .. } => f.debug_tuple("WgpuSetup::Existing").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WgpuSetup {
|
||||
/// Creates a new [`wgpu::Instance`] or clones the existing one.
|
||||
///
|
||||
/// Does *not* store the wgpu instance, so calling this repeatedly may
|
||||
/// create a new instance every time!
|
||||
pub async fn new_instance(&self) -> Arc<wgpu::Instance> {
|
||||
match self {
|
||||
Self::CreateNew(create_new) => {
|
||||
#[allow(unused_mut)]
|
||||
let mut backends = create_new.instance_descriptor.backends;
|
||||
|
||||
// Don't try WebGPU if we're not in a secure context.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if backends.contains(wgpu::Backends::BROWSER_WEBGPU) {
|
||||
let is_secure_context =
|
||||
wgpu::web_sys::window().map_or(false, |w| w.is_secure_context());
|
||||
if !is_secure_context {
|
||||
log::info!(
|
||||
"WebGPU is only available in secure contexts, i.e. on HTTPS and on localhost."
|
||||
);
|
||||
backends.remove(wgpu::Backends::BROWSER_WEBGPU);
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Creating wgpu instance with backends {:?}", backends);
|
||||
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
Arc::new(
|
||||
wgpu::util::new_instance_with_webgpu_detection(wgpu::InstanceDescriptor {
|
||||
backends: create_new.instance_descriptor.backends,
|
||||
flags: create_new.instance_descriptor.flags,
|
||||
dx12_shader_compiler: create_new
|
||||
.instance_descriptor
|
||||
.dx12_shader_compiler
|
||||
.clone(),
|
||||
gles_minor_version: create_new.instance_descriptor.gles_minor_version,
|
||||
})
|
||||
.await,
|
||||
)
|
||||
}
|
||||
Self::Existing(existing) => existing.instance.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WgpuSetupCreateNew> for WgpuSetup {
|
||||
fn from(create_new: WgpuSetupCreateNew) -> Self {
|
||||
Self::CreateNew(create_new)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WgpuSetupExisting> for WgpuSetup {
|
||||
fn from(existing: WgpuSetupExisting) -> Self {
|
||||
Self::Existing(existing)
|
||||
}
|
||||
}
|
||||
|
||||
/// Method for selecting an adapter on native.
|
||||
///
|
||||
/// This can be used for fully custom adapter selection.
|
||||
/// If available, `wgpu::Surface` is passed to allow checking for surface compatibility.
|
||||
// TODO(gfx-rs/wgpu#6665): Remove layer of `Arc` here.
|
||||
pub type NativeAdapterSelectorMethod = Arc<
|
||||
dyn Fn(&[Arc<wgpu::Adapter>], Option<&wgpu::Surface<'_>>) -> Result<Arc<wgpu::Adapter>, String>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
/// Configuration for creating a new wgpu setup.
|
||||
///
|
||||
/// Used for [`WgpuSetup::CreateNew`].
|
||||
pub struct WgpuSetupCreateNew {
|
||||
/// Instance descriptor for creating a wgpu instance.
|
||||
///
|
||||
/// The most important field is [`wgpu::InstanceDescriptor::backends`], which
|
||||
/// controls which backends are supported (wgpu will pick one of these).
|
||||
/// If you only want to support WebGL (and not WebGPU),
|
||||
/// you can set this to [`wgpu::Backends::GL`].
|
||||
/// By default on web, WebGPU will be used if available.
|
||||
/// WebGL will only be used as a fallback,
|
||||
/// and only if you have enabled the `webgl` feature of crate `wgpu`.
|
||||
pub instance_descriptor: wgpu::InstanceDescriptor,
|
||||
|
||||
/// Power preference for the adapter if [`Self::native_adapter_selector`] is not set or targeting web.
|
||||
pub power_preference: wgpu::PowerPreference,
|
||||
|
||||
/// Optional selector for native adapters.
|
||||
///
|
||||
/// This field has no effect when targeting web!
|
||||
/// Otherwise, if set [`Self::power_preference`] is ignored and the adapter is instead selected by this method.
|
||||
/// Note that [`Self::instance_descriptor`]'s [`wgpu::InstanceDescriptor::backends`]
|
||||
/// are still used to filter the adapter enumeration in the first place.
|
||||
///
|
||||
/// Defaults to `None`.
|
||||
pub native_adapter_selector: Option<NativeAdapterSelectorMethod>,
|
||||
|
||||
/// Configuration passed on device request, given an adapter
|
||||
pub device_descriptor:
|
||||
Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> + Send + Sync>,
|
||||
|
||||
/// Option path to output a wgpu trace file.
|
||||
///
|
||||
/// This only works if this feature is enabled in `wgpu-core`.
|
||||
/// Does not work when running with WebGPU.
|
||||
/// Defaults to the path set in the `WGPU_TRACE` environment variable.
|
||||
pub trace_path: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl Clone for WgpuSetupCreateNew {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
// TODO(gfx-rs/wgpu/#6849): use .clone()
|
||||
instance_descriptor: wgpu::InstanceDescriptor {
|
||||
backends: self.instance_descriptor.backends,
|
||||
flags: self.instance_descriptor.flags,
|
||||
dx12_shader_compiler: self.instance_descriptor.dx12_shader_compiler.clone(),
|
||||
gles_minor_version: self.instance_descriptor.gles_minor_version,
|
||||
},
|
||||
power_preference: self.power_preference,
|
||||
native_adapter_selector: self.native_adapter_selector.clone(),
|
||||
device_descriptor: self.device_descriptor.clone(),
|
||||
trace_path: self.trace_path.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WgpuSetupCreateNew {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("WgpuSetupCreateNew")
|
||||
.field("instance_descriptor", &self.instance_descriptor)
|
||||
.field("power_preference", &self.power_preference)
|
||||
.field(
|
||||
"native_adapter_selector",
|
||||
&self.native_adapter_selector.is_some(),
|
||||
)
|
||||
.field("trace_path", &self.trace_path)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WgpuSetupCreateNew {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
instance_descriptor: wgpu::InstanceDescriptor {
|
||||
// Add GL backend, primarily because WebGPU is not stable enough yet.
|
||||
// (note however, that the GL backend needs to be opted-in via the wgpu feature flag "webgl")
|
||||
backends: wgpu::util::backend_bits_from_env()
|
||||
.unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),
|
||||
flags: wgpu::InstanceFlags::from_build_config().with_env(),
|
||||
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||
gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
|
||||
},
|
||||
|
||||
power_preference: wgpu::util::power_preference_from_env()
|
||||
.unwrap_or(wgpu::PowerPreference::HighPerformance),
|
||||
|
||||
native_adapter_selector: None,
|
||||
|
||||
device_descriptor: Arc::new(|adapter| {
|
||||
let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl {
|
||||
wgpu::Limits::downlevel_webgl2_defaults()
|
||||
} else {
|
||||
wgpu::Limits::default()
|
||||
};
|
||||
|
||||
wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
required_features: wgpu::Features::default(),
|
||||
required_limits: wgpu::Limits {
|
||||
// When using a depth buffer, we have to be able to create a texture
|
||||
// large enough for the entire surface, and we want to support 4k+ displays.
|
||||
max_texture_dimension_2d: 8192,
|
||||
..base_limits
|
||||
},
|
||||
memory_hints: wgpu::MemoryHints::default(),
|
||||
}
|
||||
}),
|
||||
|
||||
trace_path: std::env::var("WGPU_TRACE")
|
||||
.ok()
|
||||
.map(std::path::PathBuf::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for using an existing wgpu setup.
|
||||
///
|
||||
/// Used for [`WgpuSetup::Existing`].
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuSetupExisting {
|
||||
pub instance: Arc<wgpu::Instance>,
|
||||
pub adapter: Arc<wgpu::Adapter>,
|
||||
pub device: Arc<wgpu::Device>,
|
||||
pub queue: Arc<wgpu::Queue>,
|
||||
}
|
||||
@@ -51,7 +51,7 @@ impl Painter {
|
||||
/// [`set_window()`](Self::set_window) once you have
|
||||
/// a [`winit::window::Window`] with a valid `.raw_window_handle()`
|
||||
/// associated.
|
||||
pub fn new(
|
||||
pub async fn new(
|
||||
context: Context,
|
||||
configuration: WgpuConfiguration,
|
||||
msaa_samples: u32,
|
||||
@@ -59,17 +59,8 @@ impl Painter {
|
||||
support_transparent_backbuffer: bool,
|
||||
dithering: bool,
|
||||
) -> Self {
|
||||
let instance = match &configuration.wgpu_setup {
|
||||
crate::WgpuSetup::CreateNew {
|
||||
supported_backends, ..
|
||||
} => Arc::new(wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: *supported_backends,
|
||||
..Default::default()
|
||||
})),
|
||||
crate::WgpuSetup::Existing { instance, .. } => instance.clone(),
|
||||
};
|
||||
|
||||
let (capture_tx, capture_rx) = capture_channel();
|
||||
let instance = configuration.wgpu_setup.new_instance().await;
|
||||
|
||||
Self {
|
||||
context,
|
||||
|
||||
Reference in New Issue
Block a user