Skip to main content

wgpu_types/
backend.rs

1//! [`Backend`], [`Backends`], and backend-specific options.
2
3use alloc::string::String;
4use core::{hash::Hash, str::FromStr};
5
6#[cfg(any(feature = "serde", test))]
7use serde::{Deserialize, Serialize};
8
9use crate::link_to_wgpu_docs;
10
11#[cfg(doc)]
12use crate::InstanceDescriptor;
13
14/// Backends supported by wgpu.
15///
16/// See also [`Backends`].
17#[repr(u8)]
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub enum Backend {
21    /// Dummy backend, which may be used for testing.
22    ///
23    /// It performs no rendering or computation, but allows creation of stub GPU resource types,
24    /// so that code which manages GPU resources can be tested without an available GPU.
25    /// Specifically, the following operations are implemented:
26    ///
27    /// * Enumerating adapters will always return one noop adapter, which can be used to create
28    ///   devices.
29    /// * Buffers may be created, written, mapped, and copied to other buffers.
30    /// * Command encoders may be created, but only buffer operations are useful.
31    ///
32    /// Other resources can be created but are nonfunctional; notably,
33    ///
34    /// * Render passes and compute passes are not executed.
35    /// * Textures may be created, but do not store any texels.
36    /// * There are no compatible surfaces.
37    ///
38    /// An adapter using the noop backend can only be obtained if [`NoopBackendOptions`]
39    /// enables it, in addition to the ordinary requirement of [`Backends::NOOP`] being set.
40    /// This ensures that applications not desiring a non-functional backend will not receive it.
41    Noop = 0,
42    /// Vulkan API (Windows, Linux, Android, MacOS via `vulkan-portability`/MoltenVK)
43    Vulkan = 1,
44    /// Metal API (Apple platforms)
45    Metal = 2,
46    /// Direct3D-12 (Windows)
47    Dx12 = 3,
48    /// OpenGL 3.3+ (Windows), OpenGL ES 3.0+ (Linux, Android, MacOS via Angle), and WebGL2
49    Gl = 4,
50    /// WebGPU in the browser
51    BrowserWebGpu = 5,
52}
53
54impl Backend {
55    /// Array of all [`Backend`] values, corresponding to [`Backends::all()`].
56    pub const ALL: [Backend; Backends::all().bits().count_ones() as usize] = [
57        Self::Noop,
58        Self::Vulkan,
59        Self::Metal,
60        Self::Dx12,
61        Self::Gl,
62        Self::BrowserWebGpu,
63    ];
64
65    /// Returns the string name of the backend.
66    #[must_use]
67    pub const fn to_str(self) -> &'static str {
68        match self {
69            Backend::Noop => "noop",
70            Backend::Vulkan => "vulkan",
71            Backend::Metal => "metal",
72            Backend::Dx12 => "dx12",
73            Backend::Gl => "gl",
74            Backend::BrowserWebGpu => "webgpu",
75        }
76    }
77}
78
79impl core::fmt::Display for Backend {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        f.write_str(self.to_str())
82    }
83}
84
85bitflags::bitflags! {
86    /// Represents the backends that wgpu will use.
87    #[repr(transparent)]
88    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89    #[cfg_attr(feature = "serde", serde(transparent))]
90    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
91    pub struct Backends: u32 {
92        /// [`Backend::Noop`].
93        const NOOP = 1 << Backend::Noop as u32;
94
95        /// [`Backend::Vulkan`].
96        /// Supported on Windows, Linux/Android, and macOS/iOS via Vulkan Portability (with the Vulkan feature enabled)
97        const VULKAN = 1 << Backend::Vulkan as u32;
98
99        /// [`Backend::Gl`].
100        /// Supported on Linux/Android, the web through webassembly via WebGL, and Windows and
101        /// macOS/iOS via ANGLE
102        const GL = 1 << Backend::Gl as u32;
103
104        /// [`Backend::Metal`].
105        /// Supported on macOS and iOS.
106        const METAL = 1 << Backend::Metal as u32;
107
108        /// [`Backend::Dx12`].
109        /// Supported on Windows 10 and later
110        const DX12 = 1 << Backend::Dx12 as u32;
111
112        /// [`Backend::BrowserWebGpu`].
113        /// Supported when targeting the web through WebAssembly with the `webgpu` feature enabled.
114        ///
115        /// The WebGPU backend is special in several ways:
116        /// It is not not implemented by `wgpu_core` and instead by the higher level `wgpu` crate.
117        /// Whether WebGPU is targeted is decided upon the creation of the `wgpu::Instance`,
118        /// *not* upon adapter creation. See `wgpu::Instance::new`.
119        const BROWSER_WEBGPU = 1 << Backend::BrowserWebGpu as u32;
120
121        /// All the apis that wgpu offers first tier of support for.
122        ///
123        /// * [`Backends::VULKAN`]
124        /// * [`Backends::METAL`]
125        /// * [`Backends::DX12`]
126        /// * [`Backends::BROWSER_WEBGPU`]
127        const PRIMARY = Self::VULKAN.bits()
128            | Self::METAL.bits()
129            | Self::DX12.bits()
130            | Self::BROWSER_WEBGPU.bits();
131
132        /// All the apis that wgpu offers second tier of support for. These may
133        /// be unsupported/still experimental.
134        ///
135        /// * [`Backends::GL`]
136        const SECONDARY = Self::GL.bits();
137    }
138}
139
140impl Default for Backends {
141    fn default() -> Self {
142        Self::all()
143    }
144}
145
146impl From<Backend> for Backends {
147    fn from(backend: Backend) -> Self {
148        Self::from_bits(1 << backend as u32).unwrap()
149    }
150}
151
152impl Backends {
153    /// Gets a set of backends from the environment variable `WGPU_BACKEND`.
154    ///
155    /// See [`Self::from_comma_list()`] for the format of the string.
156    pub fn from_env() -> Option<Self> {
157        let env = crate::env::var("WGPU_BACKEND")?;
158        Some(Self::from_comma_list(&env))
159    }
160
161    /// Takes the given options, modifies them based on the `WGPU_BACKEND` environment variable, and returns the result.
162    pub fn with_env(&self) -> Self {
163        if let Some(env) = Self::from_env() {
164            env
165        } else {
166            *self
167        }
168    }
169
170    /// Generates a set of backends from a comma separated list of case-insensitive backend names.
171    ///
172    /// Whitespace is stripped, so both 'gl, dx12' and 'gl,dx12' are valid.
173    ///
174    /// Always returns WEBGPU on wasm over webgpu.
175    ///
176    /// Names:
177    /// - vulkan = "vulkan" or "vk"
178    /// - dx12   = "dx12" or "d3d12"
179    /// - metal  = "metal" or "mtl"
180    /// - gles   = "opengl" or "gles" or "gl"
181    /// - webgpu = "webgpu"
182    pub fn from_comma_list(string: &str) -> Self {
183        let mut backends = Self::empty();
184        for backend in string.to_lowercase().split(',') {
185            backends |= match backend.trim() {
186                "vulkan" | "vk" => Self::VULKAN,
187                "dx12" | "d3d12" => Self::DX12,
188                "metal" | "mtl" => Self::METAL,
189                "opengl" | "gles" | "gl" => Self::GL,
190                "webgpu" => Self::BROWSER_WEBGPU,
191                "noop" => Self::NOOP,
192                b => {
193                    log::warn!("unknown backend string '{b}'");
194                    continue;
195                }
196            }
197        }
198
199        if backends.is_empty() {
200            log::warn!("no valid backend strings found!");
201        }
202
203        backends
204    }
205}
206
207/// Options that are passed to a given backend.
208///
209/// Part of [`InstanceDescriptor`].
210#[derive(Clone, Debug, Default)]
211pub struct BackendOptions {
212    /// Options for the OpenGL/OpenGLES backend, [`Backend::Gl`].
213    pub gl: GlBackendOptions,
214    /// Options for the DX12 backend, [`Backend::Dx12`].
215    pub dx12: Dx12BackendOptions,
216    /// Options for the noop backend, [`Backend::Noop`].
217    pub noop: NoopBackendOptions,
218}
219
220impl BackendOptions {
221    /// Choose backend options by calling `from_env` on every field.
222    ///
223    /// See those methods for more information.
224    #[must_use]
225    pub fn from_env_or_default() -> Self {
226        Self {
227            gl: GlBackendOptions::from_env_or_default(),
228            dx12: Dx12BackendOptions::from_env_or_default(),
229            noop: NoopBackendOptions::from_env_or_default(),
230        }
231    }
232
233    /// Takes the given options, modifies them based on the environment variables, and returns the result.
234    ///
235    /// This is equivalent to calling `with_env` on every field.
236    #[must_use]
237    pub fn with_env(self) -> Self {
238        Self {
239            gl: self.gl.with_env(),
240            dx12: self.dx12.with_env(),
241            noop: self.noop.with_env(),
242        }
243    }
244}
245
246/// Configuration for the OpenGL/OpenGLES backend.
247///
248/// Part of [`BackendOptions`].
249#[derive(Clone, Debug, Default)]
250pub struct GlBackendOptions {
251    /// Which OpenGL ES 3 minor version to request, if using OpenGL ES.
252    pub gles_minor_version: Gles3MinorVersion,
253    /// Behavior of OpenGL fences. Affects how `on_completed_work_done` and `device.poll` behave.
254    pub fence_behavior: GlFenceBehavior,
255    /// Controls whether debug functions (`glPushDebugGroup`, `glPopDebugGroup`,
256    /// `glObjectLabel`, etc.) are enabled when supported by the driver.
257    ///
258    /// By default ([`GlDebugFns::Auto`]), debug functions are automatically
259    /// disabled on devices with known bugs (e.g., Mali GPUs can crash in
260    /// `glPushDebugGroup`). Use [`GlDebugFns::ForceEnabled`] to override this
261    /// behavior, or [`GlDebugFns::Disabled`] to disable debug functions entirely.
262    ///
263    /// See also [`InstanceFlags::DISCARD_HAL_LABELS`], which prevents debug
264    /// markers and labels from being sent to *any* backend, but without the
265    /// driver-specific bug workarounds provided here.
266    ///
267    /// [`InstanceFlags::DISCARD_HAL_LABELS`]: crate::InstanceFlags::DISCARD_HAL_LABELS
268    pub debug_fns: GlDebugFns,
269}
270
271impl GlBackendOptions {
272    /// Choose OpenGL backend options by calling `from_env` on every field.
273    ///
274    /// See those methods for more information.
275    #[must_use]
276    pub fn from_env_or_default() -> Self {
277        let gles_minor_version = Gles3MinorVersion::from_env().unwrap_or_default();
278        let debug_fns = GlDebugFns::from_env().unwrap_or_default();
279        Self {
280            gles_minor_version,
281            fence_behavior: GlFenceBehavior::Normal,
282            debug_fns,
283        }
284    }
285
286    /// Takes the given options, modifies them based on the environment variables, and returns the result.
287    ///
288    /// This is equivalent to calling `with_env` on every field.
289    #[must_use]
290    pub fn with_env(self) -> Self {
291        let gles_minor_version = self.gles_minor_version.with_env();
292        let fence_behavior = self.fence_behavior.with_env();
293        let debug_fns = self.debug_fns.with_env();
294        Self {
295            gles_minor_version,
296            fence_behavior,
297            debug_fns,
298        }
299    }
300}
301
302/// Controls whether OpenGL debug functions are enabled.
303///
304/// Debug functions include `glPushDebugGroup`, `glPopDebugGroup`, `glObjectLabel`, etc.
305/// These are useful for debugging but can cause crashes on some buggy drivers.
306#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
307pub enum GlDebugFns {
308    /// Automatically decide whether to enable debug functions.
309    ///
310    /// Debug functions will be enabled if supported by the driver, unless
311    /// running on a device known to have buggy debug function implementations
312    /// (e.g., Mali GPUs which can crash in `glPushDebugGroup`).
313    ///
314    /// This is the default behavior.
315    #[default]
316    Auto,
317    /// Force enable debug functions if supported by the driver.
318    ///
319    /// This ignores any device-specific workarounds and enables debug functions
320    /// on all devices that support them, including those with known bugs.
321    ForceEnabled,
322    /// Disable debug functions entirely.
323    ///
324    /// Debug functions will not be used even if supported by the driver.
325    Disabled,
326}
327
328impl GlDebugFns {
329    /// Choose debug functions setting from the environment variable `WGPU_GL_DEBUG_FNS`.
330    ///
331    /// Possible values (case insensitive):
332    /// - `auto` - automatically decide based on device
333    /// - `forceenabled`, `force_enabled`, or `enabled` - force enable
334    /// - `disabled` - disable entirely
335    ///
336    /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set.
337    #[must_use]
338    pub fn from_env() -> Option<Self> {
339        let value = crate::env::var("WGPU_GL_DEBUG_FNS")
340            .as_deref()?
341            .to_lowercase();
342        match value.as_str() {
343            "auto" => Some(Self::Auto),
344            "forceenabled" | "force_enabled" | "enabled" => Some(Self::ForceEnabled),
345            "disabled" => Some(Self::Disabled),
346            _ => None,
347        }
348    }
349
350    /// Takes the given setting, modifies it based on the `WGPU_GL_DEBUG_FNS` environment variable, and returns the result.
351    ///
352    /// See `from_env` for more information.
353    #[must_use]
354    pub fn with_env(self) -> Self {
355        if let Some(debug_fns) = Self::from_env() {
356            debug_fns
357        } else {
358            self
359        }
360    }
361}
362
363/// Used to force wgpu to expose certain features on passthrough shaders even when
364/// those features aren't present on runtime-compiled shaders
365#[derive(Default, Clone, Debug)]
366pub struct ForceShaderModelToken {
367    inner: Option<DxcShaderModel>,
368}
369impl ForceShaderModelToken {
370    /// Creates an unsafe token, opting you in to seeing features that you may not necessarily use
371    /// on standard runtime-compiled shaders.
372    /// # Safety
373    /// Do not make use in runtime-compiled shaders of any features that may not be supported by the FXC or DXC
374    /// version you use.
375    pub unsafe fn with_shader_model(sm: DxcShaderModel) -> Self {
376        Self { inner: Some(sm) }
377    }
378
379    /// Returns the shader model version, if any, in this token.
380    pub fn get(&self) -> Option<DxcShaderModel> {
381        self.inner.clone()
382    }
383}
384
385/// Behavior when the Agility SDK fails to load.
386///
387/// See [`Dx12AgilitySDK`] for details on the Agility SDK.
388#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
389pub enum Dx12AgilitySDKLoadFailure {
390    /// Log a warning and fall back to the system-installed D3D12 runtime.
391    ///
392    /// This is the default behavior and is appropriate for most applications.
393    #[default]
394    Fallback,
395    /// Fail instance creation entirely if the Agility SDK cannot be loaded.
396    ///
397    /// Use this in environments where you are shipping the Agility SDK alongside your application
398    /// and want to ensure that it is being loaded correctly.
399    Error,
400}
401
402impl Dx12AgilitySDKLoadFailure {
403    /// Read the load failure behavior from the environment variable
404    /// `WGPU_DX12_AGILITY_SDK_REQUIRE`.
405    ///
406    /// When set to `1`, returns [`Error`](Self::Error).
407    /// When set to `0`, returns [`Fallback`](Self::Fallback).
408    #[must_use]
409    pub fn from_env() -> Option<Self> {
410        let value = crate::env::var("WGPU_DX12_AGILITY_SDK_REQUIRE")?;
411        match value.as_str() {
412            "1" => Some(Self::Error),
413            "0" => Some(Self::Fallback),
414            _ => None,
415        }
416    }
417
418    /// Takes the given setting, modifies it based on the
419    /// `WGPU_DX12_AGILITY_SDK_REQUIRE` environment variable, and returns the result.
420    ///
421    /// See [`from_env`](Self::from_env) for more information.
422    #[must_use]
423    pub fn with_env(self) -> Self {
424        if let Some(v) = Self::from_env() {
425            v
426        } else {
427            self
428        }
429    }
430}
431
432/// Configuration for loading a specific [DirectX 12 Agility SDK] runtime.
433///
434/// The Agility SDK allows applications to ship a newer version of the D3D12 runtime
435/// (`D3D12Core.dll`) alongside the application, enabling access to the latest D3D12
436/// features without waiting for the OS to update its built-in runtime. This is the
437/// standard way for games and applications to adopt new D3D12 functionality on older
438/// Windows versions.
439///
440/// Downloads and release notes are available at the [DirectX 12 Agility SDK] page.
441///
442/// wgpu loads the Agility SDK via the [Independent Devices API], which allows
443/// specifying the SDK path and version at runtime without requiring exported constants
444/// or developer mode. The [`sdk_version`](Self::sdk_version) must match the version of
445/// the `D3D12Core.dll` in the provided path exactly, or loading will fail, irrespective of
446/// the OS's built-in runtime version.
447///
448/// If the shipped SDK is older than the system runtime, the system runtime will be used.
449/// This allows applications to ship a minimum SDK version while still benefiting from SDK updates on the user's system.
450///
451/// If the Agility SDK fails to load (version mismatch, missing DLL, unsupported OS,
452/// etc.), the behavior is controlled by [`on_load_failure`](Self::on_load_failure).
453/// By default, wgpu logs a warning and falls back to the system-installed D3D12 runtime.
454/// Set it to [`Error`](Dx12AgilitySDKLoadFailure::Error) to fail instance creation instead
455/// (useful in CI/testing).
456///
457/// ## OS requirements
458///
459/// The Independent Devices API requires a Windows update from August/September 2023
460/// or newer:
461///
462/// - [Windows 11 21H2+ (KB5029332)][win11-21h2]
463/// - [Windows 10 22H2+ (KB5029331)][win10-22h2]
464/// - [Windows Server 2022+ (KB5030216)][server-2022]
465///
466/// On older OS builds the Agility SDK will not load and wgpu will log a warning and
467/// fall back to the system runtime (or error, per [`on_load_failure`](Self::on_load_failure)).
468///
469/// [DirectX 12 Agility SDK]: https://devblogs.microsoft.com/directx/directx12agility/
470/// [Independent Devices API]: https://devblogs.microsoft.com/directx/d3d12-independent-devices/
471/// [win11-21h2]: https://support.microsoft.com/en-us/topic/august-22-2023-kb5029332-os-build-22000-2360-preview-8f8aec64-77b4-4225-9a0f-f0153204ae28
472/// [win10-22h2]: https://support.microsoft.com/en-gb/topic/august-22-2023-kb5029331-os-build-19045-3393-preview-9f6c1dbd-0ee6-469b-af24-f9d0bf35ca18
473/// [server-2022]: https://support.microsoft.com/en-au/topic/september-12-2023-kb5030216-os-build-20348-1970-34d4aff3-fd05-4270-b288-4ab6379c7f81
474#[derive(Clone, Debug)]
475pub struct Dx12AgilitySDK {
476    /// The Agility SDK version number (e.g., 614 for SDK version 1.614.0).
477    ///
478    /// This must match the version of the `D3D12Core.dll` at [`sdk_path`](Self::sdk_path)
479    /// exactly, or the runtime will fail to load.
480    pub sdk_version: u32,
481    /// Path to the directory containing the Agility SDK's `D3D12Core.dll`.
482    pub sdk_path: String,
483    /// What to do if the Agility SDK fails to load.
484    ///
485    /// Defaults to [`Fallback`](Dx12AgilitySDKLoadFailure::Fallback).
486    ///
487    /// Can also be set via the `WGPU_DX12_AGILITY_SDK_REQUIRE` environment variable
488    /// (`1` for [`Error`](Dx12AgilitySDKLoadFailure::Error),
489    /// `0` for [`Fallback`](Dx12AgilitySDKLoadFailure::Fallback)).
490    pub on_load_failure: Dx12AgilitySDKLoadFailure,
491}
492
493impl Dx12AgilitySDK {
494    /// Read Agility SDK configuration from environment variables.
495    ///
496    /// Reads `WGPU_DX12_AGILITY_SDK_PATH`, `WGPU_DX12_AGILITY_SDK_VERSION`,
497    /// and `WGPU_DX12_AGILITY_SDK_REQUIRE`.
498    /// Both path and version must be set for this to return `Some`.
499    #[must_use]
500    pub fn from_env() -> Option<Self> {
501        let sdk_path = crate::env::var("WGPU_DX12_AGILITY_SDK_PATH")?;
502        let sdk_version_str = crate::env::var("WGPU_DX12_AGILITY_SDK_VERSION")?;
503        let sdk_version = sdk_version_str.parse::<u32>().ok()?;
504        let on_load_failure = Dx12AgilitySDKLoadFailure::from_env().unwrap_or_default();
505        Some(Self {
506            sdk_version,
507            sdk_path,
508            on_load_failure,
509        })
510    }
511
512    /// Takes the given configuration, overrides fields with environment variables if present,
513    /// and returns the result.
514    ///
515    /// Reads `WGPU_DX12_AGILITY_SDK_PATH`, `WGPU_DX12_AGILITY_SDK_VERSION`,
516    /// and `WGPU_DX12_AGILITY_SDK_REQUIRE`.
517    /// Each variable overrides the corresponding field independently.
518    #[must_use]
519    pub fn with_env(mut self) -> Self {
520        if let Some(sdk_path) = crate::env::var("WGPU_DX12_AGILITY_SDK_PATH") {
521            self.sdk_path = sdk_path;
522        }
523        if let Some(sdk_version_str) = crate::env::var("WGPU_DX12_AGILITY_SDK_VERSION") {
524            if let Ok(sdk_version) = sdk_version_str.parse::<u32>() {
525                self.sdk_version = sdk_version;
526            }
527        }
528        self.on_load_failure = self.on_load_failure.with_env();
529        self
530    }
531}
532
533/// Configuration for the DX12 backend.
534///
535/// Part of [`BackendOptions`].
536#[derive(Clone, Debug, Default)]
537pub struct Dx12BackendOptions {
538    /// Which DX12 shader compiler to use.
539    pub shader_compiler: Dx12Compiler,
540    /// Presentation system to use.
541    pub presentation_system: Dx12SwapchainKind,
542    /// Whether to wait for the latency waitable object before acquiring the next swapchain image.
543    pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject,
544    /// For use with passthrough shaders. Expose features as if this shader model is present, even if you do not
545    /// intend to ship DXC with your app.
546    ///
547    /// This does not override the device's shader model version, only the external shader compiler's version.
548    pub force_shader_model: ForceShaderModelToken,
549    /// Optional Agility SDK configuration for using the Independent Devices API.
550    ///
551    /// When set, wgpu will attempt to load the specified D3D12 runtime via the
552    /// Independent Devices API. If the API is unavailable or the configuration is
553    /// invalid, it falls back to the system-installed D3D12 runtime.
554    ///
555    /// Can also be set via `WGPU_DX12_AGILITY_SDK_PATH` and `WGPU_DX12_AGILITY_SDK_VERSION`
556    /// environment variables.
557    pub agility_sdk: Option<Dx12AgilitySDK>,
558}
559
560impl Dx12BackendOptions {
561    /// Choose DX12 backend options by calling `from_env` on every field.
562    ///
563    /// See those methods for more information.
564    #[must_use]
565    pub fn from_env_or_default() -> Self {
566        let compiler = Dx12Compiler::from_env().unwrap_or_default();
567        let presentation_system = Dx12SwapchainKind::from_env().unwrap_or_default();
568        let latency_waitable_object =
569            Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
570        let agility_sdk = Dx12AgilitySDK::from_env();
571        Self {
572            shader_compiler: compiler,
573            presentation_system,
574            latency_waitable_object,
575            force_shader_model: ForceShaderModelToken::default(),
576            agility_sdk,
577        }
578    }
579
580    /// Takes the given options, modifies them based on the environment variables, and returns the result.
581    ///
582    /// This is equivalent to calling `with_env` on every field.
583    #[must_use]
584    pub fn with_env(self) -> Self {
585        let shader_compiler = self.shader_compiler.with_env();
586        let presentation_system = self.presentation_system.with_env();
587        let latency_waitable_object = self.latency_waitable_object.with_env();
588        let agility_sdk = self
589            .agility_sdk
590            .map(|s| s.with_env())
591            .or_else(Dx12AgilitySDK::from_env);
592        Self {
593            shader_compiler,
594            presentation_system,
595            latency_waitable_object,
596            force_shader_model: ForceShaderModelToken::default(),
597            agility_sdk,
598        }
599    }
600}
601
602/// Configuration for the noop backend.
603///
604/// Part of [`BackendOptions`].
605#[derive(Clone, Debug, Default)]
606pub struct NoopBackendOptions {
607    /// Whether to allow the noop backend to be used.
608    ///
609    /// The noop backend stubs out all operations except for buffer creation and mapping, so
610    /// it must not be used when not expected. Therefore, it will not be used unless explicitly
611    /// enabled.
612    pub enable: bool,
613}
614
615impl NoopBackendOptions {
616    /// Choose whether the noop backend is enabled from the environment.
617    ///
618    /// It will be enabled if the environment variable `WGPU_NOOP_BACKEND` has the value `1`
619    /// and not otherwise. Future versions may assign other meanings to other values.
620    #[must_use]
621    pub fn from_env_or_default() -> Self {
622        Self {
623            enable: Self::enable_from_env().unwrap_or(false),
624        }
625    }
626
627    /// Takes the given options, modifies them based on the environment variables, and returns the
628    /// result.
629    ///
630    /// See [`from_env_or_default()`](Self::from_env_or_default) for the interpretation.
631    #[must_use]
632    pub fn with_env(self) -> Self {
633        Self {
634            enable: Self::enable_from_env().unwrap_or(self.enable),
635        }
636    }
637
638    fn enable_from_env() -> Option<bool> {
639        let value = crate::env::var("WGPU_NOOP_BACKEND")?;
640        match value.as_str() {
641            "1" => Some(true),
642            "0" => Some(false),
643            _ => None,
644        }
645    }
646}
647
648#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)]
649/// Selects which kind of swapchain to use on DX12.
650pub enum Dx12SwapchainKind {
651    /// Use a DXGI swapchain made directly from the window's HWND.
652    ///
653    /// This does not support transparency but has better support from developer tooling from RenderDoc.
654    #[default]
655    DxgiFromHwnd,
656    /// Use a DXGI swapchain made from a DirectComposition visual made automatically from the window's HWND.
657    ///
658    /// This creates a single [`IDCompositionVisual`] over the entire window that is used by the `Surface`.
659    /// If a user wants to manage the composition tree themselves, they should create their own device and
660    /// composition, and pass the relevant visual down via [`SurfaceTargetUnsafe::CompositionVisual`][CV].
661    ///
662    /// This supports transparent windows, but does not have support from RenderDoc.
663    ///
664    /// [`IDCompositionVisual`]: https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nn-dcomp-idcompositionvisual
665    #[doc = link_to_wgpu_docs!(["CV"]: "struct.SurfaceTargetUnsafe.html#variant.CompositionVisual")]
666    DxgiFromVisual,
667}
668
669impl Dx12SwapchainKind {
670    /// Choose which presentation system to use from the environment variable `WGPU_DX12_PRESENTATION_SYSTEM`.
671    ///
672    /// Valid values, case insensitive:
673    /// - `DxgiFromVisual` or `Visual`
674    /// - `DxgiFromHwnd` or `Hwnd` for [`Self::DxgiFromHwnd`]
675    #[must_use]
676    pub fn from_env() -> Option<Self> {
677        let value = crate::env::var("WGPU_DX12_PRESENTATION_SYSTEM")
678            .as_deref()?
679            .to_lowercase();
680        match value.as_str() {
681            "dxgifromvisual" | "visual" => Some(Self::DxgiFromVisual),
682            "dxgifromhwnd" | "hwnd" => Some(Self::DxgiFromHwnd),
683            _ => None,
684        }
685    }
686
687    /// Takes the given presentation system, modifies it based on the `WGPU_DX12_PRESENTATION_SYSTEM` environment variable, and returns the result.
688    ///
689    /// See [`from_env`](Self::from_env) for more information.
690    #[must_use]
691    pub fn with_env(self) -> Self {
692        if let Some(presentation_system) = Self::from_env() {
693            presentation_system
694        } else {
695            self
696        }
697    }
698}
699
700/// DXC shader model.
701#[derive(Clone, Debug)]
702#[allow(missing_docs)]
703pub enum DxcShaderModel {
704    V6_0,
705    V6_1,
706    V6_2,
707    V6_3,
708    V6_4,
709    V6_5,
710    V6_6,
711    V6_7,
712    V6_8,
713    V6_9,
714}
715
716impl DxcShaderModel {
717    /// Get the shader model supported by a certain DXC version.
718    pub fn from_dxc_version(major: u32, minor: u32) -> Self {
719        // DXC version roughly has corresponded to shader model so far, where DXC 1.x supports SM 6.x.
720        // See discussion in https://discord.com/channels/590611987420020747/996417435374714920/1471234702206701650.
721        // Presumably DXC 2.0 and up will still support shader model 6.9.
722        if major > 1 {
723            Self::V6_9
724        } else {
725            Self::from_parts(6, minor)
726        }
727    }
728
729    /// Parse a DxcShaderModel from its version components.
730    pub fn from_parts(major: u32, minor: u32) -> Self {
731        if major > 6 || minor > 8 {
732            Self::V6_9
733        } else {
734            match minor {
735                0 => DxcShaderModel::V6_0,
736                1 => DxcShaderModel::V6_1,
737                2 => DxcShaderModel::V6_2,
738                3 => DxcShaderModel::V6_3,
739                4 => DxcShaderModel::V6_4,
740                5 => DxcShaderModel::V6_5,
741                6 => DxcShaderModel::V6_6,
742                7 => DxcShaderModel::V6_7,
743                8 => DxcShaderModel::V6_8,
744                9 => DxcShaderModel::V6_9,
745                // > 6.9
746                _ => DxcShaderModel::V6_9,
747            }
748        }
749    }
750}
751
752/// Selects which DX12 shader compiler to use.
753#[derive(Clone, Debug, Default)]
754pub enum Dx12Compiler {
755    /// The Fxc compiler (default) is old, slow and unmaintained.
756    ///
757    /// However, it doesn't require any additional .dlls to be shipped with the application.
758    Fxc,
759    /// The Dxc compiler is new, fast and maintained.
760    ///
761    /// However, it requires `dxcompiler.dll` to be shipped with the application.
762    /// These files can be downloaded from <https://github.com/microsoft/DirectXShaderCompiler/releases>.
763    ///
764    /// Minimum supported version: [v1.8.2502](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.8.2502)
765    ///
766    /// It also requires WDDM 2.1 (Windows 10 version 1607).
767    DynamicDxc {
768        /// Path to `dxcompiler.dll`.
769        dxc_path: String,
770    },
771    /// The statically-linked variant of Dxc.
772    ///
773    /// The `static-dxc` feature is required for this setting to be used successfully on DX12.
774    /// Not available on `windows-aarch64-pc-*` targets.
775    StaticDxc,
776    /// Use statically-linked DXC if available. Otherwise check for dynamically linked DXC on the PATH. Finally, fallback to FXC.
777    #[default]
778    Auto,
779}
780
781impl Dx12Compiler {
782    /// Helper function to construct a `DynamicDxc` variant with default paths.
783    ///
784    /// The dll must support at least shader model 6.8.
785    pub fn default_dynamic_dxc() -> Self {
786        Self::DynamicDxc {
787            dxc_path: String::from("dxcompiler.dll"),
788        }
789    }
790
791    /// Choose which DX12 shader compiler to use from the environment variable `WGPU_DX12_COMPILER`.
792    ///
793    /// Valid values, case insensitive:
794    /// - `Fxc`
795    /// - `Dxc` or `DynamicDxc`
796    /// - `StaticDxc`
797    #[must_use]
798    pub fn from_env() -> Option<Self> {
799        let env = crate::env::var("WGPU_DX12_COMPILER")?;
800        env.parse().map_err(|expected_msg| {
801            log::warn!(
802                "Unknown value `{env:?}` for `WGPU_DX12_COMPILER` environment variable. {expected_msg}"
803            )
804        })
805        .ok()
806    }
807
808    /// Takes the given compiler, modifies it based on the `WGPU_DX12_COMPILER` environment variable, and returns the result.
809    ///
810    /// See `from_env` for more information.
811    #[must_use]
812    pub fn with_env(self) -> Self {
813        if let Some(compiler) = Self::from_env() {
814            compiler
815        } else {
816            self
817        }
818    }
819}
820
821impl FromStr for Dx12Compiler {
822    type Err = &'static str;
823
824    fn from_str(value: &str) -> Result<Self, Self::Err> {
825        Ok(match value.to_lowercase().as_str() {
826            "dxc" | "dynamicdxc" => Self::default_dynamic_dxc(),
827            "staticdxc" => Self::StaticDxc,
828            "fxc" => Self::Fxc,
829            "auto" => Self::Auto,
830            _ => return Err("Expected `dynamicdxc` (alias `dxc`), `staticdxc`, `fxc`, or `auto`."),
831        })
832    }
833}
834
835/// Whether and how to use a waitable handle obtained from `GetFrameLatencyWaitableObject`.
836#[derive(Clone, Debug, Default)]
837pub enum Dx12UseFrameLatencyWaitableObject {
838    /// Do not obtain a waitable handle and do not wait for it. The swapchain will
839    /// be created without the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag.
840    None,
841    /// Obtain a waitable handle and wait for it before acquiring the next swapchain image.
842    #[default]
843    Wait,
844    /// Create the swapchain with the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag and
845    /// obtain a waitable handle, but do not wait for it before acquiring the next swapchain image.
846    /// This is useful if the application wants to wait for the waitable object itself.
847    DontWait,
848}
849
850impl Dx12UseFrameLatencyWaitableObject {
851    /// Choose whether to use a frame latency waitable object from the environment variable `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT`.
852    ///
853    /// Valid values, case insensitive:
854    /// - `None`
855    /// - `Wait`
856    /// - `DontWait`
857    #[must_use]
858    pub fn from_env() -> Option<Self> {
859        let value = crate::env::var("WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT")
860            .as_deref()?
861            .to_lowercase();
862        match value.as_str() {
863            "none" => Some(Self::None),
864            "wait" => Some(Self::Wait),
865            "dontwait" => Some(Self::DontWait),
866            _ => None,
867        }
868    }
869
870    /// Takes the given setting, modifies it based on the `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT` environment variable, and returns the result.
871    ///
872    /// See `from_env` for more information.
873    #[must_use]
874    pub fn with_env(self) -> Self {
875        if let Some(compiler) = Self::from_env() {
876            compiler
877        } else {
878            self
879        }
880    }
881}
882
883/// Selects which OpenGL ES 3 minor version to request.
884///
885/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.
886#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
887pub enum Gles3MinorVersion {
888    /// No explicit minor version is requested, the driver automatically picks the highest available.
889    #[default]
890    Automatic,
891
892    /// Request an ES 3.0 context.
893    Version0,
894
895    /// Request an ES 3.1 context.
896    Version1,
897
898    /// Request an ES 3.2 context.
899    Version2,
900}
901
902impl Gles3MinorVersion {
903    /// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GLES_MINOR_VERSION`.
904    ///
905    /// Possible values are `0`, `1`, `2` or `automatic`. Case insensitive.
906    ///
907    /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set.
908    #[must_use]
909    pub fn from_env() -> Option<Self> {
910        let value = crate::env::var("WGPU_GLES_MINOR_VERSION")
911            .as_deref()?
912            .to_lowercase();
913        match value.as_str() {
914            "automatic" => Some(Self::Automatic),
915            "0" => Some(Self::Version0),
916            "1" => Some(Self::Version1),
917            "2" => Some(Self::Version2),
918            _ => None,
919        }
920    }
921
922    /// Takes the given compiler, modifies it based on the `WGPU_GLES_MINOR_VERSION` environment variable, and returns the result.
923    ///
924    /// See `from_env` for more information.
925    #[must_use]
926    pub fn with_env(self) -> Self {
927        if let Some(compiler) = Self::from_env() {
928            compiler
929        } else {
930            self
931        }
932    }
933}
934
935/// Dictate the behavior of fences in OpenGL.
936#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
937pub enum GlFenceBehavior {
938    /// Fences in OpenGL behave normally. If you don't know what to pick, this is what you want.
939    #[default]
940    Normal,
941    /// Fences in OpenGL are short-circuited to always return `true` immediately.
942    ///
943    /// This solves a very specific issue that arose due to a bug in wgpu-core that made
944    /// many WebGL programs work when they "shouldn't" have. If you have code that is trying
945    /// to call `device.poll(wgpu::PollType::Wait)` on WebGL, you need to enable this option
946    /// for the "Wait" to behave how you would expect.
947    ///
948    /// Previously all `poll(Wait)` acted like the OpenGL fences were signalled even if they weren't.
949    /// See <https://github.com/gfx-rs/wgpu/issues/4589> for more information.
950    ///
951    /// When this is set `Queue::on_completed_work_done` will always return the next time the device
952    /// is maintained, not when the work is actually done on the GPU.
953    AutoFinish,
954}
955
956impl GlFenceBehavior {
957    /// Returns true if the fence behavior is `AutoFinish`.
958    pub fn is_auto_finish(&self) -> bool {
959        matches!(self, Self::AutoFinish)
960    }
961
962    /// Returns true if the fence behavior is `Normal`.
963    pub fn is_normal(&self) -> bool {
964        matches!(self, Self::Normal)
965    }
966
967    /// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GL_FENCE_BEHAVIOR`.
968    ///
969    /// Possible values are `Normal` or `AutoFinish`. Case insensitive.
970    ///
971    /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set.
972    #[must_use]
973    pub fn from_env() -> Option<Self> {
974        let value = crate::env::var("WGPU_GL_FENCE_BEHAVIOR")
975            .as_deref()?
976            .to_lowercase();
977        match value.as_str() {
978            "normal" => Some(Self::Normal),
979            "autofinish" => Some(Self::AutoFinish),
980            _ => None,
981        }
982    }
983
984    /// Takes the given compiler, modifies it based on the `WGPU_GL_FENCE_BEHAVIOR` environment variable, and returns the result.
985    ///
986    /// See `from_env` for more information.
987    #[must_use]
988    pub fn with_env(self) -> Self {
989        if let Some(fence) = Self::from_env() {
990            fence
991        } else {
992            self
993        }
994    }
995}