wgpu/api/
instance.rs

1#[cfg(wgpu_core)]
2use alloc::vec::Vec;
3use core::future::Future;
4
5use crate::{dispatch::InstanceInterface, util::Mutex, *};
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    pub const fn enabled_backend_features() -> Backends {
64        let mut backends = Backends::empty();
65        // `.set` and `|=` don't work in a `const` context.
66        if cfg!(noop) {
67            backends = backends.union(Backends::NOOP);
68        }
69        if cfg!(vulkan) {
70            backends = backends.union(Backends::VULKAN);
71        }
72        if cfg!(any(gles, webgl)) {
73            backends = backends.union(Backends::GL);
74        }
75        if cfg!(metal) {
76            backends = backends.union(Backends::METAL);
77        }
78        if cfg!(dx12) {
79            backends = backends.union(Backends::DX12);
80        }
81        if cfg!(webgpu) {
82            backends = backends.union(Backends::BROWSER_WEBGPU);
83        }
84        backends
85    }
86
87    /// Create an new instance of wgpu.
88    ///
89    /// # Arguments
90    ///
91    /// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose
92    ///   during instantiation, and which [DX12 shader compiler][Dx12Compiler] wgpu will use.
93    ///
94    ///   [`Backends::BROWSER_WEBGPU`] takes a special role:
95    ///   If it is set and a [`navigator.gpu`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu)
96    ///   object is present, this instance will *only* be able to create WebGPU adapters.
97    ///
98    ///   ⚠️ On some browsers this check is insufficient to determine whether WebGPU is supported,
99    ///   as the browser may define the `navigator.gpu` object, but be unable to create any WebGPU adapters.
100    ///   For targeting _both_ WebGPU & WebGL is recommended to use [`crate::util::new_instance_with_webgpu_detection`].
101    ///
102    ///   If you instead want to force use of WebGL, either disable the `webgpu` compile-time feature
103    ///   or don't add the [`Backends::BROWSER_WEBGPU`] flag to the the `instance_desc`'s `backends` field.
104    ///   If it is set and WebGPU support is *not* detected, the instance will use wgpu-core
105    ///   to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create
106    ///   a WebGL adapter.
107    ///
108    /// # Panics
109    ///
110    /// If no backend feature for the active target platform is enabled,
111    /// this method will panic, see [`Instance::enabled_backend_features()`].
112    #[allow(clippy::allow_attributes, unreachable_code)]
113    pub fn new(_instance_desc: &InstanceDescriptor) -> Self {
114        if Self::enabled_backend_features().is_empty() {
115            panic!(
116                "No wgpu backend feature that is implemented for the target platform was enabled. \
117                 See `wgpu::Instance::enabled_backend_features()` for more information."
118            );
119        }
120
121        #[cfg(webgpu)]
122        {
123            let is_only_available_backend = !cfg!(wgpu_core);
124            let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU);
125            let support_webgpu = crate::backend::get_browser_gpu_property()
126                .map(|maybe_gpu| maybe_gpu.is_some())
127                .unwrap_or(false);
128
129            if is_only_available_backend || (requested_webgpu && support_webgpu) {
130                return Self {
131                    inner: crate::backend::ContextWebGpu::new(_instance_desc).into(),
132                };
133            }
134        }
135
136        #[cfg(wgpu_core)]
137        {
138            return Self {
139                inner: crate::backend::ContextWgpuCore::new(_instance_desc).into(),
140            };
141        }
142
143        unreachable!(
144            "Earlier check of `enabled_backend_features` should have prevented getting here!"
145        );
146    }
147
148    /// Create an new instance of wgpu from a wgpu-hal instance.
149    ///
150    /// # Arguments
151    ///
152    /// - `hal_instance` - wgpu-hal instance.
153    ///
154    /// # Safety
155    ///
156    /// Refer to the creation of wgpu-hal Instance for every backend.
157    #[cfg(wgpu_core)]
158    pub unsafe fn from_hal<A: wgc::hal_api::HalApi>(hal_instance: A::Instance) -> Self {
159        Self {
160            inner: unsafe {
161                crate::backend::ContextWgpuCore::from_hal_instance::<A>(hal_instance).into()
162            },
163        }
164    }
165
166    /// Get the [`wgpu_hal`] instance from this `Instance`.
167    ///
168    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],
169    /// and pass that struct to the to the `A` type parameter.
170    ///
171    /// Returns a guard that dereferences to the type of the hal backend
172    /// which implements [`A::Instance`].
173    ///
174    /// # Errors
175    ///
176    /// This method will return None if:
177    /// - The instance is not from the backend specified by `A`.
178    /// - The instance is from the `webgpu` or `custom` backend.
179    ///
180    /// # Safety
181    ///
182    /// - The returned resource must not be destroyed unless the guard
183    ///   is the last reference to it and it is not in use by the GPU.
184    ///   The guard and handle may be dropped at any time however.
185    /// - All the safety requirements of wgpu-hal must be upheld.
186    ///
187    /// [`A::Instance`]: hal::Api::Instance
188    #[cfg(wgpu_core)]
189    pub unsafe fn as_hal<A: wgc::hal_api::HalApi>(&self) -> Option<&A::Instance> {
190        self.inner
191            .as_core_opt()
192            .and_then(|ctx| unsafe { ctx.instance_as_hal::<A>() })
193    }
194
195    /// Create an new instance of wgpu from a wgpu-core instance.
196    ///
197    /// # Arguments
198    ///
199    /// - `core_instance` - wgpu-core instance.
200    ///
201    /// # Safety
202    ///
203    /// Refer to the creation of wgpu-core Instance.
204    #[cfg(wgpu_core)]
205    pub unsafe fn from_core(core_instance: wgc::instance::Instance) -> Self {
206        Self {
207            inner: unsafe {
208                crate::backend::ContextWgpuCore::from_core_instance(core_instance).into()
209            },
210        }
211    }
212
213    #[cfg(custom)]
214    /// Creates instance from custom context implementation
215    pub fn from_custom<T: InstanceInterface>(instance: T) -> Self {
216        Self {
217            inner: dispatch::DispatchInstance::Custom(backend::custom::DynContext::new(instance)),
218        }
219    }
220
221    #[cfg(custom)]
222    /// Returns custom implementation of Instance (if custom backend and is internally T)
223    pub fn as_custom<T: custom::InstanceInterface>(&self) -> Option<&T> {
224        self.inner.as_custom()
225    }
226
227    /// Retrieves all available [`Adapter`]s that match the given [`Backends`].
228    ///
229    /// # Arguments
230    ///
231    /// - `backends` - Backends from which to enumerate adapters.
232    #[cfg(wgpu_core)]
233    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
234        let Some(core_instance) = self.inner.as_core_opt() else {
235            return Vec::new();
236        };
237
238        core_instance
239            .enumerate_adapters(backends)
240            .into_iter()
241            .map(|adapter| {
242                let core = backend::wgpu_core::CoreAdapter {
243                    context: core_instance.clone(),
244                    id: adapter,
245                };
246                crate::Adapter { inner: core.into() }
247            })
248            .collect()
249    }
250
251    /// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`].
252    ///
253    /// Some options are "soft", so treated as non-mandatory. Others are "hard".
254    ///
255    /// If no adapters are found that satisfy all the "hard" options, an error is returned.
256    ///
257    /// When targeting WebGL2, a [`compatible_surface`](RequestAdapterOptions::compatible_surface)
258    /// must be specified; using `RequestAdapterOptions::default()` will not succeed.
259    pub fn request_adapter(
260        &self,
261        options: &RequestAdapterOptions<'_, '_>,
262    ) -> impl Future<Output = Result<Adapter, RequestAdapterError>> + WasmNotSend {
263        let future = self.inner.request_adapter(options);
264        async move { future.await.map(|adapter| Adapter { inner: adapter }) }
265    }
266
267    /// Converts a wgpu-hal `ExposedAdapter` to a wgpu [`Adapter`].
268    ///
269    /// # Safety
270    ///
271    /// `hal_adapter` must be created from this instance internal handle.
272    #[cfg(wgpu_core)]
273    pub unsafe fn create_adapter_from_hal<A: wgc::hal_api::HalApi>(
274        &self,
275        hal_adapter: hal::ExposedAdapter<A>,
276    ) -> Adapter {
277        let core_instance = self.inner.as_core();
278        let adapter = unsafe { core_instance.create_adapter_from_hal(hal_adapter) };
279        let core = backend::wgpu_core::CoreAdapter {
280            context: core_instance.clone(),
281            id: adapter,
282        };
283
284        Adapter { inner: core.into() }
285    }
286
287    /// Creates a new surface targeting a given window/canvas/surface/etc..
288    ///
289    /// Internally, this creates surfaces for all backends that are enabled for this instance.
290    ///
291    /// See [`SurfaceTarget`] for what targets are supported.
292    /// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants.
293    ///
294    /// Most commonly used are window handles (or provider of windows handles)
295    /// which can be passed directly as they're automatically converted to [`SurfaceTarget`].
296    pub fn create_surface<'window>(
297        &self,
298        target: impl Into<SurfaceTarget<'window>>,
299    ) -> Result<Surface<'window>, CreateSurfaceError> {
300        // Handle origin (i.e. window) to optionally take ownership of to make the surface outlast the window.
301        let handle_source;
302
303        let target = target.into();
304        let mut surface = match target {
305            SurfaceTarget::Window(window) => unsafe {
306                let surface = self.create_surface_unsafe(
307                    SurfaceTargetUnsafe::from_window(&window).map_err(|e| CreateSurfaceError {
308                        inner: CreateSurfaceErrorKind::RawHandle(e),
309                    })?,
310                );
311                handle_source = Some(window);
312
313                surface
314            }?,
315
316            #[cfg(web)]
317            SurfaceTarget::Canvas(canvas) => {
318                handle_source = None;
319
320                let value: &wasm_bindgen::JsValue = &canvas;
321                let obj = core::ptr::NonNull::from(value).cast();
322                let raw_window_handle = raw_window_handle::WebCanvasWindowHandle::new(obj).into();
323                let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
324
325                // Note that we need to call this while we still have `value` around.
326                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
327                unsafe {
328                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
329                        raw_display_handle,
330                        raw_window_handle,
331                    })
332                }?
333            }
334
335            #[cfg(web)]
336            SurfaceTarget::OffscreenCanvas(canvas) => {
337                handle_source = None;
338
339                let value: &wasm_bindgen::JsValue = &canvas;
340                let obj = core::ptr::NonNull::from(value).cast();
341                let raw_window_handle =
342                    raw_window_handle::WebOffscreenCanvasWindowHandle::new(obj).into();
343                let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
344
345                // Note that we need to call this while we still have `value` around.
346                // This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
347                unsafe {
348                    self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
349                        raw_display_handle,
350                        raw_window_handle,
351                    })
352                }?
353            }
354        };
355
356        surface._handle_source = handle_source;
357
358        Ok(surface)
359    }
360
361    /// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target.
362    ///
363    /// Internally, this creates surfaces for all backends that are enabled for this instance.
364    ///
365    /// See [`SurfaceTargetUnsafe`] for what targets are supported.
366    /// See [`Instance::create_surface`] for surface creation with safe target variants.
367    ///
368    /// # Safety
369    ///
370    /// - See respective [`SurfaceTargetUnsafe`] variants for safety requirements.
371    pub unsafe fn create_surface_unsafe<'window>(
372        &self,
373        target: SurfaceTargetUnsafe,
374    ) -> Result<Surface<'window>, CreateSurfaceError> {
375        let surface = unsafe { self.inner.create_surface(target)? };
376
377        Ok(Surface {
378            _handle_source: None,
379            inner: surface,
380            config: Mutex::new(None),
381        })
382    }
383
384    /// Polls all devices.
385    ///
386    /// If `force_wait` is true and this is not running on the web, then this
387    /// function will block until all in-flight buffers have been mapped and
388    /// all submitted commands have finished execution.
389    ///
390    /// Return `true` if all devices' queues are empty, or `false` if there are
391    /// queue submissions still in flight. (Note that, unless access to all
392    /// [`Queue`s] associated with this [`Instance`] is coordinated somehow,
393    /// this information could be out of date by the time the caller receives
394    /// it. `Queue`s can be shared between threads, and other threads could
395    /// submit new work at any time.)
396    ///
397    /// On the web, this is a no-op. `Device`s are automatically polled.
398    ///
399    /// [`Queue`s]: Queue
400    pub fn poll_all(&self, force_wait: bool) -> bool {
401        self.inner.poll_all_devices(force_wait)
402    }
403
404    /// Generates memory report.
405    ///
406    /// Returns `None` if the feature is not supported by the backend
407    /// which happens only when WebGPU is pre-selected by the instance creation.
408    #[cfg(wgpu_core)]
409    pub fn generate_report(&self) -> Option<wgc::global::GlobalReport> {
410        self.inner.as_core_opt().map(|ctx| ctx.generate_report())
411    }
412
413    /// Returns set of supported WGSL language extensions supported by this instance.
414    ///
415    /// <https://www.w3.org/TR/webgpu/#gpuwgsllanguagefeatures>
416    #[cfg(feature = "wgsl")]
417    pub fn wgsl_language_features(&self) -> WgslLanguageFeatures {
418        self.inner.wgsl_language_features()
419    }
420}