#![allow(clippy::reversed_empty_ranges)]
use alloc::{
borrow::{Cow, ToOwned as _},
string::String,
sync::Arc,
vec::Vec,
};
use core::{
convert::Infallible,
mem,
num::{NonZeroU32, NonZeroU64},
ops::Range,
};
use arrayvec::ArrayVec;
use thiserror::Error;
use wgpu_hal::ShouldBeNonZeroExt;
use wgt::error::{ErrorType, WebGpuError};
#[cfg(feature = "trace")]
use crate::command::ArcReferences;
use crate::{
binding_model::{BindError, BindGroup, PipelineLayout},
command::{
bind::Binder, pass::validate_immediates_alignment, pass_base, BasePass,
BindGroupStateChange, ColorAttachmentError, DrawError, EncoderStateError, IdReferences,
MapPassErr, PassErrorScope, PassStateError, RenderCommand, RenderCommandError, StateChange,
},
device::{
AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
RenderPassContext,
},
hub::Hub,
id, impl_resource_type, impl_storage_item,
init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
pipeline::{PipelineFlags, RenderPipeline},
resource::{
Buffer, DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice,
RawResourceAccess, TrackingData,
},
resource_log,
snatch::SnatchGuard,
track::RenderBundleScope,
validation::{
check_color_attachment_count, validate_color_attachment_bytes_per_sample,
WorkgroupSizeCheck,
},
Label, LabelHelpers,
};
use super::{pass, render_command::ArcRenderCommand, DrawCommandFamily, DrawKind};
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RenderBundleEncoderDescriptor<'a> {
pub label: Label<'a>,
pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
pub sample_count: u32,
pub multiview: Option<NonZeroU32>,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct RenderBundleEncoder {
base: BasePass<RenderCommand<IdReferences>, Infallible>,
parent_id: id::DeviceId,
parent: Option<()>,
pub(crate) context: RenderPassContext,
pub(crate) is_depth_read_only: bool,
pub(crate) is_stencil_read_only: bool,
#[cfg_attr(feature = "serde", serde(skip))]
current_bind_groups: BindGroupStateChange,
#[cfg_attr(feature = "serde", serde(skip))]
current_pipeline: StateChange<id::RenderPipelineId>,
}
impl_resource_type!(RenderBundleEncoder);
impl_storage_item!(RenderBundleEncoder);
fn validate_render_bundle_encoder_descriptor(
desc: &RenderBundleEncoderDescriptor,
device: Option<&Arc<Device>>,
) -> Result<(bool, bool), CreateRenderBundleError> {
let mut have_attachment = false;
let max_color_attachments = device.map_or(hal::MAX_COLOR_ATTACHMENTS as u32, |device| {
assert!(device.limits.max_color_attachments <= hal::MAX_COLOR_ATTACHMENTS as u32);
device.limits.max_color_attachments
});
check_color_attachment_count(desc.color_formats.len(), max_color_attachments)?;
for &format in desc.color_formats.iter().flatten() {
have_attachment = true;
if !format.has_color_aspect() {
return Err(CreateRenderBundleError::FormatNotColor(format));
}
if let Some(device) = device {
let format_features = device.describe_format_features(format)?;
if !format_features
.allowed_usages
.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
{
return Err(CreateRenderBundleError::FormatNotRenderable(format));
}
}
}
if let Some(device) = device {
validate_color_attachment_bytes_per_sample(
desc.color_formats.iter().flatten().copied(),
device.limits.max_color_attachment_bytes_per_sample,
)?;
}
let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {
Some(ds) => {
have_attachment = true;
let has_depth = ds.format.has_depth_aspect();
let has_stencil = ds.format.has_stencil_aspect();
if !has_depth && !has_stencil {
return Err(CreateRenderBundleError::FormatNotDepthOrStencil(ds.format));
} else {
(
!has_depth || ds.depth_read_only,
!has_stencil || ds.stencil_read_only,
)
}
}
None => (true, true),
};
if !have_attachment {
return Err(CreateRenderBundleError::NoAttachment);
}
Ok((is_depth_read_only, is_stencil_read_only))
}
impl RenderBundleEncoder {
pub fn new(
desc: &RenderBundleEncoderDescriptor,
device: Option<&Arc<Device>>,
parent_id: id::DeviceId,
) -> Result<Self, CreateRenderBundleError> {
let (is_depth_read_only, is_stencil_read_only) =
validate_render_bundle_encoder_descriptor(desc, device)?;
Ok(Self {
base: BasePass::new(&desc.label),
parent: Some(()),
parent_id,
context: RenderPassContext {
attachments: AttachmentData {
colors: desc.color_formats.iter().cloned().collect(),
resolves: ArrayVec::new(),
depth_stencil: desc.depth_stencil.map(|ds| ds.format),
},
sample_count: desc.sample_count,
multiview_mask: desc.multiview,
},
is_depth_read_only,
is_stencil_read_only,
current_bind_groups: BindGroupStateChange::new(),
current_pipeline: StateChange::new(),
})
}
pub fn dummy(parent_id: id::DeviceId) -> Self {
Self {
base: BasePass::new(&None),
parent: None,
parent_id,
context: RenderPassContext::default(),
is_depth_read_only: false,
is_stencil_read_only: false,
current_bind_groups: BindGroupStateChange::new(),
current_pipeline: StateChange::new(),
}
}
pub fn parent(&self) -> id::DeviceId {
self.parent_id
}
pub(crate) fn finish(
&mut self,
desc: &RenderBundleDescriptor,
device: &Arc<Device>,
hub: &Hub,
) -> Result<Arc<RenderBundle>, RenderBundleError> {
let scope = PassErrorScope::Bundle;
self.parent
.take()
.ok_or(RenderBundleErrorInner::Ended)
.map_pass_err(scope)?;
device.check_is_valid().map_pass_err(scope)?;
{
let encoder_desc = RenderBundleEncoderDescriptor {
label: self.base.label.as_ref().map(Cow::from),
color_formats: Cow::Borrowed(&self.context.attachments.colors),
depth_stencil: self.context.attachments.depth_stencil.map(|format| {
wgt::RenderBundleDepthStencil {
format,
depth_read_only: self.is_depth_read_only,
stencil_read_only: self.is_stencil_read_only,
}
}),
sample_count: self.context.sample_count,
multiview: self.context.multiview_mask,
};
validate_render_bundle_encoder_descriptor(&encoder_desc, Some(device))
.map_pass_err(scope)?;
};
let buffer_guard = hub.buffers.read();
let bind_group_guard = hub.bind_groups.read();
let pipeline_guard = hub.render_pipelines.read();
let mut state = State {
trackers: RenderBundleScope::new(),
pipeline: None,
vertex: Default::default(),
index: None,
flat_dynamic_offsets: Vec::new(),
device: device.clone(),
commands: Vec::new(),
buffer_memory_init_actions: Vec::new(),
texture_memory_init_actions: Vec::new(),
next_dynamic_offset: 0,
binder: Binder::new(),
immediate_slots_set: Default::default(),
};
let indices = &state.device.tracker_indices;
state.trackers.buffers.set_size(indices.buffers.size());
state.trackers.textures.set_size(indices.textures.size());
let base = &self.base;
for command in &base.commands {
match command {
&RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group,
} => {
let scope = PassErrorScope::SetBindGroup;
set_bind_group(
&mut state,
&bind_group_guard,
&base.dynamic_offsets,
index,
num_dynamic_offsets,
bind_group,
)
.map_pass_err(scope)?;
}
&RenderCommand::SetPipeline(pipeline) => {
let scope = PassErrorScope::SetPipelineRender;
set_pipeline(
&mut state,
&pipeline_guard,
&self.context,
self.is_depth_read_only,
self.is_stencil_read_only,
pipeline,
)
.map_pass_err(scope)?;
}
&RenderCommand::SetIndexBuffer {
buffer,
index_format,
offset,
size,
} => {
let scope = PassErrorScope::SetIndexBuffer;
set_index_buffer(
&mut state,
&buffer_guard,
buffer,
index_format,
offset,
size,
)
.map_pass_err(scope)?;
}
&RenderCommand::SetVertexBuffer {
slot,
buffer,
offset,
size,
} => {
let scope = PassErrorScope::SetVertexBuffer;
set_vertex_buffer(&mut state, &buffer_guard, slot, buffer, offset, size)
.map_pass_err(scope)?;
}
&RenderCommand::SetImmediate {
offset,
size_bytes,
values_offset,
} => {
let scope = PassErrorScope::SetImmediate;
set_immediates(&mut state, offset, size_bytes, values_offset)
.map_pass_err(scope)?;
}
&RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
let scope = PassErrorScope::Draw {
kind: DrawKind::Draw,
family: DrawCommandFamily::Draw,
};
draw(
&mut state,
vertex_count,
instance_count,
first_vertex,
first_instance,
)
.map_pass_err(scope)?;
}
&RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
} => {
let scope = PassErrorScope::Draw {
kind: DrawKind::Draw,
family: DrawCommandFamily::DrawIndexed,
};
draw_indexed(
&mut state,
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
)
.map_pass_err(scope)?;
}
&RenderCommand::DrawMeshTasks {
group_count_x,
group_count_y,
group_count_z,
} => {
let scope = PassErrorScope::Draw {
kind: DrawKind::Draw,
family: DrawCommandFamily::DrawMeshTasks,
};
draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
.map_pass_err(scope)?;
}
&RenderCommand::DrawIndirect {
buffer,
offset,
count: 1,
family,
vertex_or_index_limit: None,
instance_limit: None,
} => {
let scope = PassErrorScope::Draw {
kind: DrawKind::DrawIndirect,
family,
};
multi_draw_indirect(&mut state, &buffer_guard, buffer, offset, family)
.map_pass_err(scope)?;
}
&RenderCommand::DrawIndirect {
count,
vertex_or_index_limit,
instance_limit,
..
} => {
unreachable!("unexpected (multi-)draw indirect with count {count}, vertex_or_index_limits {vertex_or_index_limit:?}, instance_limit {instance_limit:?} found in a render bundle");
}
&RenderCommand::MultiDrawIndirectCount { .. }
| &RenderCommand::PushDebugGroup { color: _, len: _ }
| &RenderCommand::InsertDebugMarker { color: _, len: _ }
| &RenderCommand::PopDebugGroup => {
unimplemented!("not supported by a render bundle")
}
&RenderCommand::WriteTimestamp { .. }
| &RenderCommand::BeginOcclusionQuery { .. }
| &RenderCommand::EndOcclusionQuery
| &RenderCommand::BeginPipelineStatisticsQuery { .. }
| &RenderCommand::EndPipelineStatisticsQuery => {
unimplemented!("not supported by a render bundle")
}
&RenderCommand::ExecuteBundle(_)
| &RenderCommand::SetBlendConstant(_)
| &RenderCommand::SetStencilReference(_)
| &RenderCommand::SetViewport { .. }
| &RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"),
}
}
let State {
trackers,
flat_dynamic_offsets,
device,
commands,
buffer_memory_init_actions,
texture_memory_init_actions,
..
} = state;
let tracker_indices = device.tracker_indices.bundles.clone();
let discard_hal_labels = device
.instance_flags
.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
let string_data = mem::take(&mut self.base.string_data);
let immediates_data = mem::take(&mut self.base.immediates_data);
let context = mem::take(&mut self.context);
let render_bundle = RenderBundle {
base: BasePass {
label: desc.label.as_deref().map(str::to_owned),
error: None,
commands,
dynamic_offsets: flat_dynamic_offsets,
string_data,
immediates_data,
},
is_depth_read_only: self.is_depth_read_only,
is_stencil_read_only: self.is_stencil_read_only,
device: device.clone(),
used: trackers,
buffer_memory_init_actions,
texture_memory_init_actions,
context,
label: desc.label.to_string(),
tracking_data: TrackingData::new(tracker_indices),
discard_hal_labels,
};
let render_bundle = Arc::new(render_bundle);
Ok(render_bundle)
}
pub fn set_index_buffer(
&mut self,
buffer: id::BufferId,
index_format: wgt::IndexFormat,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::SetIndexBuffer);
self.base.commands.push(RenderCommand::SetIndexBuffer {
buffer,
index_format,
offset,
size,
});
Ok(())
}
pub fn set_bind_group(
&mut self,
index: u32,
bind_group_id: Option<id::BindGroupId>,
offsets: &[wgt::DynamicOffset],
) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::SetBindGroup);
let redundant = self.current_bind_groups.set_and_check_redundant(
bind_group_id,
index,
&mut self.base.dynamic_offsets,
offsets,
);
if redundant {
return Ok(());
}
self.base.commands.push(RenderCommand::SetBindGroup {
index,
num_dynamic_offsets: offsets.len(),
bind_group: bind_group_id,
});
Ok(())
}
pub fn set_pipeline(
&mut self,
pipeline_id: id::RenderPipelineId,
) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::SetPipelineRender);
if self.current_pipeline.set_and_check_redundant(pipeline_id) {
return Ok(());
}
self.base
.commands
.push(RenderCommand::SetPipeline(pipeline_id));
Ok(())
}
pub fn set_vertex_buffer(
&mut self,
slot: u32,
buffer_id: Option<id::BufferId>,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::SetVertexBuffer);
self.base.commands.push(RenderCommand::SetVertexBuffer {
slot,
buffer: buffer_id,
offset,
size,
});
Ok(())
}
pub fn set_immediates(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::SetImmediate);
let value_offset = self.base.immediates_data.len().try_into().expect(
"Ran out of immediate data space. Don't set 4gb of immediates per RenderBundle.",
);
self.base.immediates_data.extend(
data.chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)
.map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
);
self.base.commands.push(RenderCommand::SetImmediate {
offset,
size_bytes: data.len() as u32,
values_offset: Some(value_offset),
});
Ok(())
}
pub fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), PassStateError> {
pass_base!(
self,
PassErrorScope::Draw {
kind: DrawKind::Draw,
family: DrawCommandFamily::Draw
}
);
self.base.commands.push(RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
});
Ok(())
}
pub fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), PassStateError> {
pass_base!(
self,
PassErrorScope::Draw {
kind: DrawKind::Draw,
family: DrawCommandFamily::DrawIndexed
}
);
self.base.commands.push(RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
});
Ok(())
}
pub fn draw_indirect(
&mut self,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
pass_base!(
self,
PassErrorScope::Draw {
kind: DrawKind::DrawIndirect,
family: DrawCommandFamily::Draw
}
);
self.base.commands.push(RenderCommand::DrawIndirect {
buffer: buffer_id,
offset,
count: 1,
family: DrawCommandFamily::Draw,
vertex_or_index_limit: None,
instance_limit: None,
});
Ok(())
}
pub fn draw_indexed_indirect(
&mut self,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
pass_base!(
self,
PassErrorScope::Draw {
kind: DrawKind::DrawIndirect,
family: DrawCommandFamily::DrawIndexed
}
);
self.base.commands.push(RenderCommand::DrawIndirect {
buffer: buffer_id,
offset,
count: 1,
family: DrawCommandFamily::DrawIndexed,
vertex_or_index_limit: None,
instance_limit: None,
});
Ok(())
}
pub fn push_debug_group(&mut self, _label: &str) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::PushDebugGroup);
Ok(())
}
pub fn pop_debug_group(&mut self) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::PopDebugGroup);
Ok(())
}
pub fn insert_debug_marker(&mut self, _label: &str) -> Result<(), PassStateError> {
pass_base!(self, PassErrorScope::InsertDebugMarker);
Ok(())
}
}
fn set_bind_group(
state: &mut State,
bind_group_guard: &crate::storage::Storage<Fallible<BindGroup>>,
dynamic_offsets: &[u32],
index: u32,
num_dynamic_offsets: usize,
bind_group_id: Option<id::Id<id::markers::BindGroup>>,
) -> Result<(), RenderBundleErrorInner> {
let max_bind_groups = state.device.limits.max_bind_groups;
if index >= max_bind_groups {
return Err(
RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {
index,
max: max_bind_groups,
})
.into(),
);
}
let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;
state.next_dynamic_offset = offsets_range.end;
let offsets = &dynamic_offsets[offsets_range.clone()];
let bind_group = bind_group_id.map(|id| bind_group_guard.get(id));
if let Some(bind_group) = bind_group {
let bind_group = bind_group.get()?;
bind_group.same_device(&state.device)?;
bind_group.validate_dynamic_bindings(index, offsets)?;
unsafe { state.trackers.merge_bind_group(&bind_group.used)? };
let bind_group = state.trackers.bind_groups.insert_single(bind_group);
state
.binder
.assign_group(index as usize, bind_group, offsets);
} else {
if !offsets.is_empty() {
return Err(RenderBundleErrorInner::Bind(
BindError::DynamicOffsetCountNotZero {
group: index,
actual: offsets.len(),
},
));
}
state.binder.clear_group(index as usize);
}
Ok(())
}
fn set_pipeline(
state: &mut State,
pipeline_guard: &crate::storage::Storage<Arc<RenderPipeline>>,
context: &RenderPassContext,
is_depth_read_only: bool,
is_stencil_read_only: bool,
pipeline_id: id::Id<id::markers::RenderPipeline>,
) -> Result<(), RenderBundleErrorInner> {
let pipeline = pipeline_guard.get(pipeline_id);
pipeline.same_device(&state.device)?;
context
.check_compatible(&pipeline.pass_context, pipeline.as_ref())
.map_err(RenderCommandError::IncompatiblePipelineTargets)?;
if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {
return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
}
if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {
return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
}
state
.commands
.push(ArcRenderCommand::SetPipeline(pipeline.clone()));
state.pipeline = Some(pipeline.clone());
state
.binder
.change_pipeline_layout(pipeline.layout()?, &pipeline.late_sized_buffer_groups);
state.vertex.update_limits(&pipeline.vertex_steps);
state.trackers.render_pipelines.insert_single(pipeline);
Ok(())
}
fn set_index_buffer(
state: &mut State,
buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
buffer_id: id::Id<id::markers::Buffer>,
index_format: wgt::IndexFormat,
offset: u64,
size: Option<NonZeroU64>,
) -> Result<(), RenderBundleErrorInner> {
let buffer = buffer_guard.get(buffer_id).get()?;
state
.trackers
.buffers
.merge_single(&buffer, wgt::BufferUses::INDEX)?;
buffer.same_device(&state.device)?;
buffer.check_usage(wgt::BufferUsages::INDEX)?;
if !offset.is_multiple_of(u64::from(index_format.byte_size())) {
return Err(RenderCommandError::UnalignedIndexBuffer {
offset,
alignment: index_format.byte_size() as usize,
}
.into());
}
let end = offset + buffer.resolve_binding_size(offset, size)?;
state
.buffer_memory_init_actions
.extend(buffer.initialization_status.read().create_action(
&buffer,
offset..end.get(),
MemoryInitKind::NeedsInitializedMemory,
));
state.set_index_buffer(buffer, index_format, offset..end.get());
Ok(())
}
fn set_vertex_buffer(
state: &mut State,
buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
slot: u32,
buffer_id: Option<id::Id<id::markers::Buffer>>,
offset: u64,
size: Option<NonZeroU64>,
) -> Result<(), RenderBundleErrorInner> {
let max_vertex_buffers = state.device.limits.max_vertex_buffers;
if slot >= max_vertex_buffers {
return Err(RenderCommandError::VertexBufferIndexOutOfRange {
index: slot,
max: max_vertex_buffers,
}
.into());
}
if let Some(buffer_id) = buffer_id {
let buffer = buffer_guard.get(buffer_id).get()?;
state
.trackers
.buffers
.merge_single(&buffer, wgt::BufferUses::VERTEX)?;
buffer.same_device(&state.device)?;
buffer.check_usage(wgt::BufferUsages::VERTEX)?;
if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
}
let binding_size = buffer.resolve_binding_size(offset, size)?;
let buffer_range = offset..(offset + binding_size);
state
.buffer_memory_init_actions
.extend(buffer.initialization_status.read().create_action(
&buffer,
buffer_range.clone(),
MemoryInitKind::NeedsInitializedMemory,
));
state.vertex.set_buffer(slot as usize, buffer, buffer_range);
if let Some(pipeline) = state.pipeline.as_deref() {
state.vertex.update_limits(&pipeline.vertex_steps);
}
} else {
if offset != 0 {
return Err(RenderCommandError::from(
crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
slot,
offset,
},
)
.into());
}
if let Some(size) = size {
return Err(RenderCommandError::from(
crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
slot,
size: size.get(),
},
)
.into());
}
state.vertex.clear_buffer(slot as usize);
if let Some(pipeline) = state.pipeline.as_deref() {
state.vertex.update_limits(&pipeline.vertex_steps);
}
}
Ok(())
}
fn set_immediates(
state: &mut State,
offset: u32,
size_bytes: u32,
values_offset: Option<u32>,
) -> Result<(), RenderBundleErrorInner> {
validate_immediates_alignment(offset, size_bytes)?;
let pipeline = state
.pipeline
.as_deref()
.ok_or(DrawError::MissingPipeline(pass::MissingPipeline))?;
pipeline
.layout()?
.validate_immediates_ranges(offset, size_bytes)?;
state.commands.push(ArcRenderCommand::SetImmediate {
offset,
size_bytes,
values_offset,
});
state.immediate_slots_set |= naga::valid::ImmediateSlots::from_range(offset, size_bytes);
Ok(())
}
fn draw(
state: &mut State,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), RenderBundleErrorInner> {
state.is_ready(DrawCommandFamily::Draw)?;
state
.vertex
.limits
.validate_vertex_limit(first_vertex, vertex_count)?;
state
.vertex
.limits
.validate_instance_limit(first_instance, instance_count)?;
if instance_count > 0 && vertex_count > 0 {
state.flush_vertex_buffers();
state.flush_bindings();
state.commands.push(ArcRenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
});
}
Ok(())
}
fn draw_indexed(
state: &mut State,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), RenderBundleErrorInner> {
state.is_ready(DrawCommandFamily::DrawIndexed)?;
let index = state.index.as_ref().unwrap();
let last_index = first_index as u64 + index_count as u64;
let index_limit = index.limit();
if last_index > index_limit {
return Err(DrawError::IndexBeyondLimit {
last_index,
index_limit,
}
.into());
}
state
.vertex
.limits
.validate_instance_limit(first_instance, instance_count)?;
if instance_count > 0 && index_count > 0 {
state.flush_index();
state.flush_vertex_buffers();
state.flush_bindings();
state.commands.push(ArcRenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
});
}
Ok(())
}
fn draw_mesh_tasks(
state: &mut State,
group_count_x: u32,
group_count_y: u32,
group_count_z: u32,
) -> Result<(), RenderBundleErrorInner> {
state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
let limits = &state.device.limits;
let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
(
limits.max_task_workgroups_per_dimension,
limits.max_task_workgroup_total_count,
)
} else {
(
limits.max_mesh_workgroups_per_dimension,
limits.max_mesh_workgroup_total_count,
)
};
let total_count = WorkgroupSizeCheck {
dimensions: &[group_count_x, group_count_y, group_count_z],
per_dimension_limits: &[groups_size_limit, groups_size_limit, groups_size_limit],
per_dimension_limits_desc: "max_task_mesh_workgroups_per_dimension",
total_limit: max_groups,
total_limit_desc: "max_task_mesh_workgroup_total_count",
}
.check_and_compute_total_invocations()
.map_err(|err| RenderBundleErrorInner::Draw(err.into()))?;
if total_count > 0 {
state.flush_bindings();
state.commands.push(ArcRenderCommand::DrawMeshTasks {
group_count_x,
group_count_y,
group_count_z,
});
}
Ok(())
}
fn multi_draw_indirect(
state: &mut State,
buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
buffer_id: id::Id<id::markers::Buffer>,
offset: u64,
family: DrawCommandFamily,
) -> Result<(), RenderBundleErrorInner> {
state.is_ready(family)?;
state
.device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
let buffer = buffer_guard.get(buffer_id).get()?;
buffer.same_device(&state.device)?;
buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
let stride = super::get_src_stride_of_indirect_args(family);
assert!(offset <= wgt::BufferAddress::MAX - stride);
state
.buffer_memory_init_actions
.extend(buffer.initialization_status.read().create_action(
&buffer,
offset..(offset + stride),
MemoryInitKind::NeedsInitializedMemory,
));
let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {
let index = state.index.as_mut().unwrap();
state.commands.extend(index.flush());
index.limit()
} else {
state.vertex.limits.vertex_limit
};
let instance_limit = state.vertex.limits.instance_limit;
let buffer_uses = if state.device.indirect_validation.is_some()
&& family != DrawCommandFamily::DrawMeshTasks
{
wgt::BufferUses::STORAGE_READ_ONLY
} else {
wgt::BufferUses::INDIRECT
};
state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
state.flush_vertex_buffers();
state.flush_bindings();
state.commands.push(ArcRenderCommand::DrawIndirect {
buffer,
offset,
count: 1,
family,
vertex_or_index_limit: Some(vertex_or_index_limit),
instance_limit: Some(instance_limit),
});
Ok(())
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum CreateRenderBundleError {
#[error(transparent)]
ColorAttachment(#[from] ColorAttachmentError),
#[error("Format {0:?} does not have a color aspect")]
FormatNotColor(wgt::TextureFormat),
#[error("Color attachment format {0:?} is not renderable")]
FormatNotRenderable(wgt::TextureFormat),
#[error("Format {0:?} is not a depth/stencil format")]
FormatNotDepthOrStencil(wgt::TextureFormat),
#[error("Render bundle must have at least one attachment (color or depth/stencil)")]
NoAttachment,
#[error("Invalid number of samples {0}")]
InvalidSampleCount(u32),
#[error(transparent)]
MissingFeatures(#[from] MissingFeatures),
}
impl WebGpuError for CreateRenderBundleError {
fn webgpu_error_type(&self) -> ErrorType {
match self {
Self::ColorAttachment(e) => e.webgpu_error_type(),
Self::FormatNotColor(_)
| Self::FormatNotRenderable(_)
| Self::FormatNotDepthOrStencil(_)
| Self::NoAttachment
| Self::InvalidSampleCount(_) => ErrorType::Validation,
Self::MissingFeatures(e) => e.webgpu_error_type(),
}
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum ExecutionError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
DestroyedResource(#[from] DestroyedResourceError),
#[error("Using {0} in a render bundle is not implemented")]
Unimplemented(&'static str),
}
pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
#[derive(Debug)]
pub struct RenderBundle {
base: BasePass<ArcRenderCommand, Infallible>,
pub(super) is_depth_read_only: bool,
pub(super) is_stencil_read_only: bool,
pub(crate) device: Arc<Device>,
pub(crate) used: RenderBundleScope,
pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
pub(super) context: RenderPassContext,
label: String,
pub(crate) tracking_data: TrackingData,
discard_hal_labels: bool,
}
impl Drop for RenderBundle {
fn drop(&mut self) {
resource_log!("Drop {}", self.error_ident());
}
}
#[cfg(send_sync)]
unsafe impl Send for RenderBundle {}
#[cfg(send_sync)]
unsafe impl Sync for RenderBundle {}
impl RenderBundle {
#[cfg(feature = "trace")]
pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {
self.base.clone()
}
pub(super) unsafe fn execute(
&self,
raw: &mut dyn hal::DynCommandEncoder,
indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
snatch_guard: &SnatchGuard,
) -> Result<(), ExecutionError> {
let mut offsets = self.base.dynamic_offsets.as_slice();
let mut pipeline_layout = None::<Arc<PipelineLayout>>;
if !self.discard_hal_labels {
if let Some(ref label) = self.base.label {
unsafe { raw.begin_debug_marker(label) };
}
}
use ArcRenderCommand as Cmd;
for command in self.base.commands.iter() {
match command {
Cmd::SetBindGroup {
index,
num_dynamic_offsets,
bind_group,
} => {
let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;
unsafe {
raw.set_bind_group(
pipeline_layout
.as_ref()
.unwrap()
.raw()
.expect("PipelineLayout should be valid at this point"),
*index,
raw_bg,
&offsets[..*num_dynamic_offsets],
)
};
offsets = &offsets[*num_dynamic_offsets..];
}
Cmd::SetPipeline(pipeline) => {
unsafe {
raw.set_render_pipeline(
pipeline
.raw()
.expect("RenderPipeline should be valid when executing bundle"),
)
};
pipeline_layout = Some(
pipeline
.layout()
.expect("PipelineLayout should be valid when executing bundle")
.clone(),
);
}
Cmd::SetIndexBuffer {
buffer,
index_format,
offset,
size,
} => {
let buffer = buffer.try_raw(snatch_guard)?;
let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
unsafe { raw.set_index_buffer(bb, *index_format) };
}
Cmd::SetVertexBuffer {
slot,
buffer,
offset,
size,
} => {
let buffer = buffer.as_ref().unwrap().try_raw(snatch_guard)?;
let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
unsafe { raw.set_vertex_buffer(*slot, bb) };
}
Cmd::SetImmediate {
offset,
size_bytes,
values_offset,
} => {
let pipeline_layout = pipeline_layout.as_ref().unwrap();
if let Some(values_offset) = *values_offset {
let values_end_offset =
(values_offset + size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;
let data_slice =
&self.base.immediates_data[(values_offset as usize)..values_end_offset];
unsafe {
raw.set_immediates(
pipeline_layout
.raw()
.expect("PipelineLayout should be valid at this point"),
*offset,
data_slice,
)
}
} else {
super::immediates_clear(
*offset,
*size_bytes,
|clear_offset, clear_data| {
unsafe {
raw.set_immediates(
pipeline_layout
.raw()
.expect("PipelineLayout should be valid at this point"),
clear_offset,
clear_data,
)
};
},
);
}
}
Cmd::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
unsafe {
raw.draw(
*first_vertex,
*vertex_count,
*first_instance,
*instance_count,
)
};
}
Cmd::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
} => {
unsafe {
raw.draw_indexed(
*first_index,
*index_count,
*base_vertex,
*first_instance,
*instance_count,
)
};
}
Cmd::DrawMeshTasks {
group_count_x,
group_count_y,
group_count_z,
} => unsafe {
raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
},
Cmd::DrawIndirect {
buffer,
offset,
count: 1,
family,
vertex_or_index_limit,
instance_limit,
} => {
let (buffer, offset) = if self.device.indirect_validation.is_some()
&& *family != DrawCommandFamily::DrawMeshTasks
{
let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
indirect_draw_validation_resources,
&self.device,
buffer,
*offset,
*family,
vertex_or_index_limit
.expect("finalized render bundle missing vertex_or_index_limit"),
instance_limit.expect("finalized render bundle missing instance_limit"),
)?;
let dst_buffer =
indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
(dst_buffer, offset)
} else {
(buffer.try_raw(snatch_guard)?, *offset)
};
match family {
DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
DrawCommandFamily::DrawIndexed => unsafe {
raw.draw_indexed_indirect(buffer, offset, 1)
},
DrawCommandFamily::DrawMeshTasks => unsafe {
raw.draw_mesh_tasks_indirect(buffer, offset, 1);
},
}
}
Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
}
Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
return Err(ExecutionError::Unimplemented("debug-markers"))
}
Cmd::WriteTimestamp { .. }
| Cmd::BeginOcclusionQuery { .. }
| Cmd::EndOcclusionQuery
| Cmd::BeginPipelineStatisticsQuery { .. }
| Cmd::EndPipelineStatisticsQuery => {
return Err(ExecutionError::Unimplemented("queries"))
}
Cmd::ExecuteBundle(_)
| Cmd::SetBlendConstant(_)
| Cmd::SetStencilReference(_)
| Cmd::SetViewport { .. }
| Cmd::SetScissor(_) => unreachable!(),
}
}
if !self.discard_hal_labels {
if let Some(_) = self.base.label {
unsafe { raw.end_debug_marker() };
}
}
Ok(())
}
}
crate::impl_resource_type!(RenderBundle);
crate::impl_labeled!(RenderBundle);
crate::impl_parent_device!(RenderBundle);
crate::impl_storage_item!(RenderBundle);
crate::impl_trackable!(RenderBundle);
#[derive(Debug)]
struct IndexState {
buffer: Arc<Buffer>,
format: wgt::IndexFormat,
range: Range<wgt::BufferAddress>,
is_dirty: bool,
}
impl IndexState {
fn limit(&self) -> u64 {
let bytes_per_index = self.format.byte_size() as u64;
(self.range.end - self.range.start) / bytes_per_index
}
fn flush(&mut self) -> Option<ArcRenderCommand> {
let binding_size = self
.range
.end
.checked_sub(self.range.start)
.filter(|_| self.range.end <= self.buffer.size)
.expect("index range must be contained in buffer");
if self.is_dirty {
self.is_dirty = false;
Some(ArcRenderCommand::SetIndexBuffer {
buffer: self.buffer.clone(),
index_format: self.format,
offset: self.range.start,
size: NonZeroU64::new(binding_size),
})
} else {
None
}
}
}
#[derive(Debug)]
struct State {
trackers: RenderBundleScope,
pipeline: Option<Arc<RenderPipeline>>,
vertex: super::VertexState,
index: Option<IndexState>,
flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
device: Arc<Device>,
commands: Vec<ArcRenderCommand>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
texture_memory_init_actions: Vec<TextureInitTrackerAction>,
next_dynamic_offset: usize,
binder: Binder,
immediate_slots_set: naga::valid::ImmediateSlots,
}
impl State {
fn set_index_buffer(
&mut self,
buffer: Arc<Buffer>,
format: wgt::IndexFormat,
range: Range<wgt::BufferAddress>,
) {
match self.index {
Some(ref current)
if current.buffer.is_equal(&buffer)
&& current.format == format
&& current.range == range =>
{
return
}
_ => (),
}
self.index = Some(IndexState {
buffer,
format,
range,
is_dirty: true,
});
}
fn flush_index(&mut self) {
let commands = self.index.as_mut().and_then(|index| index.flush());
self.commands.extend(commands);
}
fn flush_vertex_buffers(&mut self) {
let vertex = &mut self.vertex;
let commands = &mut self.commands;
vertex.flush(|slot, buffer, offset, size| {
commands.push(ArcRenderCommand::SetVertexBuffer {
slot,
buffer: Some(buffer.clone()),
offset,
size,
});
});
}
fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {
if let Some(pipeline) = self.pipeline.as_ref() {
self.binder.check_compatibility(pipeline.as_ref())?;
self.binder.check_late_buffer_bindings()?;
self.vertex.validate(pipeline.as_ref(), &self.binder)?;
if family == DrawCommandFamily::DrawIndexed {
let index_format = match &self.index {
Some(index) => index.format,
None => return Err(DrawError::MissingIndexBuffer),
};
if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)
{
return Err(DrawError::UnmatchedStripIndexFormat {
pipeline: pipeline.error_ident(),
strip_index_format: pipeline.strip_index_format,
buffer_format: index_format,
});
}
}
if !self
.immediate_slots_set
.contains(pipeline.immediate_slots_required)
{
return Err(DrawError::MissingImmediateData {
missing: pipeline
.immediate_slots_required
.difference(self.immediate_slots_set),
});
}
Ok(())
} else {
Err(DrawError::MissingPipeline(pass::MissingPipeline))
}
}
fn flush_bindings(&mut self) {
let start = self.binder.take_rebind_start_index();
let entries = self.binder.list_valid_with_start(start);
self.commands
.extend(entries.map(|(i, bind_group, dynamic_offsets)| {
self.buffer_memory_init_actions
.extend_from_slice(&bind_group.buffer_init_actions);
self.texture_memory_init_actions
.extend_from_slice(&bind_group.texture_init_actions);
self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);
ArcRenderCommand::SetBindGroup {
index: i.try_into().unwrap(),
bind_group: Some(bind_group.clone()),
num_dynamic_offsets: dynamic_offsets.len(),
}
}));
}
}
#[derive(Clone, Debug, Error)]
pub enum RenderBundleErrorInner {
#[error(transparent)]
Create(#[from] CreateRenderBundleError),
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
RenderCommand(RenderCommandError),
#[error(transparent)]
Draw(#[from] DrawError),
#[error(transparent)]
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
#[error(transparent)]
Bind(#[from] BindError),
#[error(transparent)]
InvalidResource(#[from] InvalidResourceError),
#[error("Render bundle encoder has already ended")]
Ended,
}
impl<T> From<T> for RenderBundleErrorInner
where
T: Into<RenderCommandError>,
{
fn from(t: T) -> Self {
Self::RenderCommand(t.into())
}
}
#[derive(Clone, Debug, Error)]
#[error("{scope}")]
pub struct RenderBundleError {
pub scope: PassErrorScope,
#[source]
inner: RenderBundleErrorInner,
}
impl WebGpuError for RenderBundleError {
fn webgpu_error_type(&self) -> ErrorType {
let Self { scope: _, inner } = self;
match inner {
RenderBundleErrorInner::Create(e) => e.webgpu_error_type(),
RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),
RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),
RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),
RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),
RenderBundleErrorInner::InvalidResource(e) => e.webgpu_error_type(),
RenderBundleErrorInner::Ended => ErrorType::Validation,
}
}
}
impl RenderBundleError {
pub fn from_device_error(e: DeviceError) -> Self {
Self {
scope: PassErrorScope::Bundle,
inner: e.into(),
}
}
}
impl<E> MapPassErr<RenderBundleError> for E
where
E: Into<RenderBundleErrorInner>,
{
fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
RenderBundleError {
scope,
inner: self.into(),
}
}
}
impl crate::global::Global {
pub fn render_bundle_encoder_set_bind_group(
&self,
bundle: &mut RenderBundleEncoder,
index: u32,
bind_group_id: Option<id::BindGroupId>,
offsets: &[wgt::DynamicOffset],
) -> Result<(), PassStateError> {
bundle.set_bind_group(index, bind_group_id, offsets)
}
pub fn render_bundle_encoder_set_bind_group_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
index: u32,
bind_group_id: Option<id::BindGroupId>,
offsets: &[wgt::DynamicOffset],
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.set_bind_group(index, bind_group_id, offsets)
}
pub fn render_bundle_encoder_set_pipeline(
&self,
bundle: &mut RenderBundleEncoder,
pipeline_id: id::RenderPipelineId,
) -> Result<(), PassStateError> {
bundle.set_pipeline(pipeline_id)
}
pub fn render_bundle_encoder_set_pipeline_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
pipeline_id: id::RenderPipelineId,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.set_pipeline(pipeline_id)
}
pub fn render_bundle_encoder_set_vertex_buffer(
&self,
bundle: &mut RenderBundleEncoder,
slot: u32,
buffer_id: Option<id::BufferId>,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
bundle.set_vertex_buffer(slot, buffer_id, offset, size)
}
pub fn render_bundle_encoder_set_vertex_buffer_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
slot: u32,
buffer_id: Option<id::BufferId>,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.set_vertex_buffer(slot, buffer_id, offset, size)
}
pub fn render_bundle_encoder_set_index_buffer(
&self,
encoder: &mut RenderBundleEncoder,
buffer: id::BufferId,
index_format: wgt::IndexFormat,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
encoder.set_index_buffer(buffer, index_format, offset, size)
}
pub fn render_bundle_encoder_set_index_buffer_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
buffer: id::BufferId,
index_format: wgt::IndexFormat,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.set_index_buffer(buffer, index_format, offset, size)
}
pub fn render_bundle_encoder_set_immediates(
&self,
pass: &mut RenderBundleEncoder,
offset: u32,
data: &[u8],
) -> Result<(), PassStateError> {
pass.set_immediates(offset, data)
}
pub fn render_bundle_encoder_set_immediates_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
offset: u32,
data: &[u8],
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.set_immediates(offset, data)
}
pub fn render_bundle_encoder_draw(
&self,
bundle: &mut RenderBundleEncoder,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), PassStateError> {
bundle.draw(vertex_count, instance_count, first_vertex, first_instance)
}
pub fn render_bundle_encoder_draw_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.draw(vertex_count, instance_count, first_vertex, first_instance)
}
pub fn render_bundle_encoder_draw_indexed(
&self,
bundle: &mut RenderBundleEncoder,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), PassStateError> {
bundle.draw_indexed(
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
)
}
pub fn render_bundle_encoder_draw_indexed_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.draw_indexed(
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
)
}
pub fn render_bundle_encoder_draw_indirect(
&self,
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
bundle.draw_indirect(buffer_id, offset)
}
pub fn render_bundle_encoder_draw_indirect_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.draw_indirect(buffer_id, offset)
}
pub fn render_bundle_encoder_draw_indexed_indirect(
&self,
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
bundle.draw_indexed_indirect(buffer_id, offset)
}
pub fn render_bundle_encoder_draw_indexed_indirect_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.draw_indexed_indirect(buffer_id, offset)
}
pub fn render_bundle_encoder_push_debug_group(
&self,
bundle: &mut RenderBundleEncoder,
label: &str,
) -> Result<(), PassStateError> {
bundle.push_debug_group(label)
}
pub fn render_bundle_encoder_push_debug_group_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
label: &str,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.push_debug_group(label)
}
pub fn render_bundle_encoder_pop_debug_group(
&self,
bundle: &mut RenderBundleEncoder,
) -> Result<(), PassStateError> {
bundle.pop_debug_group()
}
pub fn render_bundle_encoder_pop_debug_group_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.pop_debug_group()
}
pub fn render_bundle_encoder_insert_debug_marker(
&self,
bundle: &mut RenderBundleEncoder,
label: &str,
) -> Result<(), PassStateError> {
bundle.insert_debug_marker(label)
}
pub fn render_bundle_encoder_insert_debug_marker_with_id(
&self,
bundle_encoder: id::RenderBundleEncoderId,
label: &str,
) -> Result<(), PassStateError> {
let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
let mut bundle_encoder = bundle_encoder
.try_lock()
.expect("RenderBundleEncoders should not be accessed concurrently");
bundle_encoder.insert_debug_marker(label)
}
}
pub mod bundle_ffi {
use super::RenderBundleEncoder;
use crate::{id, RawString};
use core::slice;
use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
#[deprecated(note = "Use `Global::render_bundle_encoder_set_bind_group` instead.")]
pub unsafe fn wgpu_render_bundle_set_bind_group(
bundle: &mut RenderBundleEncoder,
index: u32,
bind_group_id: Option<id::BindGroupId>,
offsets: *const DynamicOffset,
offset_length: usize,
) {
let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) };
let _ = bundle.set_bind_group(index, bind_group_id, offsets);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_set_pipeline` instead.")]
pub fn wgpu_render_bundle_set_pipeline(
bundle: &mut RenderBundleEncoder,
pipeline_id: id::RenderPipelineId,
) {
let _ = bundle.set_pipeline(pipeline_id);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_set_vertex_buffer` instead.")]
pub fn wgpu_render_bundle_set_vertex_buffer(
bundle: &mut RenderBundleEncoder,
slot: u32,
buffer_id: Option<id::BufferId>,
offset: BufferAddress,
size: Option<BufferSize>,
) {
let _ = bundle.set_vertex_buffer(slot, buffer_id, offset, size);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_set_index_buffer` instead.")]
pub fn wgpu_render_bundle_set_index_buffer(
encoder: &mut RenderBundleEncoder,
buffer: id::BufferId,
index_format: IndexFormat,
offset: BufferAddress,
size: Option<BufferSize>,
) {
let _ = encoder.set_index_buffer(buffer, index_format, offset, size);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_set_immediates` instead.")]
pub unsafe fn wgpu_render_bundle_set_immediates(
pass: &mut RenderBundleEncoder,
offset: u32,
size_bytes: u32,
data: *const u8,
) {
let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) };
let _ = pass.set_immediates(offset, data_slice);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_draw` instead.")]
pub fn wgpu_render_bundle_draw(
bundle: &mut RenderBundleEncoder,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
let _ = bundle.draw(vertex_count, instance_count, first_vertex, first_instance);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_draw_indexed` instead.")]
pub fn wgpu_render_bundle_draw_indexed(
bundle: &mut RenderBundleEncoder,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
let _ = bundle.draw_indexed(
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_draw_indirect` instead.")]
pub fn wgpu_render_bundle_draw_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
let _ = bundle.draw_indirect(buffer_id, offset);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_draw_indexed_indirect` instead.")]
pub fn wgpu_render_bundle_draw_indexed_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
let _ = bundle.draw_indexed_indirect(buffer_id, offset);
}
#[deprecated(note = "Use `Global::render_bundle_encoder_push_debug_group` instead.")]
pub unsafe fn wgpu_render_bundle_push_debug_group(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
}
#[deprecated(note = "Use `Global::render_bundle_encoder_pop_debug_group` instead.")]
pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
}
#[deprecated(note = "Use `Global::render_bundle_encoder_insert_debug_marker` instead.")]
pub unsafe fn wgpu_render_bundle_insert_debug_marker(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
}
}