diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 44ed02ff06..1b7c421d76 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -151,6 +151,7 @@ impl GPU { backend_options: wgpu_types::BackendOptions { dx12: wgpu_types::Dx12BackendOptions { shader_compiler: wgpu_types::Dx12Compiler::Fxc, + ..Default::default() }, gl: wgpu_types::GlBackendOptions::default(), noop: wgpu_types::NoopBackendOptions::default(), diff --git a/tests/src/init.rs b/tests/src/init.rs index 6f93508c20..69cfad5194 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -46,6 +46,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> backend_options: wgpu::BackendOptions { dx12: wgpu::Dx12BackendOptions { shader_compiler: dx12_shader_compiler, + ..Default::default() }, gl: wgpu::GlBackendOptions { fence_behavior: if cfg!(target_family = "wasm") { diff --git a/wgpu-hal/examples/ray-traced-triangle/main.rs b/wgpu-hal/examples/ray-traced-triangle/main.rs index 9e2a7771e1..38086f42cb 100644 --- a/wgpu-hal/examples/ray-traced-triangle/main.rs +++ b/wgpu-hal/examples/ray-traced-triangle/main.rs @@ -241,6 +241,7 @@ impl Example { backend_options: wgpu_types::BackendOptions { dx12: Dx12BackendOptions { shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(), + ..Default::default() }, ..Default::default() }, diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 50aa80ad28..b0b5c41b2a 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -55,6 +55,7 @@ impl super::Adapter { library: &Arc, instance_flags: wgt::InstanceFlags, dxc_container: Option>, + backend_options: wgt::Dx12BackendOptions, ) -> Option> { // Create the device so that we can get the capabilities. let device = { @@ -519,6 +520,7 @@ impl super::Adapter { presentation_timer, workarounds, dxc_container, + options: backend_options, }, info, features, @@ -656,6 +658,7 @@ impl crate::Adapter for super::Adapter { self.private_caps, &self.library, self.dxc_container.clone(), + self.options.clone(), )?; Ok(crate::OpenDevice { device, diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index cdc6418665..8fdb959270 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -44,6 +44,7 @@ impl super::Device { private_caps: super::PrivateCapabilities, library: &Arc, dxc_container: Option>, + backend_options: wgt::Dx12BackendOptions, ) -> Result { if private_caps .instance_flags @@ -192,6 +193,7 @@ impl super::Device { raw.clone(), Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, )), + options: backend_options, library: Arc::clone(library), #[cfg(feature = "renderdoc")] render_doc: Default::default(), diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index ef02c8aca7..90e840c5b3 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -112,6 +112,7 @@ impl crate::Instance for super::Instance { supports_allow_tearing, flags: desc.flags, dxc_container, + options: desc.backend_options.dx12.clone(), }) } @@ -128,6 +129,7 @@ impl crate::Instance for super::Instance { target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)), supports_allow_tearing: self.supports_allow_tearing, swap_chain: RwLock::new(None), + options: self.options.clone(), }), _ => Err(crate::InstanceError::new(format!( "window handle {window_handle:?} is not a Win32 handle" @@ -144,7 +146,13 @@ impl crate::Instance for super::Instance { adapters .into_iter() .filter_map(|raw| { - super::Adapter::expose(raw, &self.library, self.flags, self.dxc_container.clone()) + super::Adapter::expose( + raw, + &self.library, + self.flags, + self.dxc_container.clone(), + self.options.clone(), + ) }) .collect() } diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 48fcbee6eb..593ab5f192 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -459,6 +459,7 @@ pub struct Instance { supports_allow_tearing: bool, _lib_dxgi: DxgiLib, flags: wgt::InstanceFlags, + options: wgt::Dx12BackendOptions, dxc_container: Option>, } @@ -472,6 +473,7 @@ impl Instance { target: SurfaceTarget::Visual(visual.to_owned()), supports_allow_tearing: self.supports_allow_tearing, swap_chain: RwLock::new(None), + options: self.options.clone(), } } @@ -489,6 +491,7 @@ impl Instance { target: SurfaceTarget::SurfaceHandle(surface_handle), supports_allow_tearing: self.supports_allow_tearing, swap_chain: RwLock::new(None), + options: self.options.clone(), } } @@ -505,6 +508,7 @@ impl Instance { target: SurfaceTarget::SwapChainPanel(swap_chain_panel.to_owned()), supports_allow_tearing: self.supports_allow_tearing, swap_chain: RwLock::new(None), + options: self.options.clone(), } } } @@ -519,7 +523,7 @@ struct SwapChain { // when the swapchain is destroyed resources: Vec, /// Handle is freed in [`Self::release_resources()`] - waitable: Foundation::HANDLE, + waitable: Option, acquired_count: usize, present_mode: wgt::PresentMode, format: wgt::TextureFormat, @@ -541,11 +545,23 @@ pub struct Surface { target: SurfaceTarget, supports_allow_tearing: bool, swap_chain: RwLock>, + options: wgt::Dx12BackendOptions, } unsafe impl Send for Surface {} unsafe impl Sync for Surface {} +impl Surface { + /// Returns the waitable handle of the swapchain, if it exists. + pub unsafe fn waitable_handle(&self) -> Option { + let swapchain = self.swap_chain.read(); + if let Some(swap_chain) = swapchain.as_ref() { + return swap_chain.waitable.clone(); + } + None + } +} + #[derive(Debug, Clone, Copy)] enum MemoryArchitecture { Unified { @@ -585,6 +601,7 @@ pub struct Adapter { #[allow(unused)] workarounds: Workarounds, dxc_container: Option>, + options: wgt::Dx12BackendOptions, } unsafe impl Send for Adapter {} @@ -636,6 +653,7 @@ pub struct Device { private_caps: PrivateCapabilities, features: wgt::Features, shared: Arc, + options: wgt::Dx12BackendOptions, // CPU only pools rtv_pool: Mutex, dsv_pool: Mutex, @@ -1118,7 +1136,9 @@ impl crate::DynAccelerationStructure for AccelerationStructure {} impl SwapChain { unsafe fn release_resources(mut self) -> Dxgi::IDXGISwapChain3 { - unsafe { Foundation::HANDLE::free(&mut self.waitable) }; + if let Some(mut waitable) = self.waitable.take() { + unsafe { Foundation::HANDLE::free(&mut waitable) }; + } self.raw } @@ -1130,14 +1150,21 @@ impl SwapChain { Some(duration) => duration.as_millis() as u32, None => Threading::INFINITE, }; - match unsafe { Threading::WaitForSingleObject(self.waitable, timeout_ms) } { - Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => Err(crate::SurfaceError::Lost), - Foundation::WAIT_OBJECT_0 => Ok(true), - Foundation::WAIT_TIMEOUT => Ok(false), - other => { - log::error!("Unexpected wait status: 0x{:x?}", other); - Err(crate::SurfaceError::Lost) + + if let Some(waitable) = self.waitable { + match unsafe { Threading::WaitForSingleObject(waitable, timeout_ms) } { + Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => { + Err(crate::SurfaceError::Lost) + } + Foundation::WAIT_OBJECT_0 => Ok(true), + Foundation::WAIT_TIMEOUT => Ok(false), + other => { + log::error!("Unexpected wait status: 0x{:x?}", other); + Err(crate::SurfaceError::Lost) + } } + } else { + Ok(true) } } } @@ -1305,7 +1332,14 @@ impl crate::Surface for Surface { unsafe { swap_chain.SetMaximumFrameLatency(config.maximum_frame_latency) } .into_device_result("SetMaximumFrameLatency")?; - let waitable = unsafe { swap_chain.GetFrameLatencyWaitableObject() }; + + let waitable = match device.options.latency_waitable_object { + wgt::Dx12UseFrameLatencyWaitableObject::None => None, + wgt::Dx12UseFrameLatencyWaitableObject::Wait + | wgt::Dx12UseFrameLatencyWaitableObject::DontWait => { + Some(unsafe { swap_chain.GetFrameLatencyWaitableObject() }) + } + }; let mut resources = Vec::with_capacity(swap_chain_buffer as usize); for i in 0..swap_chain_buffer { @@ -1352,7 +1386,13 @@ impl crate::Surface for Surface { let mut swapchain = self.swap_chain.write(); let sc = swapchain.as_mut().unwrap(); - unsafe { sc.wait(timeout) }?; + match self.options.latency_waitable_object { + wgt::Dx12UseFrameLatencyWaitableObject::None + | wgt::Dx12UseFrameLatencyWaitableObject::DontWait => {} + wgt::Dx12UseFrameLatencyWaitableObject::Wait => { + unsafe { sc.wait(timeout) }?; + } + } let base_index = unsafe { sc.raw.GetCurrentBackBufferIndex() } as usize; let index = (base_index + sc.acquired_count) % sc.resources.len(); diff --git a/wgpu-types/src/instance.rs b/wgpu-types/src/instance.rs index 2083049918..d20eba8190 100644 --- a/wgpu-types/src/instance.rs +++ b/wgpu-types/src/instance.rs @@ -267,6 +267,8 @@ impl GlBackendOptions { pub struct Dx12BackendOptions { /// Which DX12 shader compiler to use. pub shader_compiler: Dx12Compiler, + /// Whether to wait for the latency waitable object before acquiring the next swapchain image. + pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject, } impl Dx12BackendOptions { @@ -276,8 +278,11 @@ impl Dx12BackendOptions { #[must_use] pub fn from_env_or_default() -> Self { let compiler = Dx12Compiler::from_env().unwrap_or_default(); + let latency_waitable_object = + Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default(); Self { shader_compiler: compiler, + latency_waitable_object, } } @@ -287,7 +292,12 @@ impl Dx12BackendOptions { #[must_use] pub fn with_env(self) -> Self { let shader_compiler = self.shader_compiler.with_env(); - Self { shader_compiler } + let latency_waitable_object = self.latency_waitable_object.with_env(); + + Self { + shader_compiler, + latency_waitable_object, + } } } @@ -429,6 +439,54 @@ impl Dx12Compiler { } } +/// Whether and how to use a waitable handle obtained from `GetFrameLatencyWaitableObject`. +#[derive(Clone, Debug, Default)] +pub enum Dx12UseFrameLatencyWaitableObject { + /// Do not obtain a waitable handle and do not wait for it. The swapchain will + /// be created without the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag. + None, + /// Obtain a waitable handle and wait for it before acquiring the next swapchain image. + #[default] + Wait, + /// Create the swapchain with the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag and + /// obtain a waitable handle, but do not wait for it before acquiring the next swapchain image. + /// This is useful if the application wants to wait for the waitable object itself. + DontWait, +} + +impl Dx12UseFrameLatencyWaitableObject { + /// Choose whether to use a frame latency waitable object from the environment variable `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT`. + /// + /// Valid values, case insensitive: + /// - `None` + /// - `Wait` + /// - `DontWait` + #[must_use] + pub fn from_env() -> Option { + let value = crate::env::var("WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT") + .as_deref()? + .to_lowercase(); + match value.as_str() { + "none" => Some(Self::None), + "wait" => Some(Self::Wait), + "dontwait" => Some(Self::DontWait), + _ => None, + } + } + + /// Takes the given setting, modifies it based on the `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT` environment variable, and returns the result. + /// + /// See `from_env` for more information. + #[must_use] + pub fn with_env(self) -> Self { + if let Some(compiler) = Self::from_env() { + compiler + } else { + self + } + } +} + /// Selects which OpenGL ES 3 minor version to request. /// /// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.