Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions examples/features/src/ray_shadows/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ var<uniform> uniforms: Uniforms;
@group(0) @binding(1)
var acc_struct: acceleration_structure;

var<push_constant> light: vec3<f32>;
struct PushConstants {
light: vec3<f32>,
}
var<push_constant> pc: PushConstants;

const SURFACE_BRIGHTNESS = 0.5;

Expand All @@ -45,7 +48,7 @@ fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
let d = vertex.tex_coords * 2.0 - 1.0;

let origin = vertex.world_position;
let direction = normalize(light - vertex.world_position);
let direction = normalize(pc.light - vertex.world_position);

var normal: vec3<f32>;
let dir_cam = normalize(camera - vertex.world_position);
Expand Down
26 changes: 3 additions & 23 deletions tests/tests/wgpu-gpu/vertex_indices/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,32 +183,12 @@ impl Test {
.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE)
|| !is_indirect;

// If this is false, it won't be ignored, but it won't show up in the shader
//
// If the IdSource is buffers, this doesn't apply
let first_vert_instance_supported = ctx.adapter_downlevel_capabilities.flags.contains(
wgpu::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW,
) || matches!(self.id_source, IdSource::Buffers)
|| !is_indirect;

match self.case {
TestCase::DrawBaseVertex => {
if !first_vert_instance_supported {
return &[0, 1, 2, 3, 4, 5];
}

&[0, 0, 0, 3, 4, 5, 6, 7, 8]
}
TestCase::DrawBaseVertex => &[0, 0, 0, 3, 4, 5, 6, 7, 8],
TestCase::Draw | TestCase::DrawInstanced => &[0, 1, 2, 3, 4, 5],
TestCase::DrawNonZeroFirstVertex => {
if !first_vert_instance_supported {
return &[0, 1, 2, 0, 0, 0];
}

&[0, 1, 2, 3, 4, 5]
}
TestCase::DrawNonZeroFirstVertex => &[0, 1, 2, 3, 4, 5],
TestCase::DrawNonZeroFirstInstance => {
if !first_vert_instance_supported || !non_zero_first_instance_supported {
if !non_zero_first_instance_supported {
return &[0, 1, 2, 0, 0, 0];
}

Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/device/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ impl Device {
raw_device.as_ref(),
&desc.required_limits,
&desc.required_features,
adapter.backend(),
)?)
} else {
None
Expand Down
26 changes: 21 additions & 5 deletions wgpu-core/src/indirect_validation/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -523,17 +526,24 @@ 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,
layout: pipeline_layout,
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,
Expand Down Expand Up @@ -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)?;
Expand Down
3 changes: 2 additions & 1 deletion wgpu-core/src/indirect_validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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:?}");
Expand Down
32 changes: 22 additions & 10 deletions wgpu-core/src/indirect_validation/validate_draw.wgsl
Original file line number Diff line number Diff line change
@@ -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`
Expand Down Expand Up @@ -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];
}
}
}
5 changes: 1 addition & 4 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,7 @@ impl super::Adapter {

let base = wgt::Limits::default();

let mut downlevel = wgt::DownlevelCapabilities::default();
// https://github.com/gfx-rs/wgpu/issues/2471
downlevel.flags -=
wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
let downlevel = wgt::DownlevelCapabilities::default();

// See https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels#feature-level-support
let max_color_attachments = 8;
Expand Down
58 changes: 53 additions & 5 deletions wgpu-hal/src/dx12/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,7 @@ impl super::Adapter {
let mut downlevel_flags = wgt::DownlevelFlags::empty()
| wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
| wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES
| wgt::DownlevelFlags::COMPARISON_SAMPLERS
| wgt::DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
| wgt::DownlevelFlags::COMPARISON_SAMPLERS;
downlevel_flags.set(wgt::DownlevelFlags::COMPUTE_SHADERS, supports_compute);
downlevel_flags.set(
wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
Expand Down
2 changes: 0 additions & 2 deletions wgpu-hal/src/gles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ in shaders, getting buffers and builtins to work correctly is a bit tricky.
We never emulate `base_vertex` and gl_VertexID behaves as `@builtin(vertex_index)` does, so we
never need to do anything about that.

We always advertise support for `VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW`.

### GL 4.2+ with ARB_shader_draw_parameters

- `@builtin(instance_index)` translates to `gl_InstanceID + gl_BaseInstance`
Expand Down
3 changes: 1 addition & 2 deletions wgpu-hal/src/vulkan/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,7 @@ impl PhysicalDeviceFeatures {
| Df::INDIRECT_EXECUTION
| Df::VIEW_FORMATS
| Df::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES
| Df::NONBLOCKING_QUERY_RESOLVE
| Df::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW;
| Df::NONBLOCKING_QUERY_RESOLVE;

dl_flags.set(
Df::SURFACE_VIEW_FORMATS,
Expand Down
20 changes: 20 additions & 0 deletions wgpu-types/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ bitflags::bitflags! {

/// Validate indirect buffer content prior to issuing indirect draws/dispatches.
///
/// This validation will transform indirect calls into no-ops if they are not valid:
///
/// - When calling `dispatch_workgroups_indirect`, all 3 indirect arguments encoded in the buffer
/// must be less than the `max_compute_workgroups_per_dimension` device limit.
/// - When calling `draw_indirect`/`draw_indexed_indirect`/`multi_draw_indirect`/`multi_draw_indexed_indirect`:
/// - If `Features::INDIRECT_FIRST_INSTANCE` is not enabled on the device, the `first_instance` indirect argument must be 0.
/// - The `first_instance` & `instance_count` indirect arguments must form a range that fits within all bound vertex buffers with `step_mode` set to `Instance`.
/// - When calling `draw_indirect`/`multi_draw_indirect`:
/// - The `first_vertex` & `vertex_count` indirect arguments must form a range that fits within all bound vertex buffers with `step_mode` set to `Vertex`.
/// - When calling `draw_indexed_indirect`/`multi_draw_indexed_indirect`:
/// - The `first_index` & `index_count` indirect arguments must form a range that fits within the bound index buffer.
///
/// __Behavior is undefined if this validation is disabled and the rules above are not satisfied.__
///
/// Disabling this will also cause the following built-ins to not report the right values on the D3D12 backend:
///
/// - the 3 components of `@builtin(num_workgroups)` will be 0
/// - the value of `@builtin(vertex_index)` will not take into account the value of the `first_vertex`/`base_vertex` argument present in the indirect buffer
/// - the value of `@builtin(instance_index)` will not take into account the value of the `first_instance` argument present in the indirect buffer
///
/// When `Self::from_env()` is used takes value from `WGPU_VALIDATION_INDIRECT_CALL` environment variable.
const VALIDATION_INDIRECT_CALL = 1 << 5;

Expand Down
28 changes: 0 additions & 28 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,34 +1079,6 @@ bitflags::bitflags! {
/// Not Supported by:
/// - GL ES / WebGL
const NONBLOCKING_QUERY_RESOLVE = 1 << 22;

/// If this is true, use of `@builtin(vertex_index)` and `@builtin(instance_index)` will properly take into consideration
/// the `first_vertex` and `first_instance` parameters of indirect draw calls.
///
/// If this is false, `@builtin(vertex_index)` and `@builtin(instance_index)` will start by counting from 0, ignoring the
/// `first_vertex` and `first_instance` parameters.
///
/// For example, if you had a draw call like this:
/// - `first_vertex: 4,`
/// - `vertex_count: 12,`
///
/// When this flag is present, `@builtin(vertex_index)` will start at 4 and go up to 15 (12 invocations).
///
/// When this flag is not present, `@builtin(vertex_index)` will start at 0 and go up to 11 (12 invocations).
///
/// This only affects the builtins in the shaders,
/// vertex buffers and instance rate vertex buffers will behave like expected with this flag disabled.
///
/// See also [`Features::`]
///
/// Supported By:
/// - Vulkan
/// - Metal
/// - OpenGL
///
/// Will be implemented in the future by:
/// - DX12 ([#2471](https://github.com/gfx-rs/wgpu/issues/2471))
const VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW = 1 << 23;
}
}

Expand Down
Loading