diff --git a/CHANGELOG.md b/CHANGELOG.md index c195abde1..31a05b941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/deno_webgpu/webidl.rs b/deno_webgpu/webidl.rs index 75a19d372..2f521f47f 100644 --- a/deno_webgpu/webidl.rs +++ b/deno_webgpu/webidl.rs @@ -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) -> 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 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, }, ) diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 6546b71c3..b3c100a10 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -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 diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 88d011036..1645088af 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -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 diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 87e8caff6..6016bc8e4 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -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); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 44e2c3010..47ffbd3c6 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -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 diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 71c469806..7f888aae1 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -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 diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs index 93c6b652a..651fdcf0c 100644 --- a/wgpu-hal/src/vulkan/command.rs +++ b/wgpu-hal/src/vulkan/command.rs @@ -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::() 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::() as u32, + ) + }; + } else { + for _ in 0..draw_count { + unsafe { + self.device.raw.cmd_draw_indexed_indirect( + self.active, + buffer.raw, + offset, + 1, + size_of::() as u32, + ) + }; + } + } } unsafe fn draw_mesh_tasks_indirect( &mut self, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 6608331f4..8a0bb03fc 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -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. /// diff --git a/wgpu-types/src/features.rs b/wgpu-types/src/features.rs index 2c4bd7245..fc584d3f0 100644 --- a/wgpu-types/src/features.rs +++ b/wgpu-types/src/features.rs @@ -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; diff --git a/wgpu/src/api/render_pass.rs b/wgpu/src/api/render_pass.rs index 8163b4261..0dd96b37e 100644 --- a/wgpu/src/api/render_pass.rs +++ b/wgpu/src/api/render_pass.rs @@ -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( diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index aeef22fce..75049d38c 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -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,