egui_wgpu/
setup.rs

1use std::sync::Arc;
2
3#[derive(Clone)]
4pub enum WgpuSetup {
5    /// Construct a wgpu setup using some predefined settings & heuristics.
6    /// This is the default option. You can customize most behaviours overriding the
7    /// supported backends, power preferences, and device description.
8    ///
9    /// By default can also be configured with various environment variables:
10    /// * `WGPU_BACKEND`: `vulkan`, `dx12`, `metal`, `opengl`, `webgpu`
11    /// * `WGPU_POWER_PREF`: `low`, `high` or `none`
12    /// * `WGPU_TRACE`: Path to a file to output a wgpu trace file.
13    ///
14    /// Each instance flag also comes with an environment variable (for details see [`wgpu::InstanceFlags`]):
15    /// * `WGPU_VALIDATION`: Enables validation (enabled by default in debug builds).
16    /// * `WGPU_DEBUG`: Generate debug information in shaders and objects  (enabled by default in debug builds).
17    /// * `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER`: Whether wgpu should expose adapters that run on top of non-compliant adapters.
18    /// * `WGPU_GPU_BASED_VALIDATION`: Enable GPU-based validation.
19    CreateNew(WgpuSetupCreateNew),
20
21    /// Run on an existing wgpu setup.
22    Existing(WgpuSetupExisting),
23}
24
25impl Default for WgpuSetup {
26    fn default() -> Self {
27        Self::CreateNew(WgpuSetupCreateNew::default())
28    }
29}
30
31impl std::fmt::Debug for WgpuSetup {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::CreateNew(create_new) => f
35                .debug_tuple("WgpuSetup::CreateNew")
36                .field(create_new)
37                .finish(),
38            Self::Existing { .. } => f.debug_tuple("WgpuSetup::Existing").finish(),
39        }
40    }
41}
42
43impl WgpuSetup {
44    /// Creates a new [`wgpu::Instance`] or clones the existing one.
45    ///
46    /// Does *not* store the wgpu instance, so calling this repeatedly may
47    /// create a new instance every time!
48    pub async fn new_instance(&self) -> wgpu::Instance {
49        match self {
50            Self::CreateNew(create_new) => {
51                #[allow(unused_mut)]
52                let mut backends = create_new.instance_descriptor.backends;
53
54                // Don't try WebGPU if we're not in a secure context.
55                #[cfg(target_arch = "wasm32")]
56                if backends.contains(wgpu::Backends::BROWSER_WEBGPU) {
57                    let is_secure_context =
58                        wgpu::web_sys::window().is_some_and(|w| w.is_secure_context());
59                    if !is_secure_context {
60                        log::info!(
61                            "WebGPU is only available in secure contexts, i.e. on HTTPS and on localhost."
62                        );
63                        backends.remove(wgpu::Backends::BROWSER_WEBGPU);
64                    }
65                }
66
67                log::debug!("Creating wgpu instance with backends {:?}", backends);
68                wgpu::util::new_instance_with_webgpu_detection(&create_new.instance_descriptor)
69                    .await
70            }
71            Self::Existing(existing) => existing.instance.clone(),
72        }
73    }
74}
75
76impl From<WgpuSetupCreateNew> for WgpuSetup {
77    fn from(create_new: WgpuSetupCreateNew) -> Self {
78        Self::CreateNew(create_new)
79    }
80}
81
82impl From<WgpuSetupExisting> for WgpuSetup {
83    fn from(existing: WgpuSetupExisting) -> Self {
84        Self::Existing(existing)
85    }
86}
87
88/// Method for selecting an adapter on native.
89///
90/// This can be used for fully custom adapter selection.
91/// If available, `wgpu::Surface` is passed to allow checking for surface compatibility.
92pub type NativeAdapterSelectorMethod = Arc<
93    dyn Fn(&[wgpu::Adapter], Option<&wgpu::Surface<'_>>) -> Result<wgpu::Adapter, String>
94        + Send
95        + Sync,
96>;
97
98/// Configuration for creating a new wgpu setup.
99///
100/// Used for [`WgpuSetup::CreateNew`].
101pub struct WgpuSetupCreateNew {
102    /// Instance descriptor for creating a wgpu instance.
103    ///
104    /// The most important field is [`wgpu::InstanceDescriptor::backends`], which
105    /// controls which backends are supported (wgpu will pick one of these).
106    /// If you only want to support WebGL (and not WebGPU),
107    /// you can set this to [`wgpu::Backends::GL`].
108    /// By default on web, WebGPU will be used if available.
109    /// WebGL will only be used as a fallback,
110    /// and only if you have enabled the `webgl` feature of crate `wgpu`.
111    pub instance_descriptor: wgpu::InstanceDescriptor,
112
113    /// Power preference for the adapter if [`Self::native_adapter_selector`] is not set or targeting web.
114    pub power_preference: wgpu::PowerPreference,
115
116    /// Optional selector for native adapters.
117    ///
118    /// This field has no effect when targeting web!
119    /// Otherwise, if set [`Self::power_preference`] is ignored and the adapter is instead selected by this method.
120    /// Note that [`Self::instance_descriptor`]'s [`wgpu::InstanceDescriptor::backends`]
121    /// are still used to filter the adapter enumeration in the first place.
122    ///
123    /// Defaults to `None`.
124    pub native_adapter_selector: Option<NativeAdapterSelectorMethod>,
125
126    /// Configuration passed on device request, given an adapter
127    pub device_descriptor:
128        Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> + Send + Sync>,
129
130    /// Option path to output a wgpu trace file.
131    ///
132    /// This only works if this feature is enabled in `wgpu-core`.
133    /// Does not work when running with WebGPU.
134    /// Defaults to the path set in the `WGPU_TRACE` environment variable.
135    pub trace_path: Option<std::path::PathBuf>,
136}
137
138impl Clone for WgpuSetupCreateNew {
139    fn clone(&self) -> Self {
140        Self {
141            instance_descriptor: self.instance_descriptor.clone(),
142            power_preference: self.power_preference,
143            native_adapter_selector: self.native_adapter_selector.clone(),
144            device_descriptor: self.device_descriptor.clone(),
145            trace_path: self.trace_path.clone(),
146        }
147    }
148}
149
150impl std::fmt::Debug for WgpuSetupCreateNew {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        f.debug_struct("WgpuSetupCreateNew")
153            .field("instance_descriptor", &self.instance_descriptor)
154            .field("power_preference", &self.power_preference)
155            .field(
156                "native_adapter_selector",
157                &self.native_adapter_selector.is_some(),
158            )
159            .field("trace_path", &self.trace_path)
160            .finish()
161    }
162}
163
164impl Default for WgpuSetupCreateNew {
165    fn default() -> Self {
166        Self {
167            instance_descriptor: wgpu::InstanceDescriptor {
168                // Add GL backend, primarily because WebGPU is not stable enough yet.
169                // (note however, that the GL backend needs to be opted-in via the wgpu feature flag "webgl")
170                backends: wgpu::Backends::from_env()
171                    .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),
172                flags: wgpu::InstanceFlags::from_build_config().with_env(),
173                backend_options: wgpu::BackendOptions::from_env_or_default(),
174            },
175
176            power_preference: wgpu::PowerPreference::from_env()
177                .unwrap_or(wgpu::PowerPreference::HighPerformance),
178
179            native_adapter_selector: None,
180
181            device_descriptor: Arc::new(|adapter| {
182                let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl {
183                    wgpu::Limits::downlevel_webgl2_defaults()
184                } else {
185                    wgpu::Limits::default()
186                };
187
188                wgpu::DeviceDescriptor {
189                    label: Some("egui wgpu device"),
190                    required_features: wgpu::Features::default(),
191                    required_limits: wgpu::Limits {
192                        // When using a depth buffer, we have to be able to create a texture
193                        // large enough for the entire surface, and we want to support 4k+ displays.
194                        max_texture_dimension_2d: 8192,
195                        ..base_limits
196                    },
197                    memory_hints: wgpu::MemoryHints::default(),
198                }
199            }),
200
201            trace_path: std::env::var("WGPU_TRACE")
202                .ok()
203                .map(std::path::PathBuf::from),
204        }
205    }
206}
207
208/// Configuration for using an existing wgpu setup.
209///
210/// Used for [`WgpuSetup::Existing`].
211#[derive(Clone)]
212pub struct WgpuSetupExisting {
213    pub instance: wgpu::Instance,
214    pub adapter: wgpu::Adapter,
215    pub device: wgpu::Device,
216    pub queue: wgpu::Queue,
217}