Skip to content

Commit 54b96a1

Browse files
committed
Fix bad debug_assert in dx12 indirect multi draw
1 parent e14050d commit 54b96a1

6 files changed

Lines changed: 68 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Bottom level categories:
8989
#### dx12
9090

9191
- Fixed use of a texture view without `TextureUsage::TEXTURE_BINDING` as a read-only depth attachment. By @andyleiserson in [#9346](https://github.com/gfx-rs/wgpu/pull/9346).
92+
- Fixed a `debug_assert` during stride validation for indirect multi draw. By @kristoff3r in [#9332](https://github.com/gfx-rs/wgpu/pull/9332)
9293

9394
## v29.0.1 (2026-03-26)
9495

tests/tests/wgpu-gpu/draw_indirect.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
2727
INSTANCED_INDEXED_DRAW_OOB_INSTANCE_START,
2828
INSTANCED_INDEXED_DRAW_OOB_INSTANCE_COUNT,
2929
INDIRECT_BUFFER_OFFSETS,
30+
MULTI_DRAW_INDEXED_INDIRECT,
31+
MULTI_DRAW_INDIRECT,
3032
]);
3133
}
3234

@@ -115,6 +117,15 @@ impl TestData {
115117
}
116118

117119
async fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) {
120+
run_test_inner(ctx, test_data, expect_noop, false).await;
121+
}
122+
123+
async fn run_test_inner(
124+
ctx: TestingContext,
125+
test_data: TestData,
126+
expect_noop: bool,
127+
use_multi_draw: bool,
128+
) {
118129
let mut vertex_buffer_layouts = Vec::new();
119130
vertex_buffer_layouts.push(wgpu::VertexBufferLayout {
120131
array_stride: 8,
@@ -283,15 +294,23 @@ async fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) {
283294
if let Some(ref index_buffer) = index_buffer {
284295
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
285296
}
286-
for draw_index in 0..draws {
297+
if use_multi_draw {
287298
if index_buffer.is_some() {
288-
let offset = pass_index * draw_index * 20;
289-
rpass.draw_indexed_indirect(&indirect_buffer, offset);
290-
rpass.draw_indexed_indirect(&indirect_buffer2, offset);
299+
rpass.multi_draw_indexed_indirect(&indirect_buffer, 0, draws);
291300
} else {
292-
let offset = pass_index * draw_index * 20;
293-
rpass.draw_indirect(&indirect_buffer, offset);
294-
rpass.draw_indirect(&indirect_buffer2, offset);
301+
rpass.multi_draw_indirect(&indirect_buffer, 0, draws);
302+
}
303+
} else {
304+
for draw_index in 0..draws {
305+
if index_buffer.is_some() {
306+
let offset = (pass_index * draw_index * 20) as u64;
307+
rpass.draw_indexed_indirect(&indirect_buffer, offset);
308+
rpass.draw_indexed_indirect(&indirect_buffer2, offset);
309+
} else {
310+
let offset = (pass_index * draw_index * 20) as u64;
311+
rpass.draw_indirect(&indirect_buffer, offset);
312+
rpass.draw_indirect(&indirect_buffer2, offset);
313+
}
295314
}
296315
}
297316
}
@@ -796,3 +815,21 @@ async fn indirect_buffer_offsets(ctx: TestingContext) {
796815
data[..half].iter().all(|b| *b == u8::MAX) && data[half..].iter().all(|b| *b == 0);
797816
assert!(succeeded);
798817
}
818+
819+
#[gpu_test]
820+
static MULTI_DRAW_INDEXED_INDIRECT: GpuTestConfiguration = GpuTestConfiguration::new()
821+
.parameters(
822+
TestParameters::default()
823+
.downlevel_flags(wgpu::DownlevelFlags::INDIRECT_EXECUTION)
824+
.limits(wgpu::Limits::downlevel_defaults()),
825+
)
826+
.run_async(|ctx| run_test_inner(ctx, get_indexed_draw_test_data(0, 6), false, true));
827+
828+
#[gpu_test]
829+
static MULTI_DRAW_INDIRECT: GpuTestConfiguration = GpuTestConfiguration::new()
830+
.parameters(
831+
TestParameters::default()
832+
.downlevel_flags(wgpu::DownlevelFlags::INDIRECT_EXECUTION)
833+
.limits(wgpu::Limits::downlevel_defaults()),
834+
)
835+
.run_async(|ctx| run_test_inner(ctx, get_draw_test_data(0, 6), false, true));

wgpu-core/src/command/bundle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ fn multi_draw_indirect(
894894

895895
let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);
896896

897-
let stride = super::get_stride_of_indirect_args(family);
897+
let stride = super::get_src_stride_of_indirect_args(family);
898898
state
899899
.buffer_memory_init_actions
900900
.extend(buffer.initialization_status.read().create_action(

wgpu-core/src/command/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub(crate) use self::{
7171
clear::clear_texture,
7272
encoder::EncodingState,
7373
memory_init::CommandBufferTextureMemoryActions,
74-
render::{get_stride_of_indirect_args, VertexLimits},
74+
render::{get_dst_stride_of_indirect_args, get_src_stride_of_indirect_args, VertexLimits},
7575
transfer::{
7676
extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
7777
validate_texture_copy_dst_format, validate_texture_copy_range,

wgpu-core/src/command/render.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,7 +2799,7 @@ fn multi_draw_indirect(
27992799
return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
28002800
}
28012801

2802-
let stride = get_stride_of_indirect_args(family);
2802+
let stride = get_src_stride_of_indirect_args(family);
28032803

28042804
let end_offset = offset + stride * count as u64;
28052805
if end_offset > indirect_buffer.size {
@@ -2917,9 +2917,12 @@ fn multi_draw_indirect(
29172917
let draw_data = draw_ctx.add(offset + stride * i as u64)?;
29182918

29192919
if draw_data.buffer_index == current_draw_data.buffer_index {
2920+
#[cfg(debug_assertions)]
2921+
let dst_stride =
2922+
get_dst_stride_of_indirect_args(state.pass.base.device.backend(), family);
29202923
debug_assert_eq!(
29212924
draw_data.offset,
2922-
current_draw_data.offset + stride * current_draw_data.count as u64
2925+
current_draw_data.offset + dst_stride * current_draw_data.count as u64
29232926
);
29242927
current_draw_data.count += 1;
29252928
} else {
@@ -2971,7 +2974,7 @@ fn multi_draw_indirect_count(
29712974
validate_mesh_draw_multiview(state)?;
29722975
}
29732976

2974-
let stride = get_stride_of_indirect_args(family);
2977+
let stride = get_src_stride_of_indirect_args(family);
29752978

29762979
state
29772980
.pass
@@ -3840,10 +3843,23 @@ impl Global {
38403843
}
38413844
}
38423845

3843-
pub(crate) const fn get_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {
3846+
pub(crate) const fn get_src_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {
38443847
match family {
38453848
DrawCommandFamily::Draw => size_of::<wgt::DrawIndirectArgs>() as u64,
38463849
DrawCommandFamily::DrawIndexed => size_of::<wgt::DrawIndexedIndirectArgs>() as u64,
38473850
DrawCommandFamily::DrawMeshTasks => size_of::<wgt::DispatchIndirectArgs>() as u64,
38483851
}
38493852
}
3853+
3854+
pub(crate) const fn get_dst_stride_of_indirect_args(
3855+
backend: wgt::Backend,
3856+
family: DrawCommandFamily,
3857+
) -> u64 {
3858+
// space for D3D12 special constants
3859+
let extra = if matches!(backend, wgt::Backend::Dx12) {
3860+
3 * size_of::<u32>() as u64
3861+
} else {
3862+
0
3863+
};
3864+
extra + get_src_stride_of_indirect_args(family)
3865+
}

wgpu-core/src/indirect_validation/draw.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -984,13 +984,7 @@ impl DrawBatcher {
984984
vertex_or_index_limit: u64,
985985
instance_limit: u64,
986986
) -> Result<(usize, u64), DeviceError> {
987-
// space for D3D12 special constants
988-
let extra = if device.backend() == wgt::Backend::Dx12 {
989-
3 * size_of::<u32>() as u64
990-
} else {
991-
0
992-
};
993-
let stride = extra + crate::command::get_stride_of_indirect_args(family);
987+
let stride = crate::command::get_dst_stride_of_indirect_args(device.backend(), family);
994988

995989
let (dst_resource_index, dst_offset) = indirect_draw_validation_resources
996990
.get_dst_subrange(stride, &mut self.current_dst_entry)?;

0 commit comments

Comments
 (0)