wgpu/api/
instance.rs

1use parking_lot::Mutex;
2
3use crate::{dispatch::InstanceInterface, *};
4
5use std::future::Future;
6
7bitflags::bitflags! {
8    /// WGSL language extensions.
9    ///
10    /// WGSL spec.: <https://www.w3.org/TR/WGSL/#language-extensions-sec>
11    #[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
12    pub struct WgslLanguageFeatures: u32 {
13        /// <https://www.w3.org/TR/WGSL/#language_extension-readonly_and_readwrite_storage_textures>
14        const ReadOnlyAndReadWriteStorageTextures = 1 << 0;
15        /// <https://www.w3.org/TR/WGSL/#language_extension-packed_4x8_integer_dot_product>
16        const Packed4x8IntegerDotProduct = 1 << 1;
17        /// <https://www.w3.org/TR/WGSL/#language_extension-unrestricted_pointer_parameters>
18        const UnrestrictedPointerParameters = 1 << 2;
19        /// <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>
20        const PointerCompositeAccess = 1 << 3;
21    }
22}
23
24/// Context for all other wgpu objects. Instance of wgpu.
25///
26/// This is the first thing you create when using wgpu.
27/// Its primary use is to create [`Adapter`]s and [`Surface`]s.
28///
29/// Does not have to be kept alive.
30///
31/// Corresponds to [WebGPU `GPU`](https://gpuweb.github.io/gpuweb/#gpu-interface).
32#[derive(Debug, Clone)]
33pub struct Instance {
34    inner: dispatch::DispatchInstance,
35}
36#[cfg(send_sync)]
37static_assertions::assert_impl_all!(Instance: Send, Sync);
38
39crate::cmp::impl_eq_ord_hash_proxy!(Instance => .inner);
40
41impl Default for Instance {
42    /// Creates a new instance of wgpu with default options.
43    ///
44    /// Backends are set to `Backends::all()`, and FXC is chosen as the `dx12_shader_compiler`.
45    ///
46    /// # Panics
47    ///
48    /// If no backend feature for the active target platform is enabled,
49    /// this method will panic, see [`Instance::enabled_backend_features()`].
50    fn default() -> Self {
51        Self::new(&InstanceDescriptor::default())
52    }
53}
54
55impl Instance {
56    /// Returns which backends can be picked for the current build configuration.
57    ///
58    /// The returned set depends on a combination of target platform and enabled features.
59    /// This does *not* do any runtime checks and is exclusively based on compile time information.
60    ///
61    /// `InstanceDescriptor::backends` does not need to be a subset of this,
62    /// but any backend that is not in this set, will not be picked.
63    ///
64    /// TODO: Right now it's otherwise not possible yet to opt-out of all features on some platforms.
65    /// See <https://github.com/gfx-rs/wgpu/issues/3514>
66    /// * Windows/Linux/Android: always enables Vulkan and GLES with no way to opt out
67    pub const fn enabled_backend_features() -> Backends {
68        let mut backends = Backends::empty();
69
70        if cfg!(native) {
71            if cfg!(metal) {
72                backends = backends.union(Backends::METAL);
73            }
74            if cfg!(dx12) {
75                backends = backends.union(Backends::DX12);
76            }
77
78            // Windows, Android, Linux currently always enable Vulkan and OpenGL.
79            // See <https://github.com/gfx-rs/wgpu/issues/3514>
80            if cfg!(target_os = "windows") || cfg!(unix) {
81                backends = backends.union(Backends::VULKAN).union(Backends::GL);
82            }
83
84            // Vulkan on Mac/iOS is only available through vulkan-portability.
85            if cfg!(target_vendor = "apple") && cfg!(feature = "vulkan-portability") {
86                backends = backends.union(Backends::VULKAN);
87            }
88
89            // GL on Mac is only available through angle.
90            if cfg!(target_os = "macos") && cfg!(feature = "angle") {
91                backends = backends.union(Backends::GL);
92            }
93        } else {
94            if cfg!(webgpu) {
95                backends = backends.union(Backends::BROWSER_WEBGPU);
96            }
97            if cfg!(webgl) {
98                backends = backends.union(Backends::GL);
99            }
100        }
101
102        backends
103    }
104
105    /// Create an new instance of wgpu.
106    ///
107    /// # Arguments
108    ///
109    /// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose
110    ///   during instantiation, and which [DX12 shader compiler][Dx12Compiler] wgpu will use.
111    ///
112    ///   [`Backends::BROWSER_WEBGPU`] takes a special role:
113    ///   If it is set and a [`navigator.gpu`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu)
114    ///   object is present, this instance will *only* be able to create WebGPU adapters.
115    ///
116    ///   ⚠️ On some browsers this check is insufficient to determine whether WebGPU is supported,
117    ///   as the browser may define the `navigator.gpu` object, but be unable to create any WebGPU adapters.
118    ///   For targeting _both_ WebGPU & WebGL is recommended to use [`crate::util::new_instance_with_webgpu_detection`].
119    ///
120    ///   If you instead want to force use of WebGL, either disable the `webgpu` compile-time feature
121    ///   or don't add the [`Backends::BROWSER_WEBGPU`] flag to the the `instance_desc`'s `backends` field.
122    ///   If it is set and WebGPU support is *not* detected, the instance will use wgpu-core
123    ///   to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create
124    ///   a WebGL adapter.
125    ///
126    /// # Panics
127    ///
128    /// If no backend feature for the active target platform is enabled,
129    /// this method will panic, see [`Instance::enabled_backend_features()`].
130    #[allow(clippy::allow_attributes, unreachable_code)]
131    pub fn new(_instance_desc: &InstanceDescriptor) -> Self {
132        if Self::enabled_backend_features().is_empty() {
133            panic!(
134                "No wgpu backend feature that is implemented for the target platform was enabled. \
135                 See `wgpu::Instance::enabled_backend_features()` for more information."
136            );
137        }
138
139        #[cfg(webgpu)]
140        {
141            let is_only_available_backend = !cfg!(wgpu_core);
142            let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU);
143            let support_webgpu = crate::backend::get_browser_gpu_property()
144                .map(|maybe_gpu| maybe_gpu.is_some())
145                .unwrap_or(false);
146
147            if is_only_available_backend || (requested_webgpu && support_webgpu) {
148                return Self {
149                    inner: crate::backend::ContextWebGpu::new(_instance_desc).into(),
150                };
151            }
152        }
153
154        #[cfg(wgpu_core)]
155        {
156            return Self {
157                inner: crate::backend::ContextWgpuCore::new(_instance_desc).into(),
158            };
159        }
160
161        unreachable!(
162            "Earlier check of `enabled_backend_features` should have prevented getting here!"
163        );
164    }
165
166    /// Create an new instance of wgpu from a wgpu-hal instance.
167    ///
168    /// # Arguments
169    ///
170    /// - `hal_instance` - wgpu-hal instance.
171    ///
172    /// # Safety
173    ///
174    /// Refer to the creation of wgpu-hal Instance for every backend.
175    #[cfg(wgpu_core)]
176    pub unsafe fn from_hal<A: wgc::hal_api::HalApi>(hal_instance: A::Instance) -> Self {
177        Self {
178            inner: unsafe {
179                crate::backend::ContextWgpuCore::from_hal_instance::<A>(hal_instance).into()
180            },
181        }
182    }
183
184    /// Return a reference to a specific backend instance, if available.
185    ///
186    /// If this `Instance` has a wgpu-hal [`Instance`] for backend
187    /// `A`, return a reference to it. Otherwise, return `None`.
188    ///
189    /// # Safety
190    ///
191    /// - The raw instance handle returned must not be manually destroyed.
192    ///
193    /// [`Instance`]: hal::Api::Instance
194    #[cfg(wgpu_core)]
195    pub unsafe fn as_hal<A: wgc::hal_api::HalApi>(&self) -> Option<&A::Instance> {
196        self.inner
197            .as_core_opt()
198            .and_then(|ctx| unsafe { ctx.instance_as_hal::<A>() })
199    }
200
201    /// Create an new instance of wgpu from a wgpu-core instance.
202    ///
203    /// # Arguments
204    ///
205    /// - `core_instance` - wgpu-core instance.
206    ///
207    /// # Safety
208    ///
209    /// Refer to the creation of wgpu-core Instance.
210    #[cfg(wgpu_core)]
211    pub unsafe fn from_core(core_instance: wgc::instance::Instance) -> Self {
212        Self {
213            inner: unsafe {
214                crate::backend::ContextWgpuCore::from_core_instance(core_instance).into()
215            },
216        }
217    }
218
219    /// Retrieves all available [`Adapter`]s that match the given [`Backends`].
220    ///
221    /// # Arguments
222    ///
223    /// - `backends` - Backends from which to enumerate adapters.
224    #[cfg(native)]
225    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
226        let Some(core_instance) = self.inner.as_core_opt() else {
227            return Vec::new();
228        };
229
230        core_instance
231            .enumerate_adapters(backends)
232            .into_iter()
233            .map(|adapter| {
234                let core = backend::wgpu_core::CoreAdapter {
235                    context: core_instance.clone(),
236                    id: adapter,
237                };
238                crate::Adapter { inner: core.into() }
239            })
240            .collect()
241    }
242
243    /// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`].
244    ///
245    /// Some options are "soft", so treated as non-mandatory. Others are "hard".
246    ///
247    /// If no adapters are found that suffice all the "hard" options, `None` is returned.
248    ///
249    /// A `compatible_surface` is required when targeting WebGL2.
250    pub fn request_adapter(
251        &self,
252        options: &RequestAdapterOptions<'_, '_>,
253    ) -> impl Future<Output = Option<Adapter>> + WasmNotSend {
254        let future = self.inner.request_adapter(options);
255        async move { future.await.map(|adapter| Adapter { inner: adapter }) }
256    }
257
258    /// Converts a wgpu-hal `ExposedAdapter` to a wgpu [`Adapter`].
259    ///
260    /// # Safety
261    ///
262    /// `hal_adapter` must be created from this instance internal handle.
263    #[cfg(wgpu_core)]
264    pub unsafe fn create_adapter_from_hal<A: wgc::hal_api::HalApi>(
265        &self,
266        hal_adapter: hal::ExposedAdapter<A>,
267    ) -> Adapter {
268        let core_instance = self.inner.as_core();
269        let adapter = unsafe { core_instance.create_adapter_from_hal(hal_adapter) };
270        let core = backend::wgpu_core::CoreAdapter {
271            context: core_instance.clone(),
272            id: adapter,
273        };
274
275        Adapter { inner: core.into() }
276    }
277
278    /// Creates a new surface targeting a given window/canvas/surface/etc..
279    ///
280    /// Internally, this creates surfaces for all backends that are enabled for this instance.
281    ///
282    /// See [`SurfaceTarget`] for what targets are supported.
283    /// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants.
284    ///
285    /// Most commonly used are window handles (or provider of windows handles)
286    /// which can be passed directly as they're automatically converted to [`SurfaceTarget`].
287    pub fn create_surface<'window>(
288        &self,
289        target: impl Into<SurfaceTarget<'window>>,
290    ) -> Result<Surface<'window>, CreateSurfaceError> {
291        // Handle origin (i.e. window) to optionally take ownership of to make the surface outlast the window.
292        let handle_source;
293
294        let target = target.into();
295        let mut surface = match target {
296            SurfaceTarget::Window(window) => unsafe {
297                let surface = self.create_surface_unsafe(
298                    SurfaceTargetUnsafe::from_window(&window).map_err(|e| CreateSurfaceError {
299                        inner: CreateSurfaceErrorKind::RawHandle(e),
300                    })?,
301                );
302                handle_source = Some(window);
303
304                surface
305            }?,
306
307            #[cfg(any(webgpu, webgl))]
308            SurfaceTarget::Canvas(canvas) => {
309                handle_source = None;
310
311                let value: &wasm_bindgen::JsValue = &canvas;
312                let obj = std::ptr::NonNull::from(value).cast();
313                let raw_window_handle = raw_window_handle::WebCanvasWindowHandle::new(obj).into();
314                let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
315
316                // Note that we need to call this while we still have `value` around.
317                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
318                unsafe {
319                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
320                        raw_display_handle,
321                        raw_window_handle,
322                    })
323                }?
324            }
325
326            #[cfg(any(webgpu, webgl))]
327            SurfaceTarget::OffscreenCanvas(canvas) => {
328                handle_source = None;
329
330                let value: &wasm_bindgen::JsValue = &canvas;
331                let obj = std::ptr::NonNull::from(value).cast();
332                let raw_window_handle =
333                    raw_window_handle::WebOffscreenCanvasWindowHandle::new(obj).into();
334                let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
335
336                // Note that we need to call this while we still have `value` around.
337                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
338                unsafe {
339                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
340                        raw_display_handle,
341                        raw_window_handle,
342                    })
343                }?
344            }
345        };
346
347        surface._handle_source = handle_source;
348
349        Ok(surface)
350    }
351
352    /// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target.
353    ///
354    /// Internally, this creates surfaces for all backends that are enabled for this instance.
355    ///
356    /// See [`SurfaceTargetUnsafe`] for what targets are supported.
357    /// See [`Instance::create_surface`] for surface creation with safe target variants.
358    ///
359    /// # Safety
360    ///
361    /// - See respective [`SurfaceTargetUnsafe`] variants for safety requirements.
362    pub unsafe fn create_surface_unsafe<'window>(
363        &self,
364        target: SurfaceTargetUnsafe,
365    ) -> Result<Surface<'window>, CreateSurfaceError> {
366        let surface = unsafe { self.inner.create_surface(target)? };
367
368        Ok(Surface {
369            _handle_source: None,
370            inner: surface,
371            config: Mutex::new(None),
372        })
373    }
374
375    /// Polls all devices.
376    ///
377    /// If `force_wait` is true and this is not running on the web, then this
378    /// function will block until all in-flight buffers have been mapped and
379    /// all submitted commands have finished execution.
380    ///
381    /// Return `true` if all devices' queues are empty, or `false` if there are
382    /// queue submissions still in flight. (Note that, unless access to all
383    /// [`Queue`s] associated with this [`Instance`] is coordinated somehow,
384    /// this information could be out of date by the time the caller receives
385    /// it. `Queue`s can be shared between threads, and other threads could
386    /// submit new work at any time.)
387    ///
388    /// On the web, this is a no-op. `Device`s are automatically polled.
389    ///
390    /// [`Queue`s]: Queue
391    pub fn poll_all(&self, force_wait: bool) -> bool {
392        self.inner.poll_all_devices(force_wait)
393    }
394
395    /// Generates memory report.
396    ///
397    /// Returns `None` if the feature is not supported by the backend
398    /// which happens only when WebGPU is pre-selected by the instance creation.
399    #[cfg(wgpu_core)]
400    pub fn generate_report(&self) -> Option<wgc::global::GlobalReport> {
401        self.inner.as_core_opt().map(|ctx| ctx.generate_report())
402    }
403
404    /// Returns set of supported WGSL language extensions supported by this instance.
405    ///
406    /// <https://www.w3.org/TR/webgpu/#gpuwgsllanguagefeatures>
407    #[cfg(feature = "wgsl")]
408    pub fn wgsl_language_features(&self) -> WgslLanguageFeatures {
409        self.inner.wgsl_language_features()
410    }
411}