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}