diff --git a/CHANGELOG.md b/CHANGELOG.md index 30e51cf74c..fc3499fcc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,74 @@ let desc: ShaderModuleDescriptor = include_wgsl!(...) By @cwfitzgerald and @rudderbucky in [#6662](https://github.com/gfx-rs/wgpu/pull/6662). +#### `wgpu::Instance::new` now takes `InstanceDescriptor` by reference + +Previously `wgpu::Instance::new` took `InstanceDescriptor` by value (which is overall fairly uncommon in wgpu). +Furthermore, `InstanceDescriptor` is now cloneable. + +```diff +- let instance = wgpu::Instance::new(instance_desc); ++ let instance = wgpu::Instance::new(&instance_desc); +``` + +By @wumpf in [#6849](https://github.com/gfx-rs/wgpu/pull/6849). + +#### Environment Variable Handling Overhaul + +Previously how various bits of code handled reading settings from environment variables was inconsistent and unideomatic. +We have unified it to (`Type::from_env()` or `Type::from_env_or_default()`) and `Type::with_env` for all types. + +```diff +- wgpu::util::backend_bits_from_env() ++ wgpu::Backends::from_env() + +- wgpu::util::power_preference_from_env() ++ wgpu::PowerPreference::from_env() + +- wgpu::util::dx12_shader_compiler_from_env() ++ wgpu::Dx12Compiler::from_env() + +- wgpu::util::gles_minor_version_from_env() ++ wgpu::Gles3MinorVersion::from_env() + +- wgpu::util::instance_descriptor_from_env() ++ wgpu::InstanceDescriptor::from_env_or_default() + +- wgpu::util::parse_backends_from_comma_list(&str) ++ wgpu::Backends::from_comma_list(&str) +``` + +By @cwfitzgerald in [#6895](https://github.com/gfx-rs/wgpu/pull/6895) + +#### Backend-specific instance options are now in separate structs + +In order to better facilitate growing more interesting backend options, we have put them into individual structs. This allows users to more easily understand what options can be defaulted and which they care about. All of these new structs implement `from_env()` and delegate to their respective `from_env()` methods. + +```diff +- let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { +- backends: wgpu::Backends::all(), +- flags: wgpu::InstanceFlags::default(), +- dx12_shader_compiler: wgpu::Dx12Compiler::Dxc, +- gles_minor_version: wgpu::Gles3MinorVersion::Automatic, +- }); ++ let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { ++ backends: wgpu::Backends::all(), ++ flags: wgpu::InstanceFlags::default(), ++ backend_options: wgpu::BackendOptions { ++ dx12: wgpu::Dx12BackendOptions { ++ shader_compiler: wgpu::Dx12ShaderCompiler::Dxc, ++ }, ++ gl: wgpu::GlBackendOptions { ++ gles_minor_version: wgpu::Gles3MinorVersion::Automatic, ++ }, ++ }, ++ }); +``` + +If you do not need any of these options, or only need one backend's info use the `default()` impl to fill out the remaining feelds. + +By @cwfitzgerald in [#6895](https://github.com/gfx-rs/wgpu/pull/6895) + #### The `diagnostic(…);` directive is now supported in WGSL Naga now parses `diagnostic(…);` directives according to the WGSL spec. This allows users to control certain lints, similar to Rust's `allow`, `warn`, and `deny` attributes. For example, in standard WGSL (but, notably, not Naga yet—see ) this snippet would emit a uniformity error: @@ -125,18 +193,6 @@ There are some limitations to keep in mind with this new functionality: By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148](https://github.com/gfx-rs/wgpu/pull/6148), [#6533](https://github.com/gfx-rs/wgpu/pull/6533), [#6353](https://github.com/gfx-rs/wgpu/pull/6353), [#6537](https://github.com/gfx-rs/wgpu/pull/6537). -#### `wgpu::Instance::new` now takes `InstanceDescriptor` by reference - -Previously `wgpu::Instance::new` took `InstanceDescriptor` by value (which is overall fairly uncommon in wgpu). -Furthermore, `InstanceDescriptor` is now cloneable. - -```diff -- let instance = wgpu::Instance::new(instance_desc); -+ let instance = wgpu::Instance::new(&instance_desc); -``` - -By @wumpf in [#6849](https://github.com/gfx-rs/wgpu/pull/6849). - #### New Features ##### Naga diff --git a/Cargo.lock b/Cargo.lock index 3dadf48a9a..848ea46b1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4206,6 +4206,7 @@ version = "23.0.0" dependencies = [ "bitflags 2.7.0", "js-sys", + "log", "serde", "serde_json", "web-sys", diff --git a/benches/benches/root.rs b/benches/benches/root.rs index 7d9f84e6b8..49d0ff0a68 100644 --- a/benches/benches/root.rs +++ b/benches/benches/root.rs @@ -26,11 +26,8 @@ impl DeviceState { }; let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends: wgpu::util::backend_bits_from_env().unwrap_or(base_backend), - flags: wgpu::InstanceFlags::empty(), - dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env() - .unwrap_or(wgpu::Dx12Compiler::Fxc), - gles_minor_version: wgpu::Gles3MinorVersion::Automatic, + backends: wgpu::Backends::from_env().unwrap_or(base_backend), + ..wgpu::InstanceDescriptor::from_env_or_default() }); let adapter = block_on(wgpu::util::initialize_adapter_from_env_or_default( diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 4bfb247012..56c475cd8f 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -381,7 +381,7 @@ pub fn op_webgpu_request_adapter( let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else( |_| wgpu_types::Backends::all(), - |s| wgpu_core::instance::parse_backends_from_comma_list(&s), + |s| wgpu_types::Backends::from_comma_list(&s), ); let instance = if let Some(instance) = state.try_borrow::() { instance @@ -391,8 +391,14 @@ pub fn op_webgpu_request_adapter( &wgpu_types::InstanceDescriptor { backends, flags: wgpu_types::InstanceFlags::from_build_config(), - dx12_shader_compiler: wgpu_types::Dx12Compiler::Fxc, - gles_minor_version: wgpu_types::Gles3MinorVersion::default(), + backend_options: wgpu_types::BackendOptions { + dx12: wgpu_types::Dx12BackendOptions { + shader_compiler: wgpu_types::Dx12Compiler::Fxc, + }, + gl: wgpu_types::GlBackendOptions { + gles_minor_version: wgpu_types::Gles3MinorVersion::default(), + }, + }, }, ))); state.borrow::() diff --git a/examples/src/framework.rs b/examples/src/framework.rs index 71800aebb0..db863526d7 100644 --- a/examples/src/framework.rs +++ b/examples/src/framework.rs @@ -268,7 +268,7 @@ impl ExampleContext { async fn init_async(surface: &mut SurfaceWrapper, window: Arc) -> Self { log::info!("Initializing wgpu..."); - let instance = wgpu::Instance::new(&wgpu::util::instance_descriptor_from_env()); + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default()); surface.pre_adapter(&instance, window); let adapter = get_adapter_with_capabilities_or_from_env( diff --git a/examples/src/hello_triangle/mod.rs b/examples/src/hello_triangle/mod.rs index 016ed544bb..935f353e44 100644 --- a/examples/src/hello_triangle/mod.rs +++ b/examples/src/hello_triangle/mod.rs @@ -10,7 +10,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { size.width = size.width.max(1); size.height = size.height.max(1); - let instance = wgpu::Instance::new(&wgpu::util::instance_descriptor_from_env()); + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default()); let surface = instance.create_surface(&window).unwrap(); let adapter = instance diff --git a/examples/src/timestamp_queries/mod.rs b/examples/src/timestamp_queries/mod.rs index 9ff07a6bbd..100c2ca04e 100644 --- a/examples/src/timestamp_queries/mod.rs +++ b/examples/src/timestamp_queries/mod.rs @@ -179,13 +179,7 @@ impl Queries { async fn run() { // Instantiates instance of wgpu - let backends = wgpu::util::backend_bits_from_env().unwrap_or_default(); - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends, - flags: wgpu::InstanceFlags::from_build_config().with_env(), - dx12_shader_compiler: wgpu::Dx12Compiler::default(), - gles_minor_version: wgpu::Gles3MinorVersion::default(), - }); + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default()); // `request_adapter` instantiates the general connection to the GPU let adapter = instance diff --git a/player/tests/test.rs b/player/tests/test.rs index 4baa66b17e..d50f21dd55 100644 --- a/player/tests/test.rs +++ b/player/tests/test.rs @@ -200,12 +200,7 @@ impl Corpus { let global = wgc::global::Global::new( "test", - &wgt::InstanceDescriptor { - backends: backend.into(), - flags: wgt::InstanceFlags::debugging(), - dx12_shader_compiler: wgt::Dx12Compiler::Fxc, - gles_minor_version: wgt::Gles3MinorVersion::default(), - }, + &wgt::InstanceDescriptor::from_env_or_default(), ); let adapter = match global.request_adapter( &wgc::instance::RequestAdapterOptions { diff --git a/tests/src/init.rs b/tests/src/init.rs index f710cfb7cb..67b33c0f19 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -34,14 +34,18 @@ pub fn initialize_instance(backends: wgpu::Backends, force_fxc: bool) -> Instanc let dx12_shader_compiler = if force_fxc { wgpu::Dx12Compiler::Fxc } else { - wgpu::util::dx12_shader_compiler_from_env().unwrap_or(wgpu::Dx12Compiler::StaticDxc) + wgpu::Dx12Compiler::from_env().unwrap_or(wgpu::Dx12Compiler::StaticDxc) }; - let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default(); + let gles_minor_version = wgpu::Gles3MinorVersion::from_env().unwrap_or_default(); Instance::new(&wgpu::InstanceDescriptor { backends, flags: wgpu::InstanceFlags::debugging().with_env(), - dx12_shader_compiler, - gles_minor_version, + backend_options: wgpu::BackendOptions { + dx12: wgpu::Dx12BackendOptions { + shader_compiler: dx12_shader_compiler, + }, + gl: wgpu::GlBackendOptions { gles_minor_version }, + }, }) } diff --git a/tests/src/native.rs b/tests/src/native.rs index afad3d46dc..eb12d22a5f 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -90,7 +90,7 @@ pub fn main() -> MainResult { GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; // Filter out the adapters that are not part of WGPU_BACKEND. - let wgpu_backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::all()); + let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default(); report .devices .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend))); diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 75340ddb78..6b1e721d4e 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -73,8 +73,12 @@ impl Instance { let hal_desc = hal::InstanceDescriptor { name: "wgpu", flags: instance_desc.flags, - dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(), - gles_minor_version: instance_desc.gles_minor_version, + dx12_shader_compiler: instance_desc + .backend_options + .dx12 + .shader_compiler + .clone(), + gles_minor_version: instance_desc.backend_options.gl.gles_minor_version, }; use hal::Instance as _; @@ -981,38 +985,3 @@ impl Global { Ok((device_id, queue_id)) } } - -/// Generates a set of backends from a comma separated list of case-insensitive backend names. -/// -/// Whitespace is stripped, so both 'gl, dx12' and 'gl,dx12' are valid. -/// -/// Always returns WEBGPU on wasm over webgpu. -/// -/// Names: -/// - vulkan = "vulkan" or "vk" -/// - dx12 = "dx12" or "d3d12" -/// - metal = "metal" or "mtl" -/// - gles = "opengl" or "gles" or "gl" -/// - webgpu = "webgpu" -pub fn parse_backends_from_comma_list(string: &str) -> Backends { - let mut backends = Backends::empty(); - for backend in string.to_lowercase().split(',') { - backends |= match backend.trim() { - "vulkan" | "vk" => Backends::VULKAN, - "dx12" | "d3d12" => Backends::DX12, - "metal" | "mtl" => Backends::METAL, - "opengl" | "gles" | "gl" => Backends::GL, - "webgpu" => Backends::BROWSER_WEBGPU, - b => { - log::warn!("unknown backend string '{}'", b); - continue; - } - } - } - - if backends.is_empty() { - log::warn!("no valid backend strings found!"); - } - - backends -} diff --git a/wgpu-info/src/report.rs b/wgpu-info/src/report.rs index 3d7d257e45..974885d2ba 100644 --- a/wgpu-info/src/report.rs +++ b/wgpu-info/src/report.rs @@ -17,12 +17,10 @@ pub struct GpuReport { impl GpuReport { pub fn generate() -> Self { - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends: wgpu::util::backend_bits_from_env().unwrap_or_default(), - flags: wgpu::InstanceFlags::debugging().with_env(), - dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env() - .unwrap_or(wgpu::Dx12Compiler::StaticDxc), - gles_minor_version: wgpu::util::gles_minor_version_from_env().unwrap_or_default(), + let instance = wgpu::Instance::new(&{ + let mut desc = wgpu::InstanceDescriptor::from_env_or_default(); + desc.flags = wgpu::InstanceFlags::debugging().with_env(); + desc }); let adapters = instance.enumerate_adapters(wgpu::Backends::all()); diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index 0c5a04a7e7..d769176f65 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -41,6 +41,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(web_sys_unstable_apis)'] } [dependencies] bitflags = { workspace = true, features = ["serde"] } +log.workspace = true serde = { workspace = true, default-features = false, features = [ "alloc", "derive", diff --git a/wgpu-types/src/env.rs b/wgpu-types/src/env.rs new file mode 100644 index 0000000000..1f9c350775 --- /dev/null +++ b/wgpu-types/src/env.rs @@ -0,0 +1,11 @@ +use alloc::string::String; + +/// No-std friendly version of `std::env::var`. Returns `None` if the environment variable is not set +/// or we are in a no-std context. +pub fn var(_key: &str) -> Option { + #[cfg(feature = "std")] + return std::env::var(_key).ok(); + + #[cfg(not(feature = "std"))] + return None; +} diff --git a/wgpu-types/src/instance.rs b/wgpu-types/src/instance.rs new file mode 100644 index 0000000000..a48aabbd4a --- /dev/null +++ b/wgpu-types/src/instance.rs @@ -0,0 +1,388 @@ +//! Types for dealing with Instances + +use alloc::string::String; + +use crate::Backends; + +/// Options for creating an instance. +#[derive(Clone, Debug)] +pub struct InstanceDescriptor { + /// Which `Backends` to enable. + pub backends: Backends, + /// Flags to tune the behavior of the instance. + pub flags: InstanceFlags, + /// Options the control the behavior of various backends. + pub backend_options: BackendOptions, +} + +impl Default for InstanceDescriptor { + fn default() -> Self { + Self { + backends: Backends::all(), + flags: InstanceFlags::default(), + backend_options: BackendOptions::default(), + } + } +} + +impl InstanceDescriptor { + /// Choose instance options entirely from environment variables. + /// + /// This is equivalent to calling `from_env` on every field. + #[must_use] + pub fn from_env_or_default() -> Self { + Self::default().with_env() + } + + /// Takes the given options, modifies them based on the environment variables, and returns the result. + /// + /// This is equivalent to calling `with_env` on every field. + #[must_use] + pub fn with_env(self) -> Self { + let backends = self.backends.with_env(); + let flags = self.flags.with_env(); + let backend_options = self.backend_options.with_env(); + Self { + backends, + flags, + backend_options, + } + } +} + +bitflags::bitflags! { + /// Instance debugging flags. + /// + /// These are not part of the webgpu standard. + /// + /// Defaults to enabling debugging-related flags if the build configuration has `debug_assertions`. + #[repr(transparent)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct InstanceFlags: u32 { + /// Generate debug information in shaders and objects. + /// + /// When `Self::from_env()` is used takes value from `WGPU_DEBUG` environment variable. + const DEBUG = 1 << 0; + /// Enable validation, if possible. + /// + /// When `Self::from_env()` is used takes value from `WGPU_VALIDATION` environment variable. + const VALIDATION = 1 << 1; + /// Don't pass labels to wgpu-hal. + /// + /// When `Self::from_env()` is used takes value from `WGPU_DISCARD_HAL_LABELS` environment variable. + const DISCARD_HAL_LABELS = 1 << 2; + /// Whether wgpu should expose adapters that run on top of non-compliant adapters. + /// + /// Turning this on might mean that some of the functionality provided by the wgpu + /// adapter/device is not working or is broken. It could be that all the functionality + /// wgpu currently exposes works but we can't tell for sure since we have no additional + /// transparency into what is working and what is not on the underlying adapter. + /// + /// This mainly applies to a Vulkan driver's compliance version. If the major compliance version + /// is `0`, then the driver is ignored. This flag allows that driver to be enabled for testing. + /// + /// When `Self::from_env()` is used takes value from `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER` environment variable. + const ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER = 1 << 3; + /// Enable GPU-based validation. Implies [`Self::VALIDATION`]. Currently, this only changes + /// behavior on the DX12 and Vulkan backends. + /// + /// Supported platforms: + /// + /// - D3D12; called ["GPU-based validation", or + /// "GBV"](https://web.archive.org/web/20230206120404/https://learn.microsoft.com/en-us/windows/win32/direct3d12/using-d3d12-debug-layer-gpu-based-validation) + /// - Vulkan, via the `VK_LAYER_KHRONOS_validation` layer; called ["GPU-Assisted + /// Validation"](https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/e45aeb85079e0835694cb8f03e6681fd18ae72c9/docs/gpu_validation.md#gpu-assisted-validation) + /// + /// When `Self::from_env()` is used takes value from `WGPU_GPU_BASED_VALIDATION` environment variable. + const GPU_BASED_VALIDATION = 1 << 4; + } +} + +impl Default for InstanceFlags { + fn default() -> Self { + Self::from_build_config() + } +} + +impl InstanceFlags { + /// Enable recommended debugging and validation flags. + #[must_use] + pub fn debugging() -> Self { + InstanceFlags::DEBUG | InstanceFlags::VALIDATION + } + + /// Enable advanced debugging and validation flags (potentially very slow). + #[must_use] + pub fn advanced_debugging() -> Self { + Self::debugging() | InstanceFlags::GPU_BASED_VALIDATION + } + + /// Infer decent defaults from the build type. + /// + /// If cfg!(debug_assertions) is true, then this returns [`Self::debugging()`]. + /// Otherwise, it returns [`Self::empty()`]. + #[must_use] + pub fn from_build_config() -> Self { + if cfg!(debug_assertions) { + return InstanceFlags::debugging(); + } + + InstanceFlags::empty() + } + + /// Derive defaults from environment variables. See [`Self::with_env()`] for more information. + #[must_use] + pub fn from_env_or_default() -> Self { + Self::default().with_env() + } + + /// Takes the given flags, modifies them based on the environment variables, and returns the result. + /// + /// - If an environment variable is set to anything but "0", the corresponding flag is set. + /// - If the value is "0", the flag is unset. + /// - If the environment variable is not present, then the flag retains its initial value. + /// + /// For example `let flags = InstanceFlags::debugging().with_env();` with `WGPU_VALIDATION=0` + /// does not contain `InstanceFlags::VALIDATION`. + /// + /// The environment variables are named after the flags prefixed with "WGPU_". For example: + /// - `WGPU_DEBUG` + /// - `WGPU_VALIDATION` + /// - `WGPU_DISCARD_HAL_LABELS` + /// - `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER` + /// - `WGPU_GPU_BASED_VALIDATION` + #[must_use] + pub fn with_env(mut self) -> Self { + fn env(key: &str) -> Option { + crate::env::var(key).map(|s| match s.as_str() { + "0" => false, + _ => true, + }) + } + + if let Some(bit) = env("WGPU_VALIDATION") { + self.set(Self::VALIDATION, bit); + } + if let Some(bit) = env("WGPU_DEBUG") { + self.set(Self::DEBUG, bit); + } + if let Some(bit) = env("WGPU_DISCARD_HAL_LABELS") { + self.set(Self::DISCARD_HAL_LABELS, bit); + } + if let Some(bit) = env("WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER") { + self.set(Self::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER, bit); + } + if let Some(bit) = env("WGPU_GPU_BASED_VALIDATION") { + self.set(Self::GPU_BASED_VALIDATION, bit); + } + + self + } +} + +/// Options that are passed to a given backend. +#[derive(Clone, Debug, Default)] +pub struct BackendOptions { + /// Options for the OpenGL/OpenGLES backend. + pub gl: GlBackendOptions, + /// Options for the DX12 backend. + pub dx12: Dx12BackendOptions, +} + +impl BackendOptions { + /// Choose backend options by calling `from_env` on every field. + /// + /// See those methods for more information. + #[must_use] + pub fn from_env_or_default() -> Self { + let gl = GlBackendOptions::from_env_or_default(); + let dx12 = Dx12BackendOptions::from_env_or_default(); + Self { gl, dx12 } + } + + /// Takes the given options, modifies them based on the environment variables, and returns the result. + /// + /// This is equivalent to calling `with_env` on every field. + #[must_use] + pub fn with_env(self) -> Self { + let gl = self.gl.with_env(); + let dx12 = self.dx12.with_env(); + Self { gl, dx12 } + } +} + +/// Configuration for the OpenGL/OpenGLES backend. +#[derive(Clone, Debug, Default)] +pub struct GlBackendOptions { + /// Which OpenGL ES 3 minor version to request, if using OpenGL ES. + pub gles_minor_version: Gles3MinorVersion, +} + +impl GlBackendOptions { + /// Choose OpenGL backend options by calling `from_env` on every field. + /// + /// See those methods for more information. + #[must_use] + pub fn from_env_or_default() -> Self { + let gles_minor_version = Gles3MinorVersion::from_env().unwrap_or_default(); + Self { gles_minor_version } + } + + /// Takes the given options, modifies them based on the environment variables, and returns the result. + /// + /// This is equivalent to calling `with_env` on every field. + #[must_use] + pub fn with_env(self) -> Self { + let gles_minor_version = self.gles_minor_version.with_env(); + Self { gles_minor_version } + } +} + +/// Configuration for the DX12 backend. +#[derive(Clone, Debug, Default)] +pub struct Dx12BackendOptions { + /// Which DX12 shader compiler to use. + pub shader_compiler: Dx12Compiler, +} + +impl Dx12BackendOptions { + /// Choose DX12 backend options by calling `from_env` on every field. + /// + /// See those methods for more information. + #[must_use] + pub fn from_env_or_default() -> Self { + let compiler = Dx12Compiler::from_env().unwrap_or_default(); + Self { + shader_compiler: compiler, + } + } + + /// Takes the given options, modifies them based on the environment variables, and returns the result. + /// + /// This is equivalent to calling `with_env` on every field. + #[must_use] + pub fn with_env(self) -> Self { + let shader_compiler = self.shader_compiler.with_env(); + Self { shader_compiler } + } +} + +/// Selects which DX12 shader compiler to use. +/// +/// If the `DynamicDxc` option is selected, but `dxcompiler.dll` and `dxil.dll` files aren't found, +/// then this will fall back to the Fxc compiler at runtime and log an error. +#[derive(Clone, Debug, Default)] +pub enum Dx12Compiler { + /// The Fxc compiler (default) is old, slow and unmaintained. + /// + /// However, it doesn't require any additional .dlls to be shipped with the application. + #[default] + Fxc, + /// The Dxc compiler is new, fast and maintained. + /// + /// However, it requires both `dxcompiler.dll` and `dxil.dll` to be shipped with the application. + /// These files can be downloaded from . + /// + /// Minimum supported version: [v1.5.2010](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.5.2010) + /// + /// It also requires WDDM 2.1 (Windows 10 version 1607). + DynamicDxc { + /// Path to `dxcompiler.dll`. + dxc_path: String, + /// Path to `dxil.dll`. + dxil_path: String, + }, + /// The statically-linked variant of Dxc. + /// + /// The `static-dxc` feature is required for this setting to be used successfully on DX12. + /// Not available on `windows-aarch64-pc-*` targets. + StaticDxc, +} + +impl Dx12Compiler { + /// Choose which DX12 shader compiler to use from the environment variable `WGPU_DX12_COMPILER`. + /// + /// Valid values, case insensitive: + /// - `Fxc` + /// - `Dxc` or `DynamicDxc` + /// - `StaticDxc` + #[must_use] + pub fn from_env() -> Option { + let value = crate::env::var("WGPU_DX12_COMPILER") + .as_deref()? + .to_lowercase(); + match value.as_str() { + "dxc" | "dynamicdxc" => Some(Self::DynamicDxc { + dxc_path: String::from("dxcompiler.dll"), + dxil_path: String::from("dxil.dll"), + }), + "staticdxc" => Some(Self::StaticDxc), + "fxc" => Some(Self::Fxc), + _ => None, + } + } + + /// Takes the given compiler, modifies it based on the `WGPU_DX12_COMPILER` environment variable, and returns the result. + /// + /// See `from_env` for more information. + #[must_use] + pub fn with_env(self) -> Self { + if let Some(compiler) = Self::from_env() { + compiler + } else { + self + } + } +} + +/// Selects which OpenGL ES 3 minor version to request. +/// +/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] +pub enum Gles3MinorVersion { + /// No explicit minor version is requested, the driver automatically picks the highest available. + #[default] + Automatic, + + /// Request an ES 3.0 context. + Version0, + + /// Request an ES 3.1 context. + Version1, + + /// Request an ES 3.2 context. + Version2, +} + +impl Gles3MinorVersion { + /// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GLES_MINOR_VERSION`. + /// + /// Possible values are `0`, `1`, `2` or `automatic`. Case insensitive. + /// + /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set. + #[must_use] + pub fn from_env() -> Option { + let value = crate::env::var("WGPU_GLES_MINOR_VERSION") + .as_deref()? + .to_lowercase(); + match value.as_str() { + "automatic" => Some(Self::Automatic), + "0" => Some(Self::Version0), + "1" => Some(Self::Version1), + "2" => Some(Self::Version2), + _ => None, + } + } + + /// Takes the given compiler, modifies it based on the `WGPU_GLES_MINOR_VERSION` environment variable, and returns the result. + /// + /// See `from_env` for more information. + #[must_use] + pub fn with_env(self) -> Self { + if let Some(compiler) = Self::from_env() { + compiler + } else { + self + } + } +} diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index bf318b95d4..b843b9e9f6 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -30,9 +30,12 @@ use { pub mod assertions; mod counters; +mod env; +pub mod instance; pub mod math; pub use counters::*; +pub use instance::*; /// Integral type used for buffer offsets. pub type BufferAddress = u64; @@ -123,6 +126,19 @@ pub enum PowerPreference { HighPerformance = 2, } +impl PowerPreference { + /// Get a power preference from the environment variable `WGPU_POWER_PREF`. + pub fn from_env() -> Option { + let env = crate::env::var("WGPU_POWER_PREF")?; + match env.to_lowercase().as_str() { + "low" => Some(Self::LowPower), + "high" => Some(Self::HighPerformance), + "none" => Some(Self::None), + _ => None, + } + } +} + bitflags::bitflags! { /// Represents the backends that wgpu will use. #[repr(transparent)] @@ -176,6 +192,60 @@ impl From for Backends { } } +impl Backends { + /// Gets a set of backends from the environment variable `WGPU_BACKEND`. + /// + /// See [`Self::from_comma_list()`] for the format of the string. + pub fn from_env() -> Option { + let env = crate::env::var("WGPU_BACKEND")?; + Some(Self::from_comma_list(&env)) + } + + /// Takes the given options, modifies them based on the `WGPU_BACKEND` environment variable, and returns the result. + pub fn with_env(&self) -> Self { + if let Some(env) = Self::from_env() { + env + } else { + *self + } + } + + /// Generates a set of backends from a comma separated list of case-insensitive backend names. + /// + /// Whitespace is stripped, so both 'gl, dx12' and 'gl,dx12' are valid. + /// + /// Always returns WEBGPU on wasm over webgpu. + /// + /// Names: + /// - vulkan = "vulkan" or "vk" + /// - dx12 = "dx12" or "d3d12" + /// - metal = "metal" or "mtl" + /// - gles = "opengl" or "gles" or "gl" + /// - webgpu = "webgpu" + pub fn from_comma_list(string: &str) -> Self { + let mut backends = Self::empty(); + for backend in string.to_lowercase().split(',') { + backends |= match backend.trim() { + "vulkan" | "vk" => Self::VULKAN, + "dx12" | "d3d12" => Self::DX12, + "metal" | "mtl" => Self::METAL, + "opengl" | "gles" | "gl" => Self::GL, + "webgpu" => Self::BROWSER_WEBGPU, + b => { + log::warn!("unknown backend string '{}'", b); + continue; + } + } + } + + if backends.is_empty() { + log::warn!("no valid backend strings found!"); + } + + backends + } +} + /// Options for requesting adapter. /// /// Corresponds to [WebGPU `GPURequestAdapterOptions`]( @@ -1015,118 +1085,6 @@ impl Features { } } -bitflags::bitflags! { - /// Instance debugging flags. - /// - /// These are not part of the webgpu standard. - /// - /// Defaults to enabling debugging-related flags if the build configuration has `debug_assertions`. - #[repr(transparent)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub struct InstanceFlags: u32 { - /// Generate debug information in shaders and objects. - const DEBUG = 1 << 0; - /// Enable validation, if possible. - const VALIDATION = 1 << 1; - /// Don't pass labels to wgpu-hal. - const DISCARD_HAL_LABELS = 1 << 2; - /// Whether wgpu should expose adapters that run on top of non-compliant adapters. - /// - /// Turning this on might mean that some of the functionality provided by the wgpu - /// adapter/device is not working or is broken. It could be that all the functionality - /// wgpu currently exposes works but we can't tell for sure since we have no additional - /// transparency into what is working and what is not on the underlying adapter. - /// - /// This mainly applies to a Vulkan driver's compliance version. If the major compliance version - /// is `0`, then the driver is ignored. This flag allows that driver to be enabled for testing. - const ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER = 1 << 3; - /// Enable GPU-based validation. Implies [`Self::VALIDATION`]. Currently, this only changes - /// behavior on the DX12 and Vulkan backends. - /// - /// Supported platforms: - /// - /// - D3D12; called ["GPU-based validation", or - /// "GBV"](https://web.archive.org/web/20230206120404/https://learn.microsoft.com/en-us/windows/win32/direct3d12/using-d3d12-debug-layer-gpu-based-validation) - /// - Vulkan, via the `VK_LAYER_KHRONOS_validation` layer; called ["GPU-Assisted - /// Validation"](https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/e45aeb85079e0835694cb8f03e6681fd18ae72c9/docs/gpu_validation.md#gpu-assisted-validation) - const GPU_BASED_VALIDATION = 1 << 4; - } -} - -impl Default for InstanceFlags { - fn default() -> Self { - Self::from_build_config() - } -} - -impl InstanceFlags { - /// Enable recommended debugging and validation flags. - #[must_use] - pub fn debugging() -> Self { - InstanceFlags::DEBUG | InstanceFlags::VALIDATION - } - - /// Enable advanced debugging and validation flags (potentially very slow). - #[must_use] - pub fn advanced_debugging() -> Self { - Self::debugging() | InstanceFlags::GPU_BASED_VALIDATION - } - - /// Infer good defaults from the build type - /// - /// Returns the default flags and add debugging flags if the build configuration has `debug_assertions`. - #[must_use] - pub fn from_build_config() -> Self { - if cfg!(debug_assertions) { - return InstanceFlags::debugging(); - } - - InstanceFlags::empty() - } - - /// Returns this set of flags, affected by environment variables. - /// - /// The presence of an environment variable implies that the corresponding flag should be set - /// unless the value is "0" in which case the flag is unset. If the environment variable is - /// not present, then the flag is unaffected. - /// - /// For example `let flags = InstanceFlags::debugging().with_env();` with `WGPU_VALIDATION=0` - /// does not contain `InstanceFlags::VALIDATION`. - /// - /// The environment variables are named after the flags prefixed with "WGPU_". For example: - /// - WGPU_DEBUG - /// - WGPU_VALIDATION - #[must_use] - pub fn with_env(mut self) -> Self { - fn env(_key: &str) -> Option { - #[cfg(feature = "std")] - return std::env::var(_key).ok().map(|s| match s.as_str() { - "0" => false, - _ => true, - }); - - // Without access to std, environment variables are considered unset - #[cfg(not(feature = "std"))] - return None; - } - - if let Some(bit) = env("WGPU_VALIDATION") { - self.set(Self::VALIDATION, bit); - } - if let Some(bit) = env("WGPU_DEBUG") { - self.set(Self::DEBUG, bit); - } - if let Some(bit) = env("WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER") { - self.set(Self::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER, bit); - } - if let Some(bit) = env("WGPU_GPU_BASED_VALIDATION") { - self.set(Self::GPU_BASED_VALIDATION, bit); - } - - self - } -} - /// Represents the sets of limits an adapter/device supports. /// /// We provide three different defaults. @@ -7755,84 +7713,6 @@ impl Default for ShaderRuntimeChecks { } } -/// Selects which DX12 shader compiler to use. -/// -/// If the `Dxc` option is selected, but `dxcompiler.dll` and `dxil.dll` files aren't found, -/// then this will fall back to the Fxc compiler at runtime and log an error. -/// -/// `wgpu::utils::init::dx12_shader_compiler_from_env` can be used to set the compiler -/// from the `WGPU_DX12_SHADER_COMPILER` environment variable, but this should only be used for testing. -#[derive(Clone, Debug, Default)] -pub enum Dx12Compiler { - /// The Fxc compiler (default) is old, slow and unmaintained. - /// - /// However, it doesn't require any additional .dlls to be shipped with the application. - #[default] - Fxc, - /// The Dxc compiler is new, fast and maintained. - /// - /// However, it requires both `dxcompiler.dll` and `dxil.dll` to be shipped with the application. - /// These files can be downloaded from . - /// - /// Minimum supported version: [v1.5.2010](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.5.2010) - /// - /// It also requires WDDM 2.1 (Windows 10 version 1607). - DynamicDxc { - /// Path to `dxcompiler.dll`. - dxc_path: String, - /// Path to `dxil.dll`. - dxil_path: String, - }, - /// The statically-linked variant of Dxc. - /// - /// The `static-dxc` feature is required for this setting to be used successfully on DX12. - /// Not available on `windows-aarch64-pc-*` targets. - StaticDxc, -} - -/// Selects which OpenGL ES 3 minor version to request. -/// -/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] -pub enum Gles3MinorVersion { - /// No explicit minor version is requested, the driver automatically picks the highest available. - #[default] - Automatic, - - /// Request an ES 3.0 context. - Version0, - - /// Request an ES 3.1 context. - Version1, - - /// Request an ES 3.2 context. - Version2, -} - -/// Options for creating an instance. -#[derive(Clone, Debug)] -pub struct InstanceDescriptor { - /// Which `Backends` to enable. - pub backends: Backends, - /// Flags to tune the behavior of the instance. - pub flags: InstanceFlags, - /// Which DX12 shader compiler to use. - pub dx12_shader_compiler: Dx12Compiler, - /// Which OpenGL ES 3 minor version to request. Will be ignored if OpenGL is available. - pub gles_minor_version: Gles3MinorVersion, -} - -impl Default for InstanceDescriptor { - fn default() -> Self { - Self { - backends: Backends::all(), - flags: InstanceFlags::default(), - dx12_shader_compiler: Dx12Compiler::default(), - gles_minor_version: Gles3MinorVersion::default(), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// Descriptor for all size defining attributes of a single triangle geometry inside a bottom level acceleration structure. diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index d45227dcf2..b83ae8c857 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -51,20 +51,20 @@ pub mod util; pub use api::*; pub use wgt::{ - AdapterInfo, AddressMode, AstcBlock, AstcChannel, Backend, Backends, BindGroupLayoutEntry, - BindingType, BlendComponent, BlendFactor, BlendOperation, BlendState, BufferAddress, - BufferBindingType, BufferSize, BufferUsages, Color, ColorTargetState, ColorWrites, - CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CoreCounters, DepthBiasState, - DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, DownlevelFlags, - Dx12Compiler, DynamicOffset, Extent3d, Face, Features, FilterMode, FrontFace, - Gles3MinorVersion, HalCounters, ImageSubresourceRange, IndexFormat, InstanceDescriptor, - InstanceFlags, InternalCounters, Limits, MaintainResult, MemoryHints, MultisampleState, - Origin2d, Origin3d, PipelineStatisticsTypes, PolygonMode, PowerPreference, - PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState, PrimitiveTopology, - PushConstantRange, QueryType, RenderBundleDepthStencil, SamplerBindingType, SamplerBorderColor, - ShaderLocation, ShaderModel, ShaderRuntimeChecks, ShaderStages, StencilFaceState, - StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities, SurfaceStatus, - TexelCopyBufferLayout, TextureAspect, TextureDimension, TextureFormat, + AdapterInfo, AddressMode, AstcBlock, AstcChannel, Backend, BackendOptions, Backends, + BindGroupLayoutEntry, BindingType, BlendComponent, BlendFactor, BlendOperation, BlendState, + BufferAddress, BufferBindingType, BufferSize, BufferUsages, Color, ColorTargetState, + ColorWrites, CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CoreCounters, + DepthBiasState, DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, + DownlevelFlags, Dx12BackendOptions, Dx12Compiler, DynamicOffset, Extent3d, Face, Features, + FilterMode, FrontFace, GlBackendOptions, Gles3MinorVersion, HalCounters, ImageSubresourceRange, + IndexFormat, InstanceDescriptor, InstanceFlags, InternalCounters, Limits, MaintainResult, + MemoryHints, MultisampleState, Origin2d, Origin3d, PipelineStatisticsTypes, PolygonMode, + PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState, + PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil, SamplerBindingType, + SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks, ShaderStages, + StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities, + SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureUsages, TextureViewDimension, VertexAttribute, VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, diff --git a/wgpu/src/util/init.rs b/wgpu/src/util/init.rs index 0a2ffa1de3..54de26da65 100644 --- a/wgpu/src/util/init.rs +++ b/wgpu/src/util/init.rs @@ -1,41 +1,7 @@ -use wgt::{Backends, PowerPreference, RequestAdapterOptions}; +use crate::{Adapter, Instance, RequestAdapterOptions, Surface}; -use crate::{Adapter, Instance, Surface}; - -#[cfg(wgpu_core)] -#[cfg_attr(docsrs, doc(cfg(all())))] -pub use wgc::instance::parse_backends_from_comma_list; -/// Just return ALL, if wgpu_core is not enabled. -#[cfg(not(wgpu_core))] -pub fn parse_backends_from_comma_list(_string: &str) -> Backends { - Backends::all() -} - -/// Get a set of backend bits from the environment variable WGPU_BACKEND. -pub fn backend_bits_from_env() -> Option { - std::env::var("WGPU_BACKEND") - .as_deref() - .map(str::to_lowercase) - .ok() - .as_deref() - .map(parse_backends_from_comma_list) -} - -/// Get a power preference from the environment variable WGPU_POWER_PREF -pub fn power_preference_from_env() -> Option { - Some( - match std::env::var("WGPU_POWER_PREF") - .as_deref() - .map(str::to_lowercase) - .as_deref() - { - Ok("low") => PowerPreference::LowPower, - Ok("high") => PowerPreference::HighPerformance, - Ok("none") => PowerPreference::None, - _ => return None, - }, - ) -} +#[cfg(doc)] +use crate::Backends; /// Initialize the adapter obeying the WGPU_ADAPTER_NAME environment variable. #[cfg(native)] @@ -48,7 +14,7 @@ pub fn initialize_adapter_from_env( .map(str::to_lowercase) .ok()?; - let adapters = instance.enumerate_adapters(Backends::all()); + let adapters = instance.enumerate_adapters(crate::Backends::all()); let mut chosen_adapter = None; for adapter in adapters { @@ -88,7 +54,7 @@ pub async fn initialize_adapter_from_env_or_default( None => { instance .request_adapter(&RequestAdapterOptions { - power_preference: power_preference_from_env().unwrap_or_default(), + power_preference: crate::PowerPreference::from_env().unwrap_or_default(), force_fallback_adapter: false, compatible_surface, }) @@ -97,65 +63,6 @@ pub async fn initialize_adapter_from_env_or_default( } } -/// Choose which DX12 shader compiler to use from the environment variable `WGPU_DX12_COMPILER`. -/// -/// Possible values are `dxc` and `fxc`. Case insensitive. -pub fn dx12_shader_compiler_from_env() -> Option { - Some( - match std::env::var("WGPU_DX12_COMPILER") - .as_deref() - .map(str::to_lowercase) - .as_deref() - { - Ok("dxc") => wgt::Dx12Compiler::DynamicDxc { - dxc_path: "dxcompiler.dll".to_string(), - dxil_path: "dxil.dll".to_string(), - }, - #[cfg(static_dxc)] - Ok("static-dxc") => wgt::Dx12Compiler::StaticDxc, - Ok("fxc") => wgt::Dx12Compiler::Fxc, - _ => return None, - }, - ) -} - -/// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GLES_MINOR_VERSION`. -/// -/// Possible values are `0`, `1`, `2` or `automatic`. Case insensitive. -pub fn gles_minor_version_from_env() -> Option { - Some( - match std::env::var("WGPU_GLES_MINOR_VERSION") - .as_deref() - .map(str::to_lowercase) - .as_deref() - { - Ok("automatic") => wgt::Gles3MinorVersion::Automatic, - Ok("0") => wgt::Gles3MinorVersion::Version0, - Ok("1") => wgt::Gles3MinorVersion::Version1, - Ok("2") => wgt::Gles3MinorVersion::Version2, - _ => return None, - }, - ) -} - -/// Get an instance descriptor from the following environment variables: -/// -/// - WGPU_BACKEND -/// - WGPU_DEBUG -/// - WGPU_VALIDATION -/// - WGPU_DX12_COMPILER -/// - WGPU_GLES_MINOR_VERSION -/// -/// If variables are missing, falls back to default or build config values -pub fn instance_descriptor_from_env() -> wgt::InstanceDescriptor { - wgt::InstanceDescriptor { - backends: backend_bits_from_env().unwrap_or_default(), - flags: wgt::InstanceFlags::from_build_config().with_env(), - dx12_shader_compiler: dx12_shader_compiler_from_env().unwrap_or_default(), - gles_minor_version: gles_minor_version_from_env().unwrap_or_default(), - } -} - /// Determines whether the [`Backends::BROWSER_WEBGPU`] backend is supported. /// /// The result can only be true if this is called from the main thread or a dedicated worker.