wgpu/api/
instance.rs

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