mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Refine Multi-Draw-Indirect (#6870)
This commit is contained in:
parent
78e35c4a7e
commit
fabcba8f9a
@ -484,10 +484,10 @@ impl RenderBundleEncoder {
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
RenderCommand::DrawIndirect {
|
||||
buffer_id,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed,
|
||||
} => {
|
||||
let scope = PassErrorScope::Draw {
|
||||
@ -504,7 +504,7 @@ impl RenderBundleEncoder {
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect { .. }
|
||||
RenderCommand::DrawIndirect { .. }
|
||||
| RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(),
|
||||
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
|
||||
@ -887,10 +887,10 @@ fn multi_draw_indirect(
|
||||
|
||||
state.flush_vertices();
|
||||
state.flush_binds(used_bind_groups, dynamic_offsets);
|
||||
state.commands.push(ArcRenderCommand::MultiDrawIndirect {
|
||||
state.commands.push(ArcRenderCommand::DrawIndirect {
|
||||
buffer,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed,
|
||||
});
|
||||
Ok(())
|
||||
@ -1101,25 +1101,25 @@ impl RenderBundle {
|
||||
)
|
||||
};
|
||||
}
|
||||
Cmd::MultiDrawIndirect {
|
||||
Cmd::DrawIndirect {
|
||||
buffer,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: false,
|
||||
} => {
|
||||
let buffer = buffer.try_raw(snatch_guard)?;
|
||||
unsafe { raw.draw_indirect(buffer, *offset, 1) };
|
||||
}
|
||||
Cmd::MultiDrawIndirect {
|
||||
Cmd::DrawIndirect {
|
||||
buffer,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: true,
|
||||
} => {
|
||||
let buffer = buffer.try_raw(snatch_guard)?;
|
||||
unsafe { raw.draw_indexed_indirect(buffer, *offset, 1) };
|
||||
}
|
||||
Cmd::MultiDrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
|
||||
Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
|
||||
return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
|
||||
}
|
||||
Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
|
||||
@ -1727,10 +1727,10 @@ pub mod bundle_ffi {
|
||||
buffer_id: id::BufferId,
|
||||
offset: BufferAddress,
|
||||
) {
|
||||
bundle.base.commands.push(RenderCommand::MultiDrawIndirect {
|
||||
bundle.base.commands.push(RenderCommand::DrawIndirect {
|
||||
buffer_id,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: false,
|
||||
});
|
||||
}
|
||||
@ -1740,10 +1740,10 @@ pub mod bundle_ffi {
|
||||
buffer_id: id::BufferId,
|
||||
offset: BufferAddress,
|
||||
) {
|
||||
bundle.base.commands.push(RenderCommand::MultiDrawIndirect {
|
||||
bundle.base.commands.push(RenderCommand::DrawIndirect {
|
||||
buffer_id,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -676,10 +676,9 @@ pub enum RenderPassErrorInner {
|
||||
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
|
||||
#[error("Indirect buffer offset {0:?} is not a multiple of 4")]
|
||||
UnalignedIndirectBufferOffset(BufferAddress),
|
||||
#[error("Indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}",
|
||||
count.map_or_else(String::new, |v| format!("(using count {v})")))]
|
||||
#[error("Indirect draw uses bytes {offset}..{end_offset} using count {count} which overruns indirect buffer of size {buffer_size}")]
|
||||
IndirectBufferOverrun {
|
||||
count: Option<NonZeroU32>,
|
||||
count: u32,
|
||||
offset: u64,
|
||||
end_offset: u64,
|
||||
buffer_size: u64,
|
||||
@ -1787,14 +1786,14 @@ impl Global {
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
ArcRenderCommand::MultiDrawIndirect {
|
||||
ArcRenderCommand::DrawIndirect {
|
||||
buffer,
|
||||
offset,
|
||||
count,
|
||||
indexed,
|
||||
} => {
|
||||
let scope = PassErrorScope::Draw {
|
||||
kind: if count.is_some() {
|
||||
kind: if count != 1 {
|
||||
DrawKind::MultiDrawIndirect
|
||||
} else {
|
||||
DrawKind::DrawIndirect
|
||||
@ -2467,7 +2466,7 @@ fn multi_draw_indirect(
|
||||
cmd_buf: &Arc<CommandBuffer>,
|
||||
indirect_buffer: Arc<crate::resource::Buffer>,
|
||||
offset: u64,
|
||||
count: Option<NonZeroU32>,
|
||||
count: u32,
|
||||
indexed: bool,
|
||||
) -> Result<(), RenderPassErrorInner> {
|
||||
api_log!(
|
||||
@ -2482,7 +2481,7 @@ fn multi_draw_indirect(
|
||||
true => size_of::<wgt::DrawIndexedIndirectArgs>(),
|
||||
};
|
||||
|
||||
if count.is_some() {
|
||||
if count != 1 {
|
||||
state
|
||||
.device
|
||||
.require_features(wgt::Features::MULTI_DRAW_INDIRECT)?;
|
||||
@ -2502,13 +2501,11 @@ fn multi_draw_indirect(
|
||||
indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
|
||||
let indirect_raw = indirect_buffer.try_raw(state.snatch_guard)?;
|
||||
|
||||
let actual_count = count.map_or(1, |c| c.get());
|
||||
|
||||
if offset % 4 != 0 {
|
||||
return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
|
||||
}
|
||||
|
||||
let end_offset = offset + stride as u64 * actual_count as u64;
|
||||
let end_offset = offset + stride as u64 * count as u64;
|
||||
if end_offset > indirect_buffer.size {
|
||||
return Err(RenderPassErrorInner::IndirectBufferOverrun {
|
||||
count,
|
||||
@ -2528,14 +2525,12 @@ fn multi_draw_indirect(
|
||||
|
||||
match indexed {
|
||||
false => unsafe {
|
||||
state
|
||||
.raw_encoder
|
||||
.draw_indirect(indirect_raw, offset, actual_count);
|
||||
state.raw_encoder.draw_indirect(indirect_raw, offset, count);
|
||||
},
|
||||
true => unsafe {
|
||||
state
|
||||
.raw_encoder
|
||||
.draw_indexed_indirect(indirect_raw, offset, actual_count);
|
||||
.draw_indexed_indirect(indirect_raw, offset, count);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@ -2599,7 +2594,7 @@ fn multi_draw_indirect_count(
|
||||
let end_offset = offset + stride * max_count as u64;
|
||||
if end_offset > indirect_buffer.size {
|
||||
return Err(RenderPassErrorInner::IndirectBufferOverrun {
|
||||
count: None,
|
||||
count: 1,
|
||||
offset,
|
||||
end_offset,
|
||||
buffer_size: indirect_buffer.size,
|
||||
@ -3103,10 +3098,10 @@ impl Global {
|
||||
};
|
||||
let base = pass.base_mut(scope)?;
|
||||
|
||||
base.commands.push(ArcRenderCommand::MultiDrawIndirect {
|
||||
base.commands.push(ArcRenderCommand::DrawIndirect {
|
||||
buffer: self.resolve_render_pass_buffer_id(scope, buffer_id)?,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: false,
|
||||
});
|
||||
|
||||
@ -3125,10 +3120,10 @@ impl Global {
|
||||
};
|
||||
let base = pass.base_mut(scope)?;
|
||||
|
||||
base.commands.push(ArcRenderCommand::MultiDrawIndirect {
|
||||
base.commands.push(ArcRenderCommand::DrawIndirect {
|
||||
buffer: self.resolve_render_pass_buffer_id(scope, buffer_id)?,
|
||||
offset,
|
||||
count: None,
|
||||
count: 1,
|
||||
indexed: true,
|
||||
});
|
||||
|
||||
@ -3148,10 +3143,10 @@ impl Global {
|
||||
};
|
||||
let base = pass.base_mut(scope)?;
|
||||
|
||||
base.commands.push(ArcRenderCommand::MultiDrawIndirect {
|
||||
base.commands.push(ArcRenderCommand::DrawIndirect {
|
||||
buffer: self.resolve_render_pass_buffer_id(scope, buffer_id)?,
|
||||
offset,
|
||||
count: NonZeroU32::new(count),
|
||||
count,
|
||||
indexed: false,
|
||||
});
|
||||
|
||||
@ -3171,10 +3166,10 @@ impl Global {
|
||||
};
|
||||
let base = pass.base_mut(scope)?;
|
||||
|
||||
base.commands.push(ArcRenderCommand::MultiDrawIndirect {
|
||||
base.commands.push(ArcRenderCommand::DrawIndirect {
|
||||
buffer: self.resolve_render_pass_buffer_id(scope, buffer_id)?,
|
||||
offset,
|
||||
count: NonZeroU32::new(count),
|
||||
count,
|
||||
indexed: true,
|
||||
});
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::{
|
||||
};
|
||||
use wgt::{BufferAddress, BufferSize, Color};
|
||||
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Rect, RenderBundle};
|
||||
|
||||
@ -82,11 +82,10 @@ pub enum RenderCommand {
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
},
|
||||
MultiDrawIndirect {
|
||||
DrawIndirect {
|
||||
buffer_id: id::BufferId,
|
||||
offset: BufferAddress,
|
||||
/// Count of `None` represents a non-multi call.
|
||||
count: Option<NonZeroU32>,
|
||||
count: u32,
|
||||
indexed: bool,
|
||||
},
|
||||
MultiDrawIndirectCount {
|
||||
@ -311,16 +310,16 @@ impl RenderCommand {
|
||||
first_instance,
|
||||
},
|
||||
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
RenderCommand::DrawIndirect {
|
||||
buffer_id,
|
||||
offset,
|
||||
count,
|
||||
indexed,
|
||||
} => ArcRenderCommand::MultiDrawIndirect {
|
||||
} => ArcRenderCommand::DrawIndirect {
|
||||
buffer: buffers_guard.get(buffer_id).get().map_err(|e| {
|
||||
RenderPassError {
|
||||
scope: PassErrorScope::Draw {
|
||||
kind: if count.is_some() {
|
||||
kind: if count != 1 {
|
||||
DrawKind::MultiDrawIndirect
|
||||
} else {
|
||||
DrawKind::DrawIndirect
|
||||
@ -459,11 +458,10 @@ pub enum ArcRenderCommand {
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
},
|
||||
MultiDrawIndirect {
|
||||
DrawIndirect {
|
||||
buffer: Arc<Buffer>,
|
||||
offset: BufferAddress,
|
||||
/// Count of `None` represents a non-multi call.
|
||||
count: Option<NonZeroU32>,
|
||||
count: u32,
|
||||
indexed: bool,
|
||||
},
|
||||
MultiDrawIndirectCount {
|
||||
|
||||
@ -372,6 +372,8 @@ impl super::Adapter {
|
||||
} else {
|
||||
vertex_shader_storage_textures.min(fragment_shader_storage_textures)
|
||||
};
|
||||
let indirect_execution =
|
||||
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_multi_draw_indirect");
|
||||
|
||||
let mut downlevel_flags = wgt::DownlevelFlags::empty()
|
||||
| wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
|
||||
@ -383,10 +385,7 @@ impl super::Adapter {
|
||||
wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE,
|
||||
max_storage_block_size != 0,
|
||||
);
|
||||
downlevel_flags.set(
|
||||
wgt::DownlevelFlags::INDIRECT_EXECUTION,
|
||||
supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_multi_draw_indirect"),
|
||||
);
|
||||
downlevel_flags.set(wgt::DownlevelFlags::INDIRECT_EXECUTION, indirect_execution);
|
||||
downlevel_flags.set(wgt::DownlevelFlags::BASE_VERTEX, supported((3, 2), (3, 2)));
|
||||
downlevel_flags.set(
|
||||
wgt::DownlevelFlags::INDEPENDENT_BLEND,
|
||||
@ -471,6 +470,8 @@ 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);
|
||||
|
||||
@ -638,12 +638,17 @@ bitflags::bitflags! {
|
||||
///
|
||||
/// Allows multiple indirect calls to be dispatched from a single buffer.
|
||||
///
|
||||
/// Supported platforms:
|
||||
/// Natively Supported Platforms:
|
||||
/// - DX12
|
||||
/// - Vulkan
|
||||
/// - Metal on Apple3+ or Mac1+ (Emulated on top of `draw_indirect` and `draw_indexed_indirect`)
|
||||
///
|
||||
/// This is a native only feature.
|
||||
/// 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
|
||||
|
||||
@ -783,7 +783,8 @@ const FEATURES_MAPPING: [(wgt::Features, webgpu_sys::GpuFeatureName); 12] = [
|
||||
];
|
||||
|
||||
fn map_wgt_features(supported_features: webgpu_sys::GpuSupportedFeatures) -> wgt::Features {
|
||||
let mut features = wgt::Features::empty();
|
||||
// We emulate MDI.
|
||||
let mut features = wgt::Features::MULTI_DRAW_INDIRECT;
|
||||
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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user