Releases: gfx-rs/wgpu
v29.0.1
v29.0.1 (2026-03-26)
This release includes wgpu-core, wgpu-hal, naga, wgpu-naga-bridge and wgpu-types version 29.0.1. All other crates remain at their previous versions.
Bug Fixes
General
- Fix limit comparison logic for
max_inter_stage_shader_variables. By @ErichDonGubler in #9264.
Metal
- Added guards to avoid calling some feature detection methods that are not implemented on
CaptureMTLDevice. By @andyleiserson in #9284. - Fix a regression where buffer limits were too conservative. This comes at the cost of non-compliant WebGPU limit validation. A future major release will keep the relaxed buffer limits on native while allowing WebGPU-mandated validation to be opted in. See #9287.
GLES / OpenGL
Validation
- Don't crash in the
Displayimplementation ofCreateTextureViewError::TooMany{MipLevels,ArrayLayers}when their base and offset overflow. By @ErichDonGubler in #8808.
v29.0.0
Major Changes
Surface::get_current_texture now returns CurrentSurfaceTexture enum
Surface::get_current_texture no longer returns Result<SurfaceTexture, SurfaceError>.
Instead, it returns a single CurrentSurfaceTexture enum that represents all possible outcomes as variants.
SurfaceError has been removed, and the suboptimal field on SurfaceTexture has been replaced by a dedicated Suboptimal variant.
match surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(frame) => { /* render */ }
wgpu::CurrentSurfaceTexture::Timeout
| wgpu::CurrentSurfaceTexture::Occluded => { /* skip frame */ }
wgpu::CurrentSurfaceTexture::Outdated
| wgpu::CurrentSurfaceTexture::Suboptimal(frame) => { /* reconfigure surface */ }
wgpu::CurrentSurfaceTexture::Lost => { /* reconfigure surface, or recreate device if device lost */ }
wgpu::CurrentSurfaceTexture::Validation => {
/* Only happens if there is a validation error and you
have registered a error scope or uncaptured error handler. */
}
}By @cwfitzgerald, @Wumpf, and @emilk in #9141 and #9257.
InstanceDescriptor initialization APIs and display handle changes
A display handle represents a connection to the platform's display server (e.g. a Wayland or X11 connection on Linux). This is distinct from a window — a display handle is the system-level connection through which windows are created and managed.
InstanceDescriptor's convenience constructors (an implementation of Default and the static from_env_or_default method) have been removed. In their place are new static methods that force recognition of whether a display handle is used:
new_with_display_handlenew_with_display_handle_from_envnew_without_display_handlenew_without_display_handle_from_env
If you are using winit, this can be populated using EventLoop::owned_display_handle.
- InstanceDescriptor::default();
- InstanceDescriptor::from_env_or_default();
+ InstanceDescriptor::new_with_display_handle(Box::new(event_loop.owned_display_handle()));
+ InstanceDescriptor::new_with_display_handle_from_env(Box::new(event_loop.owned_display_handle()));Additionally, DisplayHandle is now optional when creating a surface if a display handle was already passed to InstanceDescriptor. This means that once you've provided the display handle at instance creation time, you no longer need to pass it again for each surface you create.
By @MarijnS95 in #8782
Bind group layouts now optional in PipelineLayoutDescriptor
This allows gaps in bind group layouts and adds full support for unbinding, bring us in compliance with the WebGPU spec. As a result of this PipelineLayoutDescriptor's bind_group_layouts field now has type of &[Option<&BindGroupLayout>]. To migrate wrap bind group layout references in Some:
let pl_desc = wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[
- &bind_group_layout
+ Some(&bind_group_layout)
],
immediate_size: 0,
});MSRV update
wgpu now has a new MSRV policy. This release has an MSRV of 1.87. This is lower than v27's 1.88 and v28's 1.92. Going forward, we will only bump wgpu's MSRV if it has tangible benefits for the code, and we will never bump to an MSRV higher than stable - 3. So if stable is at 1.97 and 1.94 brought benefit to our code, we could bump it no higher than 1.94. As before, MSRV bumps will always be breaking changes.
By @cwfitzgerald in #8999.
WriteOnly
To ensure memory safety when accessing mapped GPU memory, MapMode::Write buffer mappings (BufferViewMut and also QueueWriteBufferView) can no longer be dereferenced to Rust &mut [u8]. Instead, they must be used through the new pointer type wgpu::WriteOnly<[u8]>, which does not allow reading at all.
WriteOnly<[u8]> is designed to offer similar functionality to &mut [u8] and have almost no performance overhead, but you will probably need to make some changes for anything more complicated than get_mapped_range_mut().copy_from_slice(my_data); in particular, replacing view[start..end] with view.slice(start..end).
Depth/stencil state changes
The depth_write_enabled and depth_compare members of DepthStencilState are now optional, and may be omitted when they do not apply, to match WebGPU.
depth_write_enabled is applicable, and must be Some, if format has a depth aspect, i.e., is a depth or depth/stencil format. Otherwise, a value of None best reflects that it does not apply, although Some(false) is also accepted.
depth_compare is applicable, and must be Some, if depth_write_enabled is Some(true), or if depth_fail_op for either stencil face is not Keep. Otherwise, a value of None best reflects that it does not apply, although Some(CompareFunction::Always) is also accepted.
There is also a new constructor DepthStencilState::stencil which may be used instead of a struct literal for stencil operations.
Example 1: A configuration that does a depth test and writes updated values:
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
- depth_write_enabled: true,
- depth_compare: wgpu::CompareFunction::Less,
+ depth_write_enabled: Some(true),
+ depth_compare: Some(wgpu::CompareFunction::Less),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),Example 2: A configuration with only stencil:
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Stencil8,
- depth_write_enabled: false,
- depth_compare: wgpu::CompareFunction::Always,
+ depth_write_enabled: None,
+ depth_compare: None,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),Example 3: The previous example written using the new stencil() constructor:
depth_stencil: Some(wgpu::DepthStencilState::stencil(
wgpu::TextureFormat::Stencil8,
wgpu::StencilState::default(),
)),D3D12 Agility SDK support
Added support for loading a specific DirectX 12 Agility SDK runtime via the Independent Devices API. The Agility SDK lets applications ship a newer D3D12 runtime alongside their binary, unlocking the latest D3D12 features without waiting for an OS update.
Configure it programmatically:
let options = wgpu::Dx12BackendOptions {
agility_sdk: Some(wgpu::Dx12AgilitySDK {
sdk_version: 619,
sdk_path: "path/to/sdk/bin/x64".into(),
}),
..Default::default()
};Or via environment variables:
WGPU_DX12_AGILITY_SDK_PATH=path/to/sdk/bin/x64
WGPU_DX12_AGILITY_SDK_VERSION=619
The sdk_version must match the version of the D3D12Core.dll in the provided path exactly, or loading will fail.
If the Agility SDK fails to load (e.g. version mismatch, missing DLL, or unsupported OS), wgpu logs a warning and falls back to the system D3D12 runtime.
By @cwfitzgerald in #9130.
primitive_index is now a WGSL enable extension
WGSL shaders using @builtin(primitive_index) must now request it with enable primitive_index;. The SHADER_PRIMITIVE_INDEX feature has been renamed to PRIMITIVE_INDEX and moved from FeaturesWGPU to FeaturesWebGPU. By @inner-daemons in #8879 and @andyleiserson in #9101.
- device.features().contains(wgpu::FeaturesWGPU::SHADER_PRIMITIVE_INDEX)
+ device.features().contains(wgpu::FeaturesWebGPU::PRIMITIVE_INDEX)// WGSL shaders must now include this directive:
enable primitive_index;maxInterStageShaderComponents replaced by maxInterStageShaderVariables
Migrated from the max_inter_stage_shader_components limit to max_inter_stage_shader_variables, following the latest WebGPU spec. Components counted individual scalars (e.g. a vec4 = 4 components), while variables counts locations (e.g. a vec4 = 1 variable). This changes validation in a way that should not affect most programs. By @ErichDonGubler in #8652, #8792.
- limits.max_inter_stage_shader_components
+ limits.max_inter_stage_shader_variablesOther Breaking Changes
- Use clearer field names for
StageError::InvalidWorkgroupSize. By @ErichDonGubler in #9192.
New Features
General
- Added TLAS binding array support via
ACCELERATION_STRUCTURE_BINDING_ARRAY. By @kvark in #8923. - Added
wgpu-naga-bridgecrate with conversions betweennagaandwgpu-types(features to capabilities, storage format mapping, shader stage mapping). By @atlv24 in #9201. - Added support for cooperative load/store operations in shaders. Currently only WGSL on the input and SPIR-V, METAL, and WGSL on the output are supported. By @kvark in #8251.
- Added support for per-vertex attributes in fragment shaders. Currently only WGSL input is supported, and only SPIR-V or WGSL output is s...
v28.0.1
This release includes wgpu-core, wgpu-hal version 28.0.1. All other crates remain at their previous versions.
General
- Fixed crash on nvidia cards when presenting from another thread. By @inner-daemons in #9036.
Vulkan
Metal
v28.0.0 - Mesh Shaders, Immediates, and More!
Major Changes
Mesh Shaders
This has been a long time coming. See the tracking issue for more information.
They are now fully supported on Vulkan, and supported on Metal and DX12 with passthrough shaders. WGSL parsing and rewriting
is supported, meaning they can be used through WESL or naga_oil.
Mesh shader pipelines replace the standard vertex shader pipelines and allow new ways to render meshes.
They are ideal for meshlet rendering, a form of rendering where small groups of triangles are handled together,
for both culling and rendering.
They are compute-like shaders, and generate primitives which are passed directly to the rasterizer, rather
than having a list of vertices generated individually and then using a static index buffer. This means that certain computations
on nearby groups of triangles can be done together, the relationship between vertices and primitives is more programmable, and
you can even pass non-interpolated per-primitive data to the fragment shader, independent of vertices.
Mesh shaders are very versatile, and are powerful enough to replace vertex shaders, tesselation shaders, and geometry shaders
on their own or with task shaders.
A full example of mesh shaders in use can be seen in the mesh_shader example. For the full specification of mesh shaders in wgpu, go to docs/api-specs/mesh_shading.md. Below is a small snippet of shader code demonstrating their usage:
@task
@payload(taskPayload)
@workgroup_size(1)
fn ts_main() -> @builtin(mesh_task_size) vec3<u32> {
// Task shaders can use workgroup variables like compute shaders
workgroupData = 1.0;
// Pass some data to all mesh shaders dispatched by this workgroup
taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0);
taskPayload.visible = 1;
// Dispatch a mesh shader grid with one workgroup
return vec3(1, 1, 1);
}
@mesh(mesh_output)
@payload(taskPayload)
@workgroup_size(1)
fn ms_main(@builtin(local_invocation_index) index: u32, @builtin(global_invocation_id) id: vec3<u32>) {
// Set how many outputs this workgroup will generate
mesh_output.vertex_count = 3;
mesh_output.primitive_count = 1;
// Can also use workgroup variables
workgroupData = 2.0;
// Set vertex outputs
mesh_output.vertices[0].position = positions[0];
mesh_output.vertices[0].color = colors[0] * taskPayload.colorMask;
mesh_output.vertices[1].position = positions[1];
mesh_output.vertices[1].color = colors[1] * taskPayload.colorMask;
mesh_output.vertices[2].position = positions[2];
mesh_output.vertices[2].color = colors[2] * taskPayload.colorMask;
// Set the vertex indices for the only primitive
mesh_output.primitives[0].indices = vec3<u32>(0, 1, 2);
// Cull it if the data passed by the task shader says to
mesh_output.primitives[0].cull = taskPayload.visible == 1;
// Give a noninterpolated per-primitive vec4 to the fragment shader
mesh_output.primitives[0].colorMask = vec4<f32>(1.0, 0.0, 1.0, 1.0);
}Thanks
This was a monumental effort from many different people, but it was championed by @inner-daemons, without whom it would not have happened.
Thank you @cwfitzgerald for doing the bulk of the code review. Finally thank you @ColinTimBarndt for coordinating the testing effort.
Reviewers:
wgpu Contributions:
- Metal implementation in wgpu-hal. By @inner-daemons in #8139.
- DX12 implementation in wgpu-hal. By @inner-daemons in #8110.
- Vulkan implementation in wgpu-hal. By @inner-daemons in #7089.
- wgpu/wgpu-core implementation. By @inner-daemons in #7345.
- New mesh shader limits and validation. By @inner-daemons in #8507.
naga Contributions:
- Naga IR implementation. By @inner-daemons in #8104.
wgsl-inimplementation in naga. By @inner-daemons in #8370.spv-outimplementation in naga. By @inner-daemons in #8456.wgsl-outimplementation in naga. By @Slightlyclueless in #8481.- Allow barriers in mesh/task shaders. By @inner-daemons in #8749
Testing Assistance:
- @ColinTimBarndt
- @AdamK2003
- @Mhowser
- @9291Sam
- 3 more testers who wished to remain anonymous.
Thank you to everyone to made this happen!
Switch from gpu-alloc to gpu-allocator in the vulkan backend
gpu-allocator is the allocator used in the dx12 backend, allowing to configure
the allocator the same way in those two backends converging their behavior.
This also brings the Device::generate_allocator_report feature to
the vulkan backend.
wgpu::Instance::enumerate_adapters is now async & available on WebGPU
BREAKING CHANGE: enumerate_adapters is now async:
- pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
+ pub fn enumerate_adapters(&self, backends: Backends) -> impl Future<Output = Vec<Adapter>> {This yields two benefits:
- This method is now implemented on non-native using the standard
Adapter::request_adapter(…), makingenumerate_adaptersa portable surface. This was previously a nontrivial pain point when an application wanted to do some of its own filtering of adapters. - This method can now be implemented in custom backends.
By @R-Cramer4 in #8230
New LoadOp::DontCare
In the case where a renderpass unconditionally writes to all pixels in the rendertarget,
Load can cause unnecessary memory traffic, and Clear can spend time unnecessarily
clearing the rendertargets. DontCare is a new LoadOp which will leave the contents
of the rendertarget undefined. Because this could lead to undefined behavior, this API
requires that the user gives an unsafe token to use the api.
While you can use this unconditionally, on platforms where DontCare is not available,
it will internally use a different load op.
load: LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() })By @cwfitzgerald in #8549
MipmapFilterMode is split from FilterMode
This is a breaking change that aligns wgpu with spec.
SamplerDescriptor {
...
- mipmap_filter: FilterMode::Nearest
+ mipmap_filter: MipmapFilterMode::Nearest
...
}Multiview on all major platforms and support for multiview bitmasks
Multiview is a feature that allows rendering the same content to multiple layers of a texture.
This is useful primarily in VR where you wish to display almost identical content to 2 views,
just with a different perspective. Instead of using 2 draw calls or 2 instances for each object, you
can use this feature.
Multiview is also called view instancing in DX12 or vertex amplification in Metal.
Multiview has been reworked, adding support for Metal and DX12, and adding testing and validation to wgpu itself.
This change also introduces a view bitmask, a new field in RenderPassDescriptor that allows a render pass to render
to multiple non-adjacent layers when using the SELECTIVE_MULTIVIEW feature. If you don't use multi-view,
you can set this field to none.
- wgpu::RenderPassDescriptor {
- label: None,
- color_attachments: &color_attachments,
- depth_stencil_attachment: None,
- timestamp_writes: None,
- occlusion_query_set: None,
- }
+ wgpu::RenderPassDescriptor {
+ label: None,
+ color_attachments: &color_attachments,
+ depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
+ multiview_mask: NonZero::new(3),
+ }One other breaking change worth noting is that in WGSL @builtin(view_index) now requires a type of u32, where previously it required i32.
By @inner-daemons in #8206.
Error scopes now use guards and are thread-local.
- device.push_error_scope(wgpu::ErrorFilter::Validation);
+ let scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
// ... perform operations on the device ...
- let error: Option<Error> = device.pop_error_scope().await;
+ let error: Option<Error> = scope.pop().await;Device error scopes now operate on a per-thread basis. This allows them to be used easily within multithreaded contexts,
without having the error scope capture errors from other threads.
When the std feature is not enabled, we have no way to differentiate between threads, so error scopes return to be
global operations.
By @cwfitzgerald in #8685
Log Levels
We have received complaints about wgpu being way too log spammy at log levels info/warn/error. We have
adjusted our log policy and changed logging such that info and above should be silent unless some exceptional
event happens. Our new log policy is as follows:
- Error: if we can’t (for some reason, usually a bug) communicate an error any other way.
- Warning: similar, but there may be one-shot warnings about almost certainly sub-optimal.
- Info: do not use
- Debug: Used for interesting events happening inside wgpu.
- Trace: Used for all events that might be useful to either
wgpuor application...
v27.0.4
This release includes wgpu-hal version 27.0.4. All other crates remain at their previous versions.
Bug Fixes
General
- Remove fragile dependency constraint on
ordered-floatthat prevented semver-compatible changes above5.0.0. By @kpreid in #8371.
Vulkan
- Work around extremely poor frame pacing from AMD and Nvidia cards on Windows in
FifoandFifoRelaxedpresent modes. This is due to the drivers implicitly using a DXGI (Direct3D) swapchain to implement these modes and it having vastly different timing properties. See #8310 and #8354 for more information. By @cwfitzgerald in #8420.
v26.0.6
This release includes wgpu-hal version 26.0.6. All other crates remain at their previous versions.
Bug Fixes
Vulkan
- Work around extremely poor frame pacing from AMD and Nvidia cards on Windows in
FifoandFifoRelaxedpresent modes. This is due to the drivers implicitly using a DXGI (Direct3D) swapchain to implement these modes and it having vastly different timing properties. See #8310 and #8354 for more information. By @cwfitzgerald in #8420.
v27.0.3
This release includes naga, wgpu-core and wgpu-hal version 27.0.3. All other crates remain at their previous versions.
Bug Fixes
naga
- Fix a bug that resulted in the Metal error
program scope variable must reside in constant address spacein some cases. Backport of #8311 by @teoxoy.
General
- Remove an assertion that causes problems if
CommandEncoder::as_hal_mutis used. By @andyleiserson in #8387.
DX12
- Align copies b/w textures and buffers via a single intermediate buffer per copy when
D3D12_FEATURE_DATA_D3D12_OPTIONS13.UnrestrictedBufferTextureCopyPitchSupportedisfalse. By @ErichDonGubler in #7721, backported in #8374.
v26.0.5
This release includes wgpu-hal version 26.0.5. All other crates remain at their previous versions.
Bug Fixes
DX12
- Align copies b/w textures and buffers via a single intermediate buffer per copy when
D3D12_FEATURE_DATA_D3D12_OPTIONS13.UnrestrictedBufferTextureCopyPitchSupportedisfalse. By @ErichDonGubler in #7721, backported in #8375.
v27.0.2
This release includes wgpu-hal version 27.0.2. All other crates remain at their previous versions.
Bug Fixes
DX12
- Fix device creation failures for devices that do not support mesh shaders. By @vorporeal in #8297.
v27.0.1
This release includes wgpu, wgpu-core, wgpu-hal, and wgpu-types version 27.0.1. All other crates remain at their previous versions.
Bug Fixes
- Fixed the build on docs.rs. By @cwfitzgerald in #8292.