Skip to content
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).

#### 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

Expand All @@ -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).

##### 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)

### Bug Fixes
Expand Down
1 change: 1 addition & 0 deletions deno_webgpu/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,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(),
Expand Down
1 change: 1 addition & 0 deletions tests/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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") {
Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/examples/ray-traced-triangle/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ impl<A: hal::Api> Example<A> {
backend_options: wgpu_types::BackendOptions {
dx12: Dx12BackendOptions {
shader_compiler: wgpu_types::Dx12Compiler::default_dynamic_dxc(),
..Default::default()
},
..Default::default()
},
Expand Down
3 changes: 3 additions & 0 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl super::Adapter {
instance_flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
) -> Option<crate::ExposedAdapter<super::Api>> {
// Create the device so that we can get the capabilities.
let device = {
Expand Down Expand Up @@ -534,6 +535,7 @@ impl super::Adapter {
workarounds,
memory_budget_thresholds,
compiler_container,
options: backend_options,
},
info,
features,
Expand Down Expand Up @@ -697,6 +699,7 @@ impl crate::Adapter for super::Adapter {
&self.library,
self.memory_budget_thresholds,
self.compiler_container.clone(),
self.options.clone(),
)?;
Ok(crate::OpenDevice {
device,
Expand Down
2 changes: 2 additions & 0 deletions wgpu-hal/src/dx12/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl super::Device {
library: &Arc<D3D12Lib>,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
) -> Result<Self, crate::DeviceError> {
if private_caps
.instance_flags
Expand Down Expand Up @@ -198,6 +199,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(),
Expand Down
3 changes: 3 additions & 0 deletions wgpu-hal/src/dx12/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl crate::Instance for super::Instance {
flags: desc.flags,
memory_budget_thresholds: desc.memory_budget_thresholds,
compiler_container: Arc::new(compiler_container),
options: desc.backend_options.dx12.clone(),
})
}

Expand All @@ -125,6 +126,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"
Expand All @@ -147,6 +149,7 @@ impl crate::Instance for super::Instance {
self.flags,
self.memory_budget_thresholds,
self.compiler_container.clone(),
self.options.clone(),
)
})
.collect()
Expand Down
57 changes: 46 additions & 11 deletions wgpu-hal/src/dx12/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ pub struct Instance {
flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
options: wgt::Dx12BackendOptions,
}

impl Instance {
Expand All @@ -481,6 +482,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(),
}
}

Expand All @@ -498,6 +500,7 @@ impl Instance {
target: SurfaceTarget::SurfaceHandle(surface_handle),
supports_allow_tearing: self.supports_allow_tearing,
swap_chain: RwLock::new(None),
options: self.options.clone(),
}
}

Expand All @@ -514,6 +517,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(),
}
}
}
Expand All @@ -528,7 +532,7 @@ struct SwapChain {
// when the swapchain is destroyed
resources: Vec<Direct3D12::ID3D12Resource>,
/// Handle is freed in [`Self::release_resources()`]
waitable: Foundation::HANDLE,
waitable: Option<Foundation::HANDLE>,
acquired_count: usize,
present_mode: wgt::PresentMode,
format: wgt::TextureFormat,
Expand All @@ -550,6 +554,7 @@ pub struct Surface {
target: SurfaceTarget,
supports_allow_tearing: bool,
swap_chain: RwLock<Option<SwapChain>>,
options: wgt::Dx12BackendOptions,
}

unsafe impl Send for Surface {}
Expand All @@ -559,6 +564,12 @@ impl Surface {
pub fn swap_chain(&self) -> Option<Dxgi::IDXGISwapChain3> {
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)]
Expand Down Expand Up @@ -601,6 +612,7 @@ pub struct Adapter {
workarounds: Workarounds,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
options: wgt::Dx12BackendOptions,
}

unsafe impl Send for Adapter {}
Expand Down Expand Up @@ -659,6 +671,7 @@ pub struct Device {
idler: Idler,
features: wgt::Features,
shared: Arc<DeviceShared>,
options: wgt::Dx12BackendOptions,
// CPU only pools
rtv_pool: Arc<Mutex<descriptor::CpuPool>>,
dsv_pool: Mutex<descriptor::CpuPool>,
Expand Down Expand Up @@ -1178,7 +1191,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
}

Expand All @@ -1190,14 +1205,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{other:x?}");
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{other:x?}");
Err(crate::SurfaceError::Lost)
}
}
} else {
Ok(true)
}
}
}
Expand Down Expand Up @@ -1365,7 +1387,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 {
Expand Down Expand Up @@ -1412,7 +1441,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();
Expand Down
60 changes: 59 additions & 1 deletion wgpu-types/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,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 {
Expand All @@ -368,8 +370,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,
}
}

Expand All @@ -379,7 +384,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,
}
}
}

Expand Down Expand Up @@ -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.
///
/// When using ANGLE as an OpenGL ES/EGL implementation, explicitly requesting `Version1` can provide a non-conformant ES 3.1 on APIs like D3D11.
Expand Down
Loading