wgpu_types/
backend.rs

1//! [`Backend`], [`Backends`], and backend-specific options.
2
3use alloc::string::String;
4use core::hash::Hash;
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}
256
257impl GlBackendOptions {
258    /// Choose OpenGL backend options by calling `from_env` on every field.
259    ///
260    /// See those methods for more information.
261    #[must_use]
262    pub fn from_env_or_default() -> Self {
263        let gles_minor_version = Gles3MinorVersion::from_env().unwrap_or_default();
264        Self {
265            gles_minor_version,
266            fence_behavior: GlFenceBehavior::Normal,
267        }
268    }
269
270    /// Takes the given options, modifies them based on the environment variables, and returns the result.
271    ///
272    /// This is equivalent to calling `with_env` on every field.
273    #[must_use]
274    pub fn with_env(self) -> Self {
275        let gles_minor_version = self.gles_minor_version.with_env();
276        let short_circuit_fences = self.fence_behavior.with_env();
277        Self {
278            gles_minor_version,
279            fence_behavior: short_circuit_fences,
280        }
281    }
282}
283
284/// Configuration for the DX12 backend.
285///
286/// Part of [`BackendOptions`].
287#[derive(Clone, Debug, Default)]
288pub struct Dx12BackendOptions {
289    /// Which DX12 shader compiler to use.
290    pub shader_compiler: Dx12Compiler,
291    /// Presentation system to use.
292    pub presentation_system: Dx12SwapchainKind,
293    /// Whether to wait for the latency waitable object before acquiring the next swapchain image.
294    pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject,
295}
296
297impl Dx12BackendOptions {
298    /// Choose DX12 backend options by calling `from_env` on every field.
299    ///
300    /// See those methods for more information.
301    #[must_use]
302    pub fn from_env_or_default() -> Self {
303        let compiler = Dx12Compiler::from_env().unwrap_or_default();
304        let presentation_system = Dx12SwapchainKind::from_env().unwrap_or_default();
305        let latency_waitable_object =
306            Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
307        Self {
308            shader_compiler: compiler,
309            presentation_system,
310            latency_waitable_object,
311        }
312    }
313
314    /// Takes the given options, modifies them based on the environment variables, and returns the result.
315    ///
316    /// This is equivalent to calling `with_env` on every field.
317    #[must_use]
318    pub fn with_env(self) -> Self {
319        let shader_compiler = self.shader_compiler.with_env();
320        let presentation_system = self.presentation_system.with_env();
321        let latency_waitable_object = self.latency_waitable_object.with_env();
322        Self {
323            shader_compiler,
324            presentation_system,
325            latency_waitable_object,
326        }
327    }
328}
329
330/// Configuration for the noop backend.
331///
332/// Part of [`BackendOptions`].
333#[derive(Clone, Debug, Default)]
334pub struct NoopBackendOptions {
335    /// Whether to allow the noop backend to be used.
336    ///
337    /// The noop backend stubs out all operations except for buffer creation and mapping, so
338    /// it must not be used when not expected. Therefore, it will not be used unless explicitly
339    /// enabled.
340    pub enable: bool,
341}
342
343impl NoopBackendOptions {
344    /// Choose whether the noop backend is enabled from the environment.
345    ///
346    /// It will be enabled if the environment variable `WGPU_NOOP_BACKEND` has the value `1`
347    /// and not otherwise. Future versions may assign other meanings to other values.
348    #[must_use]
349    pub fn from_env_or_default() -> Self {
350        Self {
351            enable: Self::enable_from_env().unwrap_or(false),
352        }
353    }
354
355    /// Takes the given options, modifies them based on the environment variables, and returns the
356    /// result.
357    ///
358    /// See [`from_env_or_default()`](Self::from_env_or_default) for the interpretation.
359    #[must_use]
360    pub fn with_env(self) -> Self {
361        Self {
362            enable: Self::enable_from_env().unwrap_or(self.enable),
363        }
364    }
365
366    fn enable_from_env() -> Option<bool> {
367        let value = crate::env::var("WGPU_NOOP_BACKEND")?;
368        match value.as_str() {
369            "1" => Some(true),
370            "0" => Some(false),
371            _ => None,
372        }
373    }
374}
375
376#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)]
377/// Selects which kind of swapchain to use on DX12.
378pub enum Dx12SwapchainKind {
379    /// Use a DXGI swapchain made directly from the window's HWND.
380    ///
381    /// This does not support transparency but has better support from developer tooling from RenderDoc.
382    #[default]
383    DxgiFromHwnd,
384    /// Use a DXGI swapchain made from a DirectComposition visual made automatically from the window's HWND.
385    ///
386    /// This creates a single [`IDCompositionVisual`] over the entire window that is used by the `Surface`.
387    /// If a user wants to manage the composition tree themselves, they should create their own device and
388    /// composition, and pass the relevant visual down via [`SurfaceTargetUnsafe::CompositionVisual`][CV].
389    ///
390    /// This supports transparent windows, but does not have support from RenderDoc.
391    ///
392    /// [`IDCompositionVisual`]: https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nn-dcomp-idcompositionvisual
393    #[doc = link_to_wgpu_docs!(["CV"]: "struct.SurfaceTargetUnsafe.html#variant.CompositionVisual")]
394    DxgiFromVisual,
395}
396
397impl Dx12SwapchainKind {
398    /// Choose which presentation system to use from the environment variable `WGPU_DX12_PRESENTATION_SYSTEM`.
399    ///
400    /// Valid values, case insensitive:
401    /// - `DxgiFromVisual` or `Visual`
402    /// - `DxgiFromHwnd` or `Hwnd` for [`Self::DxgiFromHwnd`]
403    #[must_use]
404    pub fn from_env() -> Option<Self> {
405        let value = crate::env::var("WGPU_DX12_PRESENTATION_SYSTEM")
406            .as_deref()?
407            .to_lowercase();
408        match value.as_str() {
409            "dxgifromvisual" | "visual" => Some(Self::DxgiFromVisual),
410            "dxgifromhwnd" | "hwnd" => Some(Self::DxgiFromHwnd),
411            _ => None,
412        }
413    }
414
415    /// Takes the given presentation system, modifies it based on the `WGPU_DX12_PRESENTATION_SYSTEM` environment variable, and returns the result.
416    ///
417    /// See [`from_env`](Self::from_env) for more information.
418    #[must_use]
419    pub fn with_env(self) -> Self {
420        if let Some(presentation_system) = Self::from_env() {
421            presentation_system
422        } else {
423            self
424        }
425    }
426}
427
428/// DXC shader model.
429#[derive(Clone, Debug)]
430#[allow(missing_docs)]
431pub enum DxcShaderModel {
432    V6_0,
433    V6_1,
434    V6_2,
435    V6_3,
436    V6_4,
437    V6_5,
438    V6_6,
439    V6_7,
440}
441
442/// Selects which DX12 shader compiler to use.
443#[derive(Clone, Debug, Default)]
444pub enum Dx12Compiler {
445    /// The Fxc compiler (default) is old, slow and unmaintained.
446    ///
447    /// However, it doesn't require any additional .dlls to be shipped with the application.
448    #[default]
449    Fxc,
450    /// The Dxc compiler is new, fast and maintained.
451    ///
452    /// However, it requires `dxcompiler.dll` to be shipped with the application.
453    /// These files can be downloaded from <https://github.com/microsoft/DirectXShaderCompiler/releases>.
454    ///
455    /// Minimum supported version: [v1.8.2502](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.8.2502)
456    ///
457    /// It also requires WDDM 2.1 (Windows 10 version 1607).
458    DynamicDxc {
459        /// Path to `dxcompiler.dll`.
460        dxc_path: String,
461        /// Maximum shader model the given dll supports.
462        max_shader_model: DxcShaderModel,
463    },
464    /// The statically-linked variant of Dxc.
465    ///
466    /// The `static-dxc` feature is required for this setting to be used successfully on DX12.
467    /// Not available on `windows-aarch64-pc-*` targets.
468    StaticDxc,
469}
470
471impl Dx12Compiler {
472    /// Helper function to construct a `DynamicDxc` variant with default paths.
473    ///
474    /// The dll must support at least shader model 6.8.
475    pub fn default_dynamic_dxc() -> Self {
476        Self::DynamicDxc {
477            dxc_path: String::from("dxcompiler.dll"),
478            max_shader_model: DxcShaderModel::V6_7, // should be 6.8 but the variant is missing
479        }
480    }
481
482    /// Choose which DX12 shader compiler to use from the environment variable `WGPU_DX12_COMPILER`.
483    ///
484    /// Valid values, case insensitive:
485    /// - `Fxc`
486    /// - `Dxc` or `DynamicDxc`
487    /// - `StaticDxc`
488    #[must_use]
489    pub fn from_env() -> Option<Self> {
490        let value = crate::env::var("WGPU_DX12_COMPILER")
491            .as_deref()?
492            .to_lowercase();
493        match value.as_str() {
494            "dxc" | "dynamicdxc" => Some(Self::default_dynamic_dxc()),
495            "staticdxc" => Some(Self::StaticDxc),
496            "fxc" => Some(Self::Fxc),
497            _ => None,
498        }
499    }
500
501    /// Takes the given compiler, modifies it based on the `WGPU_DX12_COMPILER` environment variable, and returns the result.
502    ///
503    /// See `from_env` for more information.
504    #[must_use]
505    pub fn with_env(self) -> Self {
506        if let Some(compiler) = Self::from_env() {
507            compiler
508        } else {
509            self
510        }
511    }
512}
513
514/// Whether and how to use a waitable handle obtained from `GetFrameLatencyWaitableObject`.
515#[derive(Clone, Debug, Default)]
516pub enum Dx12UseFrameLatencyWaitableObject {
517    /// Do not obtain a waitable handle and do not wait for it. The swapchain will
518    /// be created without the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag.
519    None,
520    /// Obtain a waitable handle and wait for it before acquiring the next swapchain image.
521    #[default]
522    Wait,
523    /// Create the swapchain with the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag and
524    /// obtain a waitable handle, but do not wait for it before acquiring the next swapchain image.
525    /// This is useful if the application wants to wait for the waitable object itself.
526    DontWait,
527}
528
529impl Dx12UseFrameLatencyWaitableObject {
530    /// Choose whether to use a frame latency waitable object from the environment variable `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT`.
531    ///
532    /// Valid values, case insensitive:
533    /// - `None`
534    /// - `Wait`
535    /// - `DontWait`
536    #[must_use]
537    pub fn from_env() -> Option<Self> {
538        let value = crate::env::var("WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT")
539            .as_deref()?
540            .to_lowercase();
541        match value.as_str() {
542            "none" => Some(Self::None),
543            "wait" => Some(Self::Wait),
544            "dontwait" => Some(Self::DontWait),
545            _ => None,
546        }
547    }
548
549    /// Takes the given setting, modifies it based on the `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT` environment variable, and returns the result.
550    ///
551    /// See `from_env` for more information.
552    #[must_use]
553    pub fn with_env(self) -> Self {
554        if let Some(compiler) = Self::from_env() {
555            compiler
556        } else {
557            self
558        }
559    }
560}
561
562/// Selects which OpenGL ES 3 minor version to request.
563///
564/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.
565#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
566pub enum Gles3MinorVersion {
567    /// No explicit minor version is requested, the driver automatically picks the highest available.
568    #[default]
569    Automatic,
570
571    /// Request an ES 3.0 context.
572    Version0,
573
574    /// Request an ES 3.1 context.
575    Version1,
576
577    /// Request an ES 3.2 context.
578    Version2,
579}
580
581impl Gles3MinorVersion {
582    /// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GLES_MINOR_VERSION`.
583    ///
584    /// Possible values are `0`, `1`, `2` or `automatic`. Case insensitive.
585    ///
586    /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set.
587    #[must_use]
588    pub fn from_env() -> Option<Self> {
589        let value = crate::env::var("WGPU_GLES_MINOR_VERSION")
590            .as_deref()?
591            .to_lowercase();
592        match value.as_str() {
593            "automatic" => Some(Self::Automatic),
594            "0" => Some(Self::Version0),
595            "1" => Some(Self::Version1),
596            "2" => Some(Self::Version2),
597            _ => None,
598        }
599    }
600
601    /// Takes the given compiler, modifies it based on the `WGPU_GLES_MINOR_VERSION` environment variable, and returns the result.
602    ///
603    /// See `from_env` for more information.
604    #[must_use]
605    pub fn with_env(self) -> Self {
606        if let Some(compiler) = Self::from_env() {
607            compiler
608        } else {
609            self
610        }
611    }
612}
613
614/// Dictate the behavior of fences in OpenGL.
615#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
616pub enum GlFenceBehavior {
617    /// Fences in OpenGL behave normally. If you don't know what to pick, this is what you want.
618    #[default]
619    Normal,
620    /// Fences in OpenGL are short-circuited to always return `true` immediately.
621    ///
622    /// This solves a very specific issue that arose due to a bug in wgpu-core that made
623    /// many WebGL programs work when they "shouldn't" have. If you have code that is trying
624    /// to call `device.poll(wgpu::PollType::Wait)` on WebGL, you need to enable this option
625    /// for the "Wait" to behave how you would expect.
626    ///
627    /// Previously all `poll(Wait)` acted like the OpenGL fences were signalled even if they weren't.
628    /// See <https://github.com/gfx-rs/wgpu/issues/4589> for more information.
629    ///
630    /// When this is set `Queue::on_completed_work_done` will always return the next time the device
631    /// is maintained, not when the work is actually done on the GPU.
632    AutoFinish,
633}
634
635impl GlFenceBehavior {
636    /// Returns true if the fence behavior is `AutoFinish`.
637    pub fn is_auto_finish(&self) -> bool {
638        matches!(self, Self::AutoFinish)
639    }
640
641    /// Returns true if the fence behavior is `Normal`.
642    pub fn is_normal(&self) -> bool {
643        matches!(self, Self::Normal)
644    }
645
646    /// Choose which minor OpenGL ES version to use from the environment variable `WGPU_GL_FENCE_BEHAVIOR`.
647    ///
648    /// Possible values are `Normal` or `AutoFinish`. Case insensitive.
649    ///
650    /// Use with `unwrap_or_default()` to get the default value if the environment variable is not set.
651    #[must_use]
652    pub fn from_env() -> Option<Self> {
653        let value = crate::env::var("WGPU_GL_FENCE_BEHAVIOR")
654            .as_deref()?
655            .to_lowercase();
656        match value.as_str() {
657            "normal" => Some(Self::Normal),
658            "autofinish" => Some(Self::AutoFinish),
659            _ => None,
660        }
661    }
662
663    /// Takes the given compiler, modifies it based on the `WGPU_GL_FENCE_BEHAVIOR` environment variable, and returns the result.
664    ///
665    /// See `from_env` for more information.
666    #[must_use]
667    pub fn with_env(self) -> Self {
668        if let Some(fence) = Self::from_env() {
669            fence
670        } else {
671            self
672        }
673    }
674}