Allow disabling waiting for latency waitable object (#7400)

This commit is contained in:
Marc Pabst 2025-08-10 11:01:25 +01:00 committed by GitHub
parent f04b3b020f
commit e7cdfc436a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 122 additions and 13 deletions

View File

@ -73,7 +73,7 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).
#### DX12 #### DX12
- Fixed a bug where access to matrices with 2 rows would not work in some cases. By @andyleiserson in [#7438](https://github.com/gfx-rs/wgpu/pull/7438). - Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400)
### Bug Fixes ### Bug Fixes
@ -85,6 +85,11 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).
- Fix `STATUS_HEAP_CORRUPTION` crash when concurrently calling `create_sampler`. By @atlv24 in [#8043](https://github.com/gfx-rs/wgpu/pull/8043). - Fix `STATUS_HEAP_CORRUPTION` crash when concurrently calling `create_sampler`. By @atlv24 in [#8043](https://github.com/gfx-rs/wgpu/pull/8043).
##### DX12
- Fixed a bug where access to matrices with 2 rows would not work in some cases. By @andyleiserson in [#7438](https://github.com/gfx-rs/wgpu/pull/7438).
## v26.0.2 (2025-07-23) ## v26.0.2 (2025-07-23)
### Bug Fixes ### Bug Fixes

View File

@ -158,6 +158,7 @@ impl GPU {
backend_options: wgpu_types::BackendOptions { backend_options: wgpu_types::BackendOptions {
dx12: wgpu_types::Dx12BackendOptions { dx12: wgpu_types::Dx12BackendOptions {
shader_compiler: wgpu_types::Dx12Compiler::Fxc, shader_compiler: wgpu_types::Dx12Compiler::Fxc,
..Default::default()
}, },
gl: wgpu_types::GlBackendOptions::default(), gl: wgpu_types::GlBackendOptions::default(),
noop: wgpu_types::NoopBackendOptions::default(), noop: wgpu_types::NoopBackendOptions::default(),

View File

@ -50,6 +50,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) ->
backend_options: wgpu::BackendOptions { backend_options: wgpu::BackendOptions {
dx12: wgpu::Dx12BackendOptions { dx12: wgpu::Dx12BackendOptions {
shader_compiler: dx12_shader_compiler, shader_compiler: dx12_shader_compiler,
..Default::default()
}, },
gl: wgpu::GlBackendOptions { gl: wgpu::GlBackendOptions {
fence_behavior: if cfg!(target_family = "wasm") { fence_behavior: if cfg!(target_family = "wasm") {

View File

@ -242,6 +242,7 @@ impl<A: hal::Api> Example<A> {
backend_options: wgpu_types::BackendOptions { backend_options: wgpu_types::BackendOptions {
dx12: Dx12BackendOptions { dx12: Dx12BackendOptions {
shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(), shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(),
..Default::default()
}, },
..Default::default() ..Default::default()
}, },

View File

@ -58,6 +58,7 @@ impl super::Adapter {
instance_flags: wgt::InstanceFlags, instance_flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds, memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>, compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
) -> Option<crate::ExposedAdapter<super::Api>> { ) -> Option<crate::ExposedAdapter<super::Api>> {
// Create the device so that we can get the capabilities. // Create the device so that we can get the capabilities.
let device = { let device = {
@ -534,6 +535,7 @@ impl super::Adapter {
workarounds, workarounds,
memory_budget_thresholds, memory_budget_thresholds,
compiler_container, compiler_container,
options: backend_options,
}, },
info, info,
features, features,
@ -697,6 +699,7 @@ impl crate::Adapter for super::Adapter {
&self.library, &self.library,
self.memory_budget_thresholds, self.memory_budget_thresholds,
self.compiler_container.clone(), self.compiler_container.clone(),
self.options.clone(),
)?; )?;
Ok(crate::OpenDevice { Ok(crate::OpenDevice {
device, device,

View File

@ -48,6 +48,7 @@ impl super::Device {
library: &Arc<D3D12Lib>, library: &Arc<D3D12Lib>,
memory_budget_thresholds: wgt::MemoryBudgetThresholds, memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>, compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
) -> Result<Self, crate::DeviceError> { ) -> Result<Self, crate::DeviceError> {
if private_caps if private_caps
.instance_flags .instance_flags
@ -198,6 +199,7 @@ impl super::Device {
raw.clone(), raw.clone(),
Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
)), )),
options: backend_options,
library: Arc::clone(library), library: Arc::clone(library),
#[cfg(feature = "renderdoc")] #[cfg(feature = "renderdoc")]
render_doc: Default::default(), render_doc: Default::default(),

View File

@ -109,6 +109,7 @@ impl crate::Instance for super::Instance {
flags: desc.flags, flags: desc.flags,
memory_budget_thresholds: desc.memory_budget_thresholds, memory_budget_thresholds: desc.memory_budget_thresholds,
compiler_container: Arc::new(compiler_container), compiler_container: Arc::new(compiler_container),
options: desc.backend_options.dx12.clone(),
}) })
} }
@ -125,6 +126,7 @@ impl crate::Instance for super::Instance {
target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)), target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)),
supports_allow_tearing: self.supports_allow_tearing, supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None), swap_chain: RwLock::new(None),
options: self.options.clone(),
}), }),
_ => Err(crate::InstanceError::new(format!( _ => Err(crate::InstanceError::new(format!(
"window handle {window_handle:?} is not a Win32 handle" "window handle {window_handle:?} is not a Win32 handle"
@ -147,6 +149,7 @@ impl crate::Instance for super::Instance {
self.flags, self.flags,
self.memory_budget_thresholds, self.memory_budget_thresholds,
self.compiler_container.clone(), self.compiler_container.clone(),
self.options.clone(),
) )
}) })
.collect() .collect()

View File

@ -464,6 +464,7 @@ pub struct Instance {
flags: wgt::InstanceFlags, flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds, memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>, compiler_container: Arc<shader_compilation::CompilerContainer>,
options: wgt::Dx12BackendOptions,
} }
impl Instance { impl Instance {
@ -481,6 +482,7 @@ impl Instance {
target: SurfaceTarget::Visual(visual.to_owned()), target: SurfaceTarget::Visual(visual.to_owned()),
supports_allow_tearing: self.supports_allow_tearing, supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None), swap_chain: RwLock::new(None),
options: self.options.clone(),
} }
} }
@ -498,6 +500,7 @@ impl Instance {
target: SurfaceTarget::SurfaceHandle(surface_handle), target: SurfaceTarget::SurfaceHandle(surface_handle),
supports_allow_tearing: self.supports_allow_tearing, supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None), swap_chain: RwLock::new(None),
options: self.options.clone(),
} }
} }
@ -514,6 +517,7 @@ impl Instance {
target: SurfaceTarget::SwapChainPanel(swap_chain_panel.to_owned()), target: SurfaceTarget::SwapChainPanel(swap_chain_panel.to_owned()),
supports_allow_tearing: self.supports_allow_tearing, supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None), swap_chain: RwLock::new(None),
options: self.options.clone(),
} }
} }
} }
@ -528,7 +532,7 @@ struct SwapChain {
// when the swapchain is destroyed // when the swapchain is destroyed
resources: Vec<Direct3D12::ID3D12Resource>, resources: Vec<Direct3D12::ID3D12Resource>,
/// Handle is freed in [`Self::release_resources()`] /// Handle is freed in [`Self::release_resources()`]
waitable: Foundation::HANDLE, waitable: Option<Foundation::HANDLE>,
acquired_count: usize, acquired_count: usize,
present_mode: wgt::PresentMode, present_mode: wgt::PresentMode,
format: wgt::TextureFormat, format: wgt::TextureFormat,
@ -550,6 +554,7 @@ pub struct Surface {
target: SurfaceTarget, target: SurfaceTarget,
supports_allow_tearing: bool, supports_allow_tearing: bool,
swap_chain: RwLock<Option<SwapChain>>, swap_chain: RwLock<Option<SwapChain>>,
options: wgt::Dx12BackendOptions,
} }
unsafe impl Send for Surface {} unsafe impl Send for Surface {}
@ -559,6 +564,12 @@ impl Surface {
pub fn swap_chain(&self) -> Option<Dxgi::IDXGISwapChain3> { pub fn swap_chain(&self) -> Option<Dxgi::IDXGISwapChain3> {
Some(self.swap_chain.read().as_ref()?.raw.clone()) Some(self.swap_chain.read().as_ref()?.raw.clone())
} }
/// Returns the waitable handle associated with this swap chain, if any.
/// Handle is only valid while the swap chain is alive.
pub unsafe fn waitable_handle(&self) -> Option<Foundation::HANDLE> {
self.swap_chain.read().as_ref()?.waitable
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -601,6 +612,7 @@ pub struct Adapter {
workarounds: Workarounds, workarounds: Workarounds,
memory_budget_thresholds: wgt::MemoryBudgetThresholds, memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>, compiler_container: Arc<shader_compilation::CompilerContainer>,
options: wgt::Dx12BackendOptions,
} }
unsafe impl Send for Adapter {} unsafe impl Send for Adapter {}
@ -659,6 +671,7 @@ pub struct Device {
idler: Idler, idler: Idler,
features: wgt::Features, features: wgt::Features,
shared: Arc<DeviceShared>, shared: Arc<DeviceShared>,
options: wgt::Dx12BackendOptions,
// CPU only pools // CPU only pools
rtv_pool: Arc<Mutex<descriptor::CpuPool>>, rtv_pool: Arc<Mutex<descriptor::CpuPool>>,
dsv_pool: Mutex<descriptor::CpuPool>, dsv_pool: Mutex<descriptor::CpuPool>,
@ -1178,7 +1191,9 @@ impl crate::DynAccelerationStructure for AccelerationStructure {}
impl SwapChain { impl SwapChain {
unsafe fn release_resources(mut self) -> Dxgi::IDXGISwapChain3 { 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 self.raw
} }
@ -1190,14 +1205,21 @@ impl SwapChain {
Some(duration) => duration.as_millis() as u32, Some(duration) => duration.as_millis() as u32,
None => Threading::INFINITE, None => Threading::INFINITE,
}; };
match unsafe { Threading::WaitForSingleObject(self.waitable, timeout_ms) } {
Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => Err(crate::SurfaceError::Lost), if let Some(waitable) = self.waitable {
Foundation::WAIT_OBJECT_0 => Ok(true), match unsafe { Threading::WaitForSingleObject(waitable, timeout_ms) } {
Foundation::WAIT_TIMEOUT => Ok(false), Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => {
other => { Err(crate::SurfaceError::Lost)
log::error!("Unexpected wait status: 0x{other:x?}"); }
Err(crate::SurfaceError::Lost) Foundation::WAIT_OBJECT_0 => Ok(true),
Foundation::WAIT_TIMEOUT => Ok(false),
other => {
log::error!("Unexpected wait status: 0x{other:x?}");
Err(crate::SurfaceError::Lost)
}
} }
} else {
Ok(true)
} }
} }
} }
@ -1365,7 +1387,14 @@ impl crate::Surface for Surface {
unsafe { swap_chain.SetMaximumFrameLatency(config.maximum_frame_latency) } unsafe { swap_chain.SetMaximumFrameLatency(config.maximum_frame_latency) }
.into_device_result("SetMaximumFrameLatency")?; .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); let mut resources = Vec::with_capacity(swap_chain_buffer as usize);
for i in 0..swap_chain_buffer { for i in 0..swap_chain_buffer {
@ -1412,7 +1441,13 @@ impl crate::Surface for Surface {
let mut swapchain = self.swap_chain.write(); let mut swapchain = self.swap_chain.write();
let sc = swapchain.as_mut().unwrap(); 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 base_index = unsafe { sc.raw.GetCurrentBackBufferIndex() } as usize;
let index = (base_index + sc.acquired_count) % sc.resources.len(); let index = (base_index + sc.acquired_count) % sc.resources.len();

View File

@ -359,6 +359,8 @@ impl GlBackendOptions {
pub struct Dx12BackendOptions { pub struct Dx12BackendOptions {
/// Which DX12 shader compiler to use. /// Which DX12 shader compiler to use.
pub shader_compiler: Dx12Compiler, 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 { impl Dx12BackendOptions {
@ -368,8 +370,11 @@ impl Dx12BackendOptions {
#[must_use] #[must_use]
pub fn from_env_or_default() -> Self { pub fn from_env_or_default() -> Self {
let compiler = Dx12Compiler::from_env().unwrap_or_default(); let compiler = Dx12Compiler::from_env().unwrap_or_default();
let latency_waitable_object =
Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
Self { Self {
shader_compiler: compiler, shader_compiler: compiler,
latency_waitable_object,
} }
} }
@ -379,7 +384,12 @@ impl Dx12BackendOptions {
#[must_use] #[must_use]
pub fn with_env(self) -> Self { pub fn with_env(self) -> Self {
let shader_compiler = self.shader_compiler.with_env(); 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,
}
} }
} }
@ -515,6 +525,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<Self> {
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. /// 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. /// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.