Skip to content

Commit e7cdfc4

Browse files
authored
Allow disabling waiting for latency waitable object (#7400)
1 parent f04b3b0 commit e7cdfc4

File tree

9 files changed

+122
-13
lines changed

9 files changed

+122
-13
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).
7373

7474
#### DX12
7575

76-
- 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).
76+
- Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400)
7777

7878
### Bug Fixes
7979

@@ -85,6 +85,11 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).
8585

8686
- Fix `STATUS_HEAP_CORRUPTION` crash when concurrently calling `create_sampler`. By @atlv24 in [#8043](https://github.com/gfx-rs/wgpu/pull/8043).
8787

88+
##### DX12
89+
90+
- 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).
91+
92+
8893
## v26.0.2 (2025-07-23)
8994

9095
### Bug Fixes

deno_webgpu/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ impl GPU {
158158
backend_options: wgpu_types::BackendOptions {
159159
dx12: wgpu_types::Dx12BackendOptions {
160160
shader_compiler: wgpu_types::Dx12Compiler::Fxc,
161+
..Default::default()
161162
},
162163
gl: wgpu_types::GlBackendOptions::default(),
163164
noop: wgpu_types::NoopBackendOptions::default(),

tests/src/init.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) ->
5050
backend_options: wgpu::BackendOptions {
5151
dx12: wgpu::Dx12BackendOptions {
5252
shader_compiler: dx12_shader_compiler,
53+
..Default::default()
5354
},
5455
gl: wgpu::GlBackendOptions {
5556
fence_behavior: if cfg!(target_family = "wasm") {

wgpu-hal/examples/ray-traced-triangle/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ impl<A: hal::Api> Example<A> {
242242
backend_options: wgpu_types::BackendOptions {
243243
dx12: Dx12BackendOptions {
244244
shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(),
245+
..Default::default()
245246
},
246247
..Default::default()
247248
},

wgpu-hal/src/dx12/adapter.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ impl super::Adapter {
5858
instance_flags: wgt::InstanceFlags,
5959
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
6060
compiler_container: Arc<shader_compilation::CompilerContainer>,
61+
backend_options: wgt::Dx12BackendOptions,
6162
) -> Option<crate::ExposedAdapter<super::Api>> {
6263
// Create the device so that we can get the capabilities.
6364
let device = {
@@ -534,6 +535,7 @@ impl super::Adapter {
534535
workarounds,
535536
memory_budget_thresholds,
536537
compiler_container,
538+
options: backend_options,
537539
},
538540
info,
539541
features,
@@ -697,6 +699,7 @@ impl crate::Adapter for super::Adapter {
697699
&self.library,
698700
self.memory_budget_thresholds,
699701
self.compiler_container.clone(),
702+
self.options.clone(),
700703
)?;
701704
Ok(crate::OpenDevice {
702705
device,

wgpu-hal/src/dx12/device.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl super::Device {
4848
library: &Arc<D3D12Lib>,
4949
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
5050
compiler_container: Arc<shader_compilation::CompilerContainer>,
51+
backend_options: wgt::Dx12BackendOptions,
5152
) -> Result<Self, crate::DeviceError> {
5253
if private_caps
5354
.instance_flags
@@ -198,6 +199,7 @@ impl super::Device {
198199
raw.clone(),
199200
Direct3D12::D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
200201
)),
202+
options: backend_options,
201203
library: Arc::clone(library),
202204
#[cfg(feature = "renderdoc")]
203205
render_doc: Default::default(),

wgpu-hal/src/dx12/instance.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ impl crate::Instance for super::Instance {
109109
flags: desc.flags,
110110
memory_budget_thresholds: desc.memory_budget_thresholds,
111111
compiler_container: Arc::new(compiler_container),
112+
options: desc.backend_options.dx12.clone(),
112113
})
113114
}
114115

@@ -125,6 +126,7 @@ impl crate::Instance for super::Instance {
125126
target: SurfaceTarget::WndHandle(Foundation::HWND(handle.hwnd.get() as *mut _)),
126127
supports_allow_tearing: self.supports_allow_tearing,
127128
swap_chain: RwLock::new(None),
129+
options: self.options.clone(),
128130
}),
129131
_ => Err(crate::InstanceError::new(format!(
130132
"window handle {window_handle:?} is not a Win32 handle"
@@ -147,6 +149,7 @@ impl crate::Instance for super::Instance {
147149
self.flags,
148150
self.memory_budget_thresholds,
149151
self.compiler_container.clone(),
152+
self.options.clone(),
150153
)
151154
})
152155
.collect()

wgpu-hal/src/dx12/mod.rs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ pub struct Instance {
464464
flags: wgt::InstanceFlags,
465465
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
466466
compiler_container: Arc<shader_compilation::CompilerContainer>,
467+
options: wgt::Dx12BackendOptions,
467468
}
468469

469470
impl Instance {
@@ -481,6 +482,7 @@ impl Instance {
481482
target: SurfaceTarget::Visual(visual.to_owned()),
482483
supports_allow_tearing: self.supports_allow_tearing,
483484
swap_chain: RwLock::new(None),
485+
options: self.options.clone(),
484486
}
485487
}
486488

@@ -498,6 +500,7 @@ impl Instance {
498500
target: SurfaceTarget::SurfaceHandle(surface_handle),
499501
supports_allow_tearing: self.supports_allow_tearing,
500502
swap_chain: RwLock::new(None),
503+
options: self.options.clone(),
501504
}
502505
}
503506

@@ -514,6 +517,7 @@ impl Instance {
514517
target: SurfaceTarget::SwapChainPanel(swap_chain_panel.to_owned()),
515518
supports_allow_tearing: self.supports_allow_tearing,
516519
swap_chain: RwLock::new(None),
520+
options: self.options.clone(),
517521
}
518522
}
519523
}
@@ -528,7 +532,7 @@ struct SwapChain {
528532
// when the swapchain is destroyed
529533
resources: Vec<Direct3D12::ID3D12Resource>,
530534
/// Handle is freed in [`Self::release_resources()`]
531-
waitable: Foundation::HANDLE,
535+
waitable: Option<Foundation::HANDLE>,
532536
acquired_count: usize,
533537
present_mode: wgt::PresentMode,
534538
format: wgt::TextureFormat,
@@ -550,6 +554,7 @@ pub struct Surface {
550554
target: SurfaceTarget,
551555
supports_allow_tearing: bool,
552556
swap_chain: RwLock<Option<SwapChain>>,
557+
options: wgt::Dx12BackendOptions,
553558
}
554559

555560
unsafe impl Send for Surface {}
@@ -559,6 +564,12 @@ impl Surface {
559564
pub fn swap_chain(&self) -> Option<Dxgi::IDXGISwapChain3> {
560565
Some(self.swap_chain.read().as_ref()?.raw.clone())
561566
}
567+
568+
/// Returns the waitable handle associated with this swap chain, if any.
569+
/// Handle is only valid while the swap chain is alive.
570+
pub unsafe fn waitable_handle(&self) -> Option<Foundation::HANDLE> {
571+
self.swap_chain.read().as_ref()?.waitable
572+
}
562573
}
563574

564575
#[derive(Debug, Clone, Copy)]
@@ -601,6 +612,7 @@ pub struct Adapter {
601612
workarounds: Workarounds,
602613
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
603614
compiler_container: Arc<shader_compilation::CompilerContainer>,
615+
options: wgt::Dx12BackendOptions,
604616
}
605617

606618
unsafe impl Send for Adapter {}
@@ -659,6 +671,7 @@ pub struct Device {
659671
idler: Idler,
660672
features: wgt::Features,
661673
shared: Arc<DeviceShared>,
674+
options: wgt::Dx12BackendOptions,
662675
// CPU only pools
663676
rtv_pool: Arc<Mutex<descriptor::CpuPool>>,
664677
dsv_pool: Mutex<descriptor::CpuPool>,
@@ -1178,7 +1191,9 @@ impl crate::DynAccelerationStructure for AccelerationStructure {}
11781191

11791192
impl SwapChain {
11801193
unsafe fn release_resources(mut self) -> Dxgi::IDXGISwapChain3 {
1181-
unsafe { Foundation::HANDLE::free(&mut self.waitable) };
1194+
if let Some(mut waitable) = self.waitable.take() {
1195+
unsafe { Foundation::HANDLE::free(&mut waitable) };
1196+
}
11821197
self.raw
11831198
}
11841199

@@ -1190,14 +1205,21 @@ impl SwapChain {
11901205
Some(duration) => duration.as_millis() as u32,
11911206
None => Threading::INFINITE,
11921207
};
1193-
match unsafe { Threading::WaitForSingleObject(self.waitable, timeout_ms) } {
1194-
Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => Err(crate::SurfaceError::Lost),
1195-
Foundation::WAIT_OBJECT_0 => Ok(true),
1196-
Foundation::WAIT_TIMEOUT => Ok(false),
1197-
other => {
1198-
log::error!("Unexpected wait status: 0x{other:x?}");
1199-
Err(crate::SurfaceError::Lost)
1208+
1209+
if let Some(waitable) = self.waitable {
1210+
match unsafe { Threading::WaitForSingleObject(waitable, timeout_ms) } {
1211+
Foundation::WAIT_ABANDONED | Foundation::WAIT_FAILED => {
1212+
Err(crate::SurfaceError::Lost)
1213+
}
1214+
Foundation::WAIT_OBJECT_0 => Ok(true),
1215+
Foundation::WAIT_TIMEOUT => Ok(false),
1216+
other => {
1217+
log::error!("Unexpected wait status: 0x{other:x?}");
1218+
Err(crate::SurfaceError::Lost)
1219+
}
12001220
}
1221+
} else {
1222+
Ok(true)
12011223
}
12021224
}
12031225
}
@@ -1365,7 +1387,14 @@ impl crate::Surface for Surface {
13651387

13661388
unsafe { swap_chain.SetMaximumFrameLatency(config.maximum_frame_latency) }
13671389
.into_device_result("SetMaximumFrameLatency")?;
1368-
let waitable = unsafe { swap_chain.GetFrameLatencyWaitableObject() };
1390+
1391+
let waitable = match device.options.latency_waitable_object {
1392+
wgt::Dx12UseFrameLatencyWaitableObject::None => None,
1393+
wgt::Dx12UseFrameLatencyWaitableObject::Wait
1394+
| wgt::Dx12UseFrameLatencyWaitableObject::DontWait => {
1395+
Some(unsafe { swap_chain.GetFrameLatencyWaitableObject() })
1396+
}
1397+
};
13691398

13701399
let mut resources = Vec::with_capacity(swap_chain_buffer as usize);
13711400
for i in 0..swap_chain_buffer {
@@ -1412,7 +1441,13 @@ impl crate::Surface for Surface {
14121441
let mut swapchain = self.swap_chain.write();
14131442
let sc = swapchain.as_mut().unwrap();
14141443

1415-
unsafe { sc.wait(timeout) }?;
1444+
match self.options.latency_waitable_object {
1445+
wgt::Dx12UseFrameLatencyWaitableObject::None
1446+
| wgt::Dx12UseFrameLatencyWaitableObject::DontWait => {}
1447+
wgt::Dx12UseFrameLatencyWaitableObject::Wait => {
1448+
unsafe { sc.wait(timeout) }?;
1449+
}
1450+
}
14161451

14171452
let base_index = unsafe { sc.raw.GetCurrentBackBufferIndex() } as usize;
14181453
let index = (base_index + sc.acquired_count) % sc.resources.len();

wgpu-types/src/instance.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ impl GlBackendOptions {
359359
pub struct Dx12BackendOptions {
360360
/// Which DX12 shader compiler to use.
361361
pub shader_compiler: Dx12Compiler,
362+
/// Whether to wait for the latency waitable object before acquiring the next swapchain image.
363+
pub latency_waitable_object: Dx12UseFrameLatencyWaitableObject,
362364
}
363365

364366
impl Dx12BackendOptions {
@@ -368,8 +370,11 @@ impl Dx12BackendOptions {
368370
#[must_use]
369371
pub fn from_env_or_default() -> Self {
370372
let compiler = Dx12Compiler::from_env().unwrap_or_default();
373+
let latency_waitable_object =
374+
Dx12UseFrameLatencyWaitableObject::from_env().unwrap_or_default();
371375
Self {
372376
shader_compiler: compiler,
377+
latency_waitable_object,
373378
}
374379
}
375380

@@ -379,7 +384,12 @@ impl Dx12BackendOptions {
379384
#[must_use]
380385
pub fn with_env(self) -> Self {
381386
let shader_compiler = self.shader_compiler.with_env();
382-
Self { shader_compiler }
387+
let latency_waitable_object = self.latency_waitable_object.with_env();
388+
389+
Self {
390+
shader_compiler,
391+
latency_waitable_object,
392+
}
383393
}
384394
}
385395

@@ -515,6 +525,54 @@ impl Dx12Compiler {
515525
}
516526
}
517527

528+
/// Whether and how to use a waitable handle obtained from `GetFrameLatencyWaitableObject`.
529+
#[derive(Clone, Debug, Default)]
530+
pub enum Dx12UseFrameLatencyWaitableObject {
531+
/// Do not obtain a waitable handle and do not wait for it. The swapchain will
532+
/// be created without the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag.
533+
None,
534+
/// Obtain a waitable handle and wait for it before acquiring the next swapchain image.
535+
#[default]
536+
Wait,
537+
/// Create the swapchain with the `DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT` flag and
538+
/// obtain a waitable handle, but do not wait for it before acquiring the next swapchain image.
539+
/// This is useful if the application wants to wait for the waitable object itself.
540+
DontWait,
541+
}
542+
543+
impl Dx12UseFrameLatencyWaitableObject {
544+
/// Choose whether to use a frame latency waitable object from the environment variable `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT`.
545+
///
546+
/// Valid values, case insensitive:
547+
/// - `None`
548+
/// - `Wait`
549+
/// - `DontWait`
550+
#[must_use]
551+
pub fn from_env() -> Option<Self> {
552+
let value = crate::env::var("WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT")
553+
.as_deref()?
554+
.to_lowercase();
555+
match value.as_str() {
556+
"none" => Some(Self::None),
557+
"wait" => Some(Self::Wait),
558+
"dontwait" => Some(Self::DontWait),
559+
_ => None,
560+
}
561+
}
562+
563+
/// Takes the given setting, modifies it based on the `WGPU_DX12_USE_FRAME_LATENCY_WAITABLE_OBJECT` environment variable, and returns the result.
564+
///
565+
/// See `from_env` for more information.
566+
#[must_use]
567+
pub fn with_env(self) -> Self {
568+
if let Some(compiler) = Self::from_env() {
569+
compiler
570+
} else {
571+
self
572+
}
573+
}
574+
}
575+
518576
/// Selects which OpenGL ES 3 minor version to request.
519577
///
520578
/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.

0 commit comments

Comments
 (0)