Remove MULTI_DRAW_INDIRECT feature (#8162)

This commit is contained in:
Connor Fitzgerald 2025-08-28 12:00:14 -04:00 committed by GitHub
parent 97ba77d668
commit 2ff46cd7cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 54 additions and 60 deletions

View File

@ -74,6 +74,16 @@ Difference for SPIR-V passthrough:
This allows using precompiled shaders without manually checking which backend's code to pass, for example if you have shaders precompiled for both DXIL and SPIR-V.
#### Multi-draw indirect is now unconditionally supported when indirect draws are supported
We have removed `Features::MULTI_DRAW_INDIRECT` as it was unconditionally available on all platforms.
`RenderPass::multi_draw_indirect` is now available if the device supports downlevel flag `DownlevelFlags::INDIRECT_EXECUTION`.
If you are using spirv-passthrough with multi-draw indirect and `gl_DrawID`, you can know if `MULTI_DRAW_INDIRECT` is being emulated
by if the `Feature::MULTI_DRAW_INDIRECT_COUNT` feature is available on the device, this feature cannot be emulated efficicently.
By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162).
### New Features
#### General

View File

@ -399,8 +399,6 @@ pub enum GPUFeatureName {
UniformBufferBindingArrays,
#[webidl(rename = "partially-bound-binding-array")]
PartiallyBoundBindingArray,
#[webidl(rename = "multi-draw-indirect")]
MultiDrawIndirect,
#[webidl(rename = "multi-draw-indirect-count")]
MultiDrawIndirectCount,
#[webidl(rename = "push-constants")]
@ -470,7 +468,6 @@ pub fn feature_names_to_features(names: Vec<GPUFeatureName>) -> wgpu_types::Feat
GPUFeatureName::StorageTextureArrayNonUniformIndexing => Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
GPUFeatureName::UniformBufferBindingArrays => Features::UNIFORM_BUFFER_BINDING_ARRAYS,
GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY,
GPUFeatureName::MultiDrawIndirect => Features::MULTI_DRAW_INDIRECT,
GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT,
GPUFeatureName::PushConstants => Features::PUSH_CONSTANTS,
GPUFeatureName::AddressModeClampToZero => Features::ADDRESS_MODE_CLAMP_TO_ZERO,
@ -593,9 +590,6 @@ pub fn features_to_feature_names(features: wgpu_types::Features) -> HashSet<GPUF
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
return_features.insert(PartiallyBoundBindingArray);
}
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
return_features.insert(MultiDrawIndirect);
}
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
return_features.insert(MultiDrawIndirectCount);
}

View File

@ -269,8 +269,9 @@ fn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration {
wgpu::Features::EXPERIMENTAL_MESH_SHADER
| wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS
| match draw_type {
DrawType::Standard | DrawType::Indirect => wgpu::Features::empty(),
DrawType::MultiIndirect => wgpu::Features::MULTI_DRAW_INDIRECT,
DrawType::Standard | DrawType::Indirect | DrawType::MultiIndirect => {
wgpu::Features::empty()
}
DrawType::MultiIndirectCount => wgpu::Features::MULTI_DRAW_INDIRECT_COUNT,
},
)

View File

@ -2696,13 +2696,6 @@ fn multi_draw_indirect(
state.is_ready(family)?;
if count != 1 {
state
.general
.device
.require_features(wgt::Features::MULTI_DRAW_INDIRECT)?;
}
state
.general
.device

View File

@ -343,7 +343,6 @@ impl super::Adapter {
| wgt::Features::DEPTH32FLOAT_STENCIL8
| wgt::Features::INDIRECT_FIRST_INSTANCE
| wgt::Features::MAPPABLE_PRIMARY_BUFFERS
| wgt::Features::MULTI_DRAW_INDIRECT
| wgt::Features::MULTI_DRAW_INDIRECT_COUNT
| wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER
| wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO

View File

@ -479,8 +479,6 @@ impl super::Adapter {
wgt::Features::SHADER_EARLY_DEPTH_TEST,
supported((3, 1), (4, 2)) || extensions.contains("GL_ARB_shader_image_load_store"),
);
// We emulate MDI with a loop of draw calls.
features.set(wgt::Features::MULTI_DRAW_INDIRECT, indirect_execution);
if extensions.contains("GL_ARB_timer_query") {
features.set(wgt::Features::TIMESTAMP_QUERY, true);
features.set(wgt::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, true);

View File

@ -931,10 +931,7 @@ impl super::PrivateCapabilities {
| F::EXTERNAL_TEXTURE;
features.set(F::FLOAT32_FILTERABLE, self.supports_float_filtering);
features.set(
F::INDIRECT_FIRST_INSTANCE | F::MULTI_DRAW_INDIRECT,
self.indirect_draw_dispatch,
);
features.set(F::INDIRECT_FIRST_INSTANCE, self.indirect_draw_dispatch);
features.set(
F::TIMESTAMP_QUERY | F::TIMESTAMP_QUERY_INSIDE_ENCODERS,
self.timestamp_query_support

View File

@ -266,9 +266,7 @@ impl PhysicalDeviceFeatures {
requested_features.contains(wgt::Features::INDIRECT_FIRST_INSTANCE),
)
//.dual_src_blend(requested_features.contains(wgt::Features::DUAL_SRC_BLENDING))
.multi_draw_indirect(
requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT),
)
.multi_draw_indirect(phd_features.core.multi_draw_indirect != 0)
.fill_mode_non_solid(requested_features.intersects(
wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT,
))
@ -602,7 +600,6 @@ impl PhysicalDeviceFeatures {
self.core.draw_indirect_first_instance != 0,
);
//if self.core.dual_src_blend != 0
features.set(F::MULTI_DRAW_INDIRECT, self.core.multi_draw_indirect != 0);
features.set(F::POLYGON_MODE_LINE, self.core.fill_mode_non_solid != 0);
features.set(F::POLYGON_MODE_POINT, self.core.fill_mode_non_solid != 0);
//if self.core.depth_bounds != 0 {
@ -1775,6 +1772,7 @@ impl super::Instance {
vk::ImageTiling::OPTIMAL,
depth_stencil_required_flags(),
),
multi_draw_indirect: phd_features.core.multi_draw_indirect != 0,
non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1,
can_present: true,
//TODO: make configurable

View File

@ -1146,15 +1146,29 @@ impl crate::CommandEncoder for super::CommandEncoder {
offset: wgt::BufferAddress,
draw_count: u32,
) {
unsafe {
self.device.raw.cmd_draw_indexed_indirect(
self.active,
buffer.raw,
offset,
draw_count,
size_of::<wgt::DrawIndexedIndirectArgs>() as u32,
)
};
if draw_count >= 1 && self.device.private_caps.multi_draw_indirect {
unsafe {
self.device.raw.cmd_draw_indexed_indirect(
self.active,
buffer.raw,
offset,
draw_count,
size_of::<wgt::DrawIndexedIndirectArgs>() as u32,
)
};
} else {
for _ in 0..draw_count {
unsafe {
self.device.raw.cmd_draw_indexed_indirect(
self.active,
buffer.raw,
offset,
1,
size_of::<wgt::DrawIndexedIndirectArgs>() as u32,
)
};
}
}
}
unsafe fn draw_mesh_tasks_indirect(
&mut self,

View File

@ -575,6 +575,7 @@ struct PrivateCapabilities {
/// Ability to present contents to any screen. Only needed to work around broken platform configurations.
can_present: bool,
non_coherent_map_mask: wgt::BufferAddress,
multi_draw_indirect: bool,
/// True if this adapter advertises the [`robustBufferAccess`][vrba] feature.
///

View File

@ -784,28 +784,11 @@ bitflags_array! {
///
/// This is a native only feature.
const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 13;
/// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`].
///
/// Allows multiple indirect calls to be dispatched from a single buffer.
///
/// Natively Supported Platforms:
/// - DX12
/// - Vulkan
///
/// Emulated Platforms:
/// - Metal
/// - OpenGL
/// - WebGPU
///
/// Emulation is preformed by looping over the individual indirect draw calls in the backend. This is still significantly
/// faster than enulating it yourself, as wgpu only does draw call validation once.
///
/// [`RenderPass::multi_draw_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indirect
/// [`RenderPass::multi_draw_indexed_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indexed_indirect
const MULTI_DRAW_INDIRECT = 1 << 14;
/// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`].
///
/// This allows the use of a buffer containing the actual number of draw calls.
/// This allows the use of a buffer containing the actual number of draw calls. This feature being present also implies
/// that all calls to [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`] are not being emulated
/// with a series of `draw_indirect` calls.
///
/// Supported platforms:
/// - DX12
@ -813,6 +796,8 @@ bitflags_array! {
///
/// This is a native only feature.
///
/// [`RenderPass::multi_draw_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indirect
/// [`RenderPass::multi_draw_indexed_indirect`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indexed_indirect
/// [`RenderPass::multi_draw_indirect_count`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indirect_count
/// [`RenderPass::multi_draw_indexed_indirect_count`]: ../wgpu/struct.RenderPass.html#method.multi_draw_indexed_indirect_count
const MULTI_DRAW_INDIRECT_COUNT = 1 << 15;

View File

@ -236,6 +236,8 @@ impl RenderPass<'_> {
///
/// This is like calling [`RenderPass::draw`] but the contents of the call are specified in the `indirect_buffer`.
/// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).
///
/// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].
pub fn draw_indirect(&mut self, indirect_buffer: &Buffer, indirect_offset: BufferAddress) {
self.inner
.draw_indirect(&indirect_buffer.inner, indirect_offset);
@ -246,6 +248,8 @@ impl RenderPass<'_> {
///
/// This is like calling [`RenderPass::draw_indexed`] but the contents of the call are specified in the `indirect_buffer`.
/// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).
///
/// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].
pub fn draw_indexed_indirect(
&mut self,
indirect_buffer: &Buffer,
@ -287,10 +291,7 @@ impl RenderPass<'_> {
self.inner.execute_bundles(&mut render_bundles);
}
}
/// [`Features::MULTI_DRAW_INDIRECT`] must be enabled on the device in order to call these functions.
impl RenderPass<'_> {
/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`.
/// `count` draw calls are issued.
///
@ -299,6 +300,8 @@ impl RenderPass<'_> {
/// The structure expected in `indirect_buffer` must conform to [`DrawIndirectArgs`](crate::util::DrawIndirectArgs).
/// These draw structures are expected to be tightly packed.
///
/// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].
///
/// This drawing command uses the current render state, as set by preceding `set_*()` methods.
/// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indirect(
@ -320,6 +323,8 @@ impl RenderPass<'_> {
/// The structure expected in `indirect_buffer` must conform to [`DrawIndexedIndirectArgs`](crate::util::DrawIndexedIndirectArgs).
/// These draw structures are expected to be tightly packed.
///
/// Calling this requires the device support [`DownlevelFlags::INDIRECT_EXECUTION`].
///
/// This drawing command uses the current render state, as set by preceding `set_*()` methods.
/// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indexed_indirect(

View File

@ -772,8 +772,7 @@ const FEATURES_MAPPING: [(wgt::Features, webgpu_sys::GpuFeatureName); 15] = [
];
fn map_wgt_features(supported_features: webgpu_sys::GpuSupportedFeatures) -> wgt::Features {
// We emulate MDI.
let mut features = wgt::Features::MULTI_DRAW_INDIRECT;
let mut features = wgt::Features::empty();
for (wgpu_feat, web_feat) in FEATURES_MAPPING {
match wasm_bindgen::JsValue::from(web_feat).as_string() {
Some(value) if supported_features.has(&value) => features |= wgpu_feat,