[d3d12] get vertex_index & instance_index builtins working for indirect draws

This commit is contained in:
teoxoy 2025-04-14 16:34:51 +02:00 committed by Teodor Tanasoaia
parent 3805768cb4
commit 597114052e
5 changed files with 99 additions and 21 deletions

View File

@ -268,6 +268,7 @@ impl Device {
raw_device.as_ref(),
&desc.required_limits,
&desc.required_features,
adapter.backend(),
)?)
} else {
None

View File

@ -62,6 +62,7 @@ impl Draw {
pub(super) fn new(
device: &dyn hal::DynDevice,
required_features: &wgt::Features,
backend: wgt::Backend,
) -> Result<Self, CreateIndirectValidationPipelineError> {
let module = create_validation_module(device)?;
@ -92,11 +93,13 @@ impl Draw {
let supports_indirect_first_instance =
required_features.contains(wgt::Features::INDIRECT_FIRST_INSTANCE);
let write_d3d12_special_constants = backend == wgt::Backend::Dx12;
let pipeline = create_validation_pipeline(
device,
module.as_ref(),
pipeline_layout.as_ref(),
supports_indirect_first_instance,
write_d3d12_special_constants,
)?;
Ok(Self {
@ -523,6 +526,7 @@ fn create_validation_pipeline(
module: &dyn hal::DynShaderModule,
pipeline_layout: &dyn hal::DynPipelineLayout,
supports_indirect_first_instance: bool,
write_d3d12_special_constants: bool,
) -> Result<Box<dyn hal::DynComputePipeline>, CreateIndirectValidationPipelineError> {
let pipeline_desc = hal::ComputePipelineDescriptor {
label: None,
@ -530,10 +534,16 @@ fn create_validation_pipeline(
stage: hal::ProgrammableStage {
module,
entry_point: "main",
constants: &hashbrown::HashMap::from([(
"supports_indirect_first_instance".to_string(),
f64::from(supports_indirect_first_instance),
)]),
constants: &hashbrown::HashMap::from([
(
"supports_indirect_first_instance".to_string(),
f64::from(supports_indirect_first_instance),
),
(
"write_d3d12_special_constants".to_string(),
f64::from(write_d3d12_special_constants),
),
]),
zero_initialize_workgroup_memory: false,
},
cache: None,
@ -913,7 +923,13 @@ impl DrawBatcher {
vertex_or_index_limit: u64,
instance_limit: u64,
) -> Result<(usize, u64), DeviceError> {
let stride = crate::command::get_stride_of_indirect_args(indexed);
// space for D3D12 special constants
let extra = if device.backend() == wgt::Backend::Dx12 {
3 * size_of::<u32>() as u64
} else {
0
};
let stride = extra + crate::command::get_stride_of_indirect_args(indexed);
let (dst_resource_index, dst_offset) = indirect_draw_validation_resources
.get_dst_subrange(stride, &mut self.current_dst_entry)?;

View File

@ -33,6 +33,7 @@ impl IndirectValidation {
device: &dyn hal::DynDevice,
required_limits: &wgt::Limits,
required_features: &wgt::Features,
backend: wgt::Backend,
) -> Result<Self, DeviceError> {
let dispatch = match Dispatch::new(device, required_limits) {
Ok(dispatch) => dispatch,
@ -41,7 +42,7 @@ impl IndirectValidation {
return Err(DeviceError::Lost);
}
};
let draw = match Draw::new(device, required_features) {
let draw = match Draw::new(device, required_features, backend) {
Ok(draw) => draw,
Err(e) => {
log::error!("indirect-draw-validation error: {e:?}");

View File

@ -1,4 +1,5 @@
override supports_indirect_first_instance: bool;
override write_d3d12_special_constants: bool;
struct MetadataEntry {
// bits 0..30 are an offset into `src`
@ -66,21 +67,32 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3u) {
failed |= first_instance != 0u;
}
let dst_offset = select(0u, 3u, write_d3d12_special_constants);
if failed {
dst[dst_base_offset + 0] = 0u;
dst[dst_base_offset + 1] = 0u;
dst[dst_base_offset + 2] = 0u;
dst[dst_base_offset + 3] = 0u;
if write_d3d12_special_constants {
dst[dst_base_offset + 0] = 0u;
dst[dst_base_offset + 1] = 0u;
dst[dst_base_offset + 2] = 0u;
}
dst[dst_base_offset + dst_offset + 0] = 0u;
dst[dst_base_offset + dst_offset + 1] = 0u;
dst[dst_base_offset + dst_offset + 2] = 0u;
dst[dst_base_offset + dst_offset + 3] = 0u;
if (is_indexed) {
dst[dst_base_offset + 4] = 0u;
dst[dst_base_offset + dst_offset + 4] = 0u;
}
} else {
dst[dst_base_offset + 0] = src[src_base_offset + 0];
dst[dst_base_offset + 1] = src[src_base_offset + 1];
dst[dst_base_offset + 2] = src[src_base_offset + 2];
dst[dst_base_offset + 3] = src[src_base_offset + 3];
if write_d3d12_special_constants {
dst[dst_base_offset + 0] = src[src_base_offset + 2 + u32(is_indexed)];
dst[dst_base_offset + 1] = src[src_base_offset + 3 + u32(is_indexed)];
dst[dst_base_offset + 2] = 0u;
}
dst[dst_base_offset + dst_offset + 0] = src[src_base_offset + 0];
dst[dst_base_offset + dst_offset + 1] = src[src_base_offset + 1];
dst[dst_base_offset + dst_offset + 2] = src[src_base_offset + 2];
dst[dst_base_offset + dst_offset + 3] = src[src_base_offset + 3];
if (is_indexed) {
dst[dst_base_offset + 4] = src[src_base_offset + 4];
dst[dst_base_offset + dst_offset + 4] = src[src_base_offset + 4];
}
}
}

View File

@ -104,7 +104,7 @@ impl super::CommandEncoder {
self.pass.clear();
}
unsafe fn prepare_draw(&mut self, first_vertex: i32, first_instance: u32) {
unsafe fn prepare_vertex_buffers(&mut self) {
while self.pass.dirty_vertex_buffers != 0 {
let list = self.list.as_ref().unwrap();
let index = self.pass.dirty_vertex_buffers.trailing_zeros();
@ -116,6 +116,12 @@ impl super::CommandEncoder {
);
}
}
}
unsafe fn prepare_draw(&mut self, first_vertex: i32, first_instance: u32) {
unsafe {
self.prepare_vertex_buffers();
}
if let Some(root_index) = self
.pass
.layout
@ -1195,10 +1201,31 @@ impl crate::CommandEncoder for super::CommandEncoder {
offset: wgt::BufferAddress,
draw_count: u32,
) {
unsafe { self.prepare_draw(0, 0) };
if self
.pass
.layout
.special_constants
.as_ref()
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
.is_some()
{
unsafe { self.prepare_vertex_buffers() };
self.update_root_elements();
} else {
unsafe { self.prepare_draw(0, 0) };
}
let cmd_signature = &self
.pass
.layout
.special_constants
.as_ref()
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
.unwrap_or_else(|| &self.shared.cmd_signatures)
.draw;
unsafe {
self.list.as_ref().unwrap().ExecuteIndirect(
&self.shared.cmd_signatures.draw,
cmd_signature,
draw_count,
&buffer.resource,
offset,
@ -1213,10 +1240,31 @@ impl crate::CommandEncoder for super::CommandEncoder {
offset: wgt::BufferAddress,
draw_count: u32,
) {
unsafe { self.prepare_draw(0, 0) };
if self
.pass
.layout
.special_constants
.as_ref()
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
.is_some()
{
unsafe { self.prepare_vertex_buffers() };
self.update_root_elements();
} else {
unsafe { self.prepare_draw(0, 0) };
}
let cmd_signature = &self
.pass
.layout
.special_constants
.as_ref()
.and_then(|sc| sc.indirect_cmd_signatures.as_ref())
.unwrap_or_else(|| &self.shared.cmd_signatures)
.draw_indexed;
unsafe {
self.list.as_ref().unwrap().ExecuteIndirect(
&self.shared.cmd_signatures.draw_indexed,
cmd_signature,
draw_count,
&buffer.resource,
offset,