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