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
28 changes: 14 additions & 14 deletions wgpu-core/src/command/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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!(),
Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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,
});
}
Expand All @@ -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,
});
}
Expand Down
41 changes: 18 additions & 23 deletions wgpu-core/src/command/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!(
Expand All @@ -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)?;
Expand All @@ -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,
Expand All @@ -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(())
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
});

Expand All @@ -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,
});

Expand All @@ -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,
});

Expand All @@ -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,
});

Expand Down
18 changes: 8 additions & 10 deletions wgpu-core/src/command/render_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 5 additions & 4 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 8 additions & 3 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion wgpu/src/backend/webgpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading