#![allow(clippy::reversed_empty_ranges)]
use crate::{
command::{BasePass, DrawError, RenderCommand, RenderCommandError},
conv,
device::{
AttachmentData, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS, SHADER_STAGE_COUNT,
},
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Storage, Token},
id,
resource::BufferUse,
span,
track::TrackerSet,
validation::check_buffer_usage,
Label, LifeGuard, RefCount, Stored, MAX_BIND_GROUPS,
};
use arrayvec::ArrayVec;
use std::{
borrow::{Borrow, Cow},
iter,
marker::PhantomData,
ops::Range,
};
use thiserror::Error;
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct RenderBundleEncoderDescriptor<'a> {
pub label: Label<'a>,
pub color_formats: Cow<'a, [wgt::TextureFormat]>,
pub depth_stencil_format: Option<wgt::TextureFormat>,
pub sample_count: u32,
}
#[derive(Debug)]
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub struct RenderBundleEncoder {
base: BasePass<RenderCommand>,
parent_id: id::DeviceId,
pub(crate) context: RenderPassContext,
}
impl RenderBundleEncoder {
pub fn new(
desc: &RenderBundleEncoderDescriptor,
parent_id: id::DeviceId,
base: Option<BasePass<RenderCommand>>,
) -> Result<Self, CreateRenderBundleError> {
span!(_guard, INFO, "RenderBundleEncoder::new");
Ok(RenderBundleEncoder {
base: base.unwrap_or_else(BasePass::new),
parent_id,
context: RenderPassContext {
attachments: AttachmentData {
colors: desc.color_formats.iter().cloned().collect(),
resolves: ArrayVec::new(),
depth_stencil: desc.depth_stencil_format,
},
sample_count: {
let sc = desc.sample_count;
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
return Err(CreateRenderBundleError::InvalidSampleCount(sc));
}
sc as u8
},
},
})
}
pub fn parent(&self) -> id::DeviceId {
self.parent_id
}
}
#[derive(Clone, Debug, Error)]
pub enum CreateRenderBundleError {
#[error("invalid number of samples {0}")]
InvalidSampleCount(u32),
}
pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
#[derive(Debug)]
pub struct RenderBundle {
base: BasePass<RenderCommand>,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) used: TrackerSet,
pub(crate) context: RenderPassContext,
pub(crate) life_guard: LifeGuard,
}
unsafe impl Send for RenderBundle {}
unsafe impl Sync for RenderBundle {}
impl RenderBundle {
pub(crate) unsafe fn execute<B: GfxBackend>(
&self,
cmd_buf: &mut B::CommandBuffer,
pipeline_layout_guard: &Storage<
crate::binding_model::PipelineLayout<B>,
id::PipelineLayoutId,
>,
bind_group_guard: &Storage<crate::binding_model::BindGroup<B>, id::BindGroupId>,
pipeline_guard: &Storage<crate::pipeline::RenderPipeline<B>, id::RenderPipelineId>,
buffer_guard: &Storage<crate::resource::Buffer<B>, id::BufferId>,
) {
use hal::command::CommandBuffer as _;
let mut offsets = self.base.dynamic_offsets.as_slice();
let mut index_type = hal::IndexType::U16;
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
for command in self.base.commands.iter() {
match *command {
RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
} => {
let bind_group = bind_group_guard.get(bind_group_id).unwrap();
cmd_buf.bind_graphics_descriptor_sets(
&pipeline_layout_guard[pipeline_layout_id.unwrap()].raw,
index as usize,
iter::once(bind_group.raw.raw()),
&offsets[..num_dynamic_offsets as usize],
);
offsets = &offsets[num_dynamic_offsets as usize..];
}
RenderCommand::SetPipeline(pipeline_id) => {
let pipeline = pipeline_guard.get(pipeline_id).unwrap();
cmd_buf.bind_graphics_pipeline(&pipeline.raw);
index_type = conv::map_index_format(pipeline.index_format);
pipeline_layout_id = Some(pipeline.layout_id.value);
}
RenderCommand::SetIndexBuffer {
buffer_id,
offset,
size,
} => {
let buffer = buffer_guard.get(buffer_id).unwrap();
let view = hal::buffer::IndexBufferView {
buffer: &buffer.raw,
range: hal::buffer::SubRange {
offset,
size: size.map(|s| s.get()),
},
index_type,
};
cmd_buf.bind_index_buffer(view);
}
RenderCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
size,
} => {
let buffer = buffer_guard.get(buffer_id).unwrap();
let range = hal::buffer::SubRange {
offset,
size: size.map(|s| s.get()),
};
cmd_buf.bind_vertex_buffers(slot, iter::once((&buffer.raw, range)));
}
RenderCommand::SetPushConstant {
stages,
offset,
size_bytes,
values_offset,
} => {
let pipeline_layout_id = pipeline_layout_id.unwrap();
let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
if let Some(values_offset) = values_offset {
let values_end_offset = (values_offset + size_bytes / 4) as usize;
let data_slice = &self.base.push_constant_data
[(values_offset as usize)..values_end_offset];
cmd_buf.push_graphics_constants(
&pipeline_layout.raw,
conv::map_shader_stage_flags(stages),
offset,
&data_slice,
)
} else {
super::push_constant_clear(
offset,
size_bytes,
|clear_offset, clear_data| {
cmd_buf.push_graphics_constants(
&pipeline_layout.raw,
conv::map_shader_stage_flags(stages),
clear_offset,
clear_data,
);
},
);
}
}
RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
cmd_buf.draw(
first_vertex..first_vertex + vertex_count,
first_instance..first_instance + instance_count,
);
}
RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
} => {
cmd_buf.draw_indexed(
first_index..first_index + index_count,
base_vertex,
first_instance..first_instance + instance_count,
);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset,
count: None,
indexed: false,
} => {
let buffer = buffer_guard.get(buffer_id).unwrap();
cmd_buf.draw_indirect(&buffer.raw, offset, 1, 0);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset,
count: None,
indexed: true,
} => {
let buffer = buffer_guard.get(buffer_id).unwrap();
cmd_buf.draw_indexed_indirect(&buffer.raw, offset, 1, 0);
}
RenderCommand::MultiDrawIndirect { .. }
| RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(),
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
RenderCommand::PopDebugGroup => unimplemented!(),
RenderCommand::ExecuteBundle(_)
| RenderCommand::SetBlendColor(_)
| RenderCommand::SetStencilReference(_)
| RenderCommand::SetViewport { .. }
| RenderCommand::SetScissor(_) => unreachable!(),
}
}
}
}
impl Borrow<RefCount> for RenderBundle {
fn borrow(&self) -> &RefCount {
self.life_guard.ref_count.as_ref().unwrap()
}
}
#[derive(Debug)]
struct IndexState {
buffer: Option<id::BufferId>,
format: wgt::IndexFormat,
range: Range<wgt::BufferAddress>,
is_dirty: bool,
}
impl IndexState {
fn new() -> Self {
IndexState {
buffer: None,
format: wgt::IndexFormat::default(),
range: 0..0,
is_dirty: false,
}
}
fn limit(&self) -> u32 {
assert!(self.buffer.is_some());
let bytes_per_index = match self.format {
wgt::IndexFormat::Uint16 => 2,
wgt::IndexFormat::Uint32 => 4,
};
((self.range.end - self.range.start) / bytes_per_index) as u32
}
fn flush(&mut self) -> Option<RenderCommand> {
if self.is_dirty {
self.is_dirty = false;
Some(RenderCommand::SetIndexBuffer {
buffer_id: self.buffer.unwrap(),
offset: self.range.start,
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
} else {
None
}
}
fn set_format(&mut self, format: wgt::IndexFormat) {
if self.format != format {
self.format = format;
self.is_dirty = true;
}
}
fn set_buffer(&mut self, id: id::BufferId, range: Range<wgt::BufferAddress>) {
self.buffer = Some(id);
self.range = range;
self.is_dirty = true;
}
}
#[derive(Debug)]
struct VertexState {
buffer: Option<id::BufferId>,
range: Range<wgt::BufferAddress>,
stride: wgt::BufferAddress,
rate: wgt::InputStepMode,
is_dirty: bool,
}
impl VertexState {
fn new() -> Self {
VertexState {
buffer: None,
range: 0..0,
stride: 0,
rate: wgt::InputStepMode::Vertex,
is_dirty: false,
}
}
fn set_buffer(&mut self, buffer_id: id::BufferId, range: Range<wgt::BufferAddress>) {
self.buffer = Some(buffer_id);
self.range = range;
self.is_dirty = true;
}
fn flush(&mut self, slot: u32) -> Option<RenderCommand> {
if self.is_dirty {
self.is_dirty = false;
Some(RenderCommand::SetVertexBuffer {
slot,
buffer_id: self.buffer.unwrap(),
offset: self.range.start,
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
} else {
None
}
}
}
#[derive(Debug)]
struct BindState {
bind_group: Option<(id::BindGroupId, id::BindGroupLayoutId)>,
dynamic_offsets: Range<usize>,
is_dirty: bool,
}
impl BindState {
fn new() -> Self {
BindState {
bind_group: None,
dynamic_offsets: 0..0,
is_dirty: false,
}
}
fn set_group(
&mut self,
bind_group_id: id::BindGroupId,
layout_id: id::BindGroupLayoutId,
dyn_offset: usize,
dyn_count: usize,
) -> bool {
match self.bind_group {
Some((bg_id, _)) if bg_id == bind_group_id && dyn_count == 0 => false,
_ => {
self.bind_group = Some((bind_group_id, layout_id));
self.dynamic_offsets = dyn_offset..dyn_offset + dyn_count;
self.is_dirty = true;
true
}
}
}
}
#[derive(Debug)]
struct PushConstantState {
ranges: ArrayVec<[wgt::PushConstantRange; SHADER_STAGE_COUNT]>,
is_dirty: bool,
}
impl PushConstantState {
fn new() -> Self {
Self {
ranges: ArrayVec::new(),
is_dirty: false,
}
}
fn set_push_constants(&mut self, new_ranges: &[wgt::PushConstantRange]) -> bool {
if &*self.ranges != new_ranges {
self.ranges = new_ranges.iter().cloned().collect();
self.is_dirty = true;
true
} else {
false
}
}
}
#[derive(Debug)]
struct State {
trackers: TrackerSet,
index: IndexState,
vertex: ArrayVec<[VertexState; MAX_VERTEX_BUFFERS]>,
bind: ArrayVec<[BindState; MAX_BIND_GROUPS]>,
push_constant_ranges: PushConstantState,
raw_dynamic_offsets: Vec<wgt::DynamicOffset>,
flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
used_bind_groups: usize,
}
impl State {
fn vertex_limits(&self) -> (u32, u32) {
let mut vertex_limit = !0;
let mut instance_limit = !0;
for vbs in &self.vertex {
if vbs.stride == 0 {
continue;
}
let limit = ((vbs.range.end - vbs.range.start) / vbs.stride) as u32;
match vbs.rate {
wgt::InputStepMode::Vertex => vertex_limit = vertex_limit.min(limit),
wgt::InputStepMode::Instance => instance_limit = instance_limit.min(limit),
}
}
(vertex_limit, instance_limit)
}
fn invalidate_group_from(&mut self, slot: usize) {
for bind in self.bind[slot..].iter_mut() {
if bind.bind_group.is_some() {
bind.is_dirty = true;
}
}
}
fn set_bind_group(
&mut self,
slot: u8,
bind_group_id: id::BindGroupId,
layout_id: id::Valid<id::BindGroupLayoutId>,
offsets: &[wgt::DynamicOffset],
) {
if self.bind[slot as usize].set_group(
bind_group_id,
layout_id.0,
self.raw_dynamic_offsets.len(),
offsets.len(),
) {
self.invalidate_group_from(slot as usize + 1);
}
self.raw_dynamic_offsets.extend(offsets);
}
fn set_pipeline(
&mut self,
index_format: wgt::IndexFormat,
vertex_strides: &[(wgt::BufferAddress, wgt::InputStepMode)],
layout_ids: &[id::Valid<id::BindGroupLayoutId>],
push_constant_layouts: &[wgt::PushConstantRange],
) {
self.index.set_format(index_format);
for (vs, &(stride, step_mode)) in self.vertex.iter_mut().zip(vertex_strides) {
if vs.stride != stride || vs.rate != step_mode {
vs.stride = stride;
vs.rate = step_mode;
vs.is_dirty = true;
}
}
let push_constants_changed = self
.push_constant_ranges
.set_push_constants(push_constant_layouts);
self.used_bind_groups = layout_ids.len();
let invalid_from = if push_constants_changed {
Some(0)
} else {
self.bind
.iter()
.zip(layout_ids)
.position(|(bs, layout_id)| match bs.bind_group {
Some((_, bgl_id)) => bgl_id != layout_id.0,
None => false,
})
};
if let Some(slot) = invalid_from {
self.invalidate_group_from(slot);
}
}
fn flush_push_constants(&mut self) -> Option<impl Iterator<Item = RenderCommand>> {
let is_dirty = self.push_constant_ranges.is_dirty;
if is_dirty {
let nonoverlapping_ranges =
super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges.ranges);
Some(
nonoverlapping_ranges
.into_iter()
.map(|range| RenderCommand::SetPushConstant {
stages: range.stages,
offset: range.range.start,
size_bytes: range.range.end - range.range.start,
values_offset: None,
}),
)
} else {
None
}
}
fn flush_vertices(&mut self) -> impl Iterator<Item = RenderCommand> + '_ {
self.vertex
.iter_mut()
.enumerate()
.flat_map(|(i, vs)| vs.flush(i as u32))
}
fn flush_binds(&mut self) -> impl Iterator<Item = RenderCommand> + '_ {
for bs in self.bind[..self.used_bind_groups].iter() {
if bs.is_dirty {
self.flat_dynamic_offsets
.extend_from_slice(&self.raw_dynamic_offsets[bs.dynamic_offsets.clone()]);
}
}
self.bind
.iter_mut()
.take(self.used_bind_groups)
.enumerate()
.flat_map(|(i, bs)| {
if bs.is_dirty {
bs.is_dirty = false;
Some(RenderCommand::SetBindGroup {
index: i as u8,
bind_group_id: bs.bind_group.unwrap().0,
num_dynamic_offsets: (bs.dynamic_offsets.end - bs.dynamic_offsets.start)
as u8,
})
} else {
None
}
})
}
}
#[derive(Clone, Debug, Error)]
pub enum RenderBundleError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
RenderCommand(#[from] RenderCommandError),
#[error(transparent)]
Draw(#[from] DrawError),
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn render_bundle_encoder_finish<B: GfxBackend>(
&self,
bundle_encoder: RenderBundleEncoder,
desc: &RenderBundleDescriptor,
id_in: Input<G, id::RenderBundleId>,
) -> Result<id::RenderBundleId, RenderBundleError> {
span!(_guard, INFO, "RenderBundleEncoder::finish");
let hub = B::hub(self);
let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = device_guard
.get(bundle_encoder.parent_id)
.map_err(|_| DeviceError::Invalid)?;
let render_bundle = {
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
let (buffer_guard, _) = hub.buffers.read(&mut token);
let mut state = State {
trackers: TrackerSet::new(bundle_encoder.parent_id.backend()),
index: IndexState::new(),
vertex: (0..MAX_VERTEX_BUFFERS)
.map(|_| VertexState::new())
.collect(),
bind: (0..MAX_BIND_GROUPS).map(|_| BindState::new()).collect(),
push_constant_ranges: PushConstantState::new(),
raw_dynamic_offsets: Vec::new(),
flat_dynamic_offsets: Vec::new(),
used_bind_groups: 0,
};
let mut commands = Vec::new();
let mut base = bundle_encoder.base.as_ref();
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
for &command in base.commands {
match command {
RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
} => {
let max_bind_groups = device.limits.max_bind_groups;
if (index as u32) >= max_bind_groups {
Err(RenderCommandError::BindGroupIndexOutOfRange {
index,
max: max_bind_groups,
})?
}
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets =
&base.dynamic_offsets[num_dynamic_offsets as usize..];
if let Some(offset) = offsets
.iter()
.map(|offset| *offset as wgt::BufferAddress)
.find(|offset| offset % wgt::BIND_BUFFER_ALIGNMENT != 0)
{
Err(RenderCommandError::UnalignedBufferOffset(offset))?
}
let bind_group = state
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))?;
if bind_group.dynamic_binding_info.len() != offsets.len() {
Err(RenderCommandError::InvalidDynamicOffsetCount {
actual: offsets.len(),
expected: bind_group.dynamic_binding_info.len(),
})?
}
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets);
state.trackers.merge_extend(&bind_group.used);
}
RenderCommand::SetPipeline(pipeline_id) => {
let pipeline = state
.trackers
.render_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.unwrap();
if !bundle_encoder.context.compatible(&pipeline.pass_context) {
Err(RenderCommandError::IncompatiblePipeline)?
}
let layout = &pipeline_layout_guard[pipeline.layout_id.value];
pipeline_layout_id = Some(pipeline.layout_id.value);
state.set_pipeline(
pipeline.index_format,
&pipeline.vertex_strides,
&layout.bind_group_layout_ids,
&layout.push_constant_ranges,
);
commands.push(command);
if let Some(iter) = state.flush_push_constants() {
commands.extend(iter)
}
}
RenderCommand::SetIndexBuffer {
buffer_id,
offset,
size,
} => {
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX)
.map_err(RenderCommandError::from)?;
let end = match size {
Some(s) => offset + s.get(),
None => buffer.size,
};
state.index.set_buffer(buffer_id, offset..end);
}
RenderCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
size,
} => {
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX)
.map_err(RenderCommandError::from)?;
let end = match size {
Some(s) => offset + s.get(),
None => buffer.size,
};
state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
}
RenderCommand::SetPushConstant {
stages,
offset,
size_bytes,
values_offset: _,
} => {
let end_offset = offset + size_bytes;
let pipeline_layout_id =
pipeline_layout_id.ok_or(DrawError::MissingPipeline)?;
let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
pipeline_layout
.validate_push_constant_ranges(stages, offset, end_offset)
.map_err(RenderCommandError::from)?;
commands.push(command);
}
RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
let (vertex_limit, instance_limit) = state.vertex_limits();
let last_vertex = first_vertex + vertex_count;
if last_vertex > vertex_limit {
Err(DrawError::VertexBeyondLimit {
last_vertex,
vertex_limit,
})?
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})?
}
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex: _,
first_instance,
} => {
let (_, instance_limit) = state.vertex_limits();
let index_limit = state.index.limit();
let last_index = first_index + index_count;
if last_index > index_limit {
Err(DrawError::IndexBeyondLimit {
last_index,
index_limit,
})?
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})?
}
commands.extend(state.index.flush());
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset: _,
count: None,
indexed: false,
} => {
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_err(RenderCommandError::from)?;
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset: _,
count: None,
indexed: true,
} => {
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.map_err(|err| RenderCommandError::Buffer(buffer_id, err))?;
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_err(RenderCommandError::from)?;
commands.extend(state.index.flush());
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::MultiDrawIndirect { .. }
| RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(),
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
RenderCommand::PopDebugGroup => unimplemented!(),
RenderCommand::ExecuteBundle(_)
| RenderCommand::SetBlendColor(_)
| RenderCommand::SetStencilReference(_)
| RenderCommand::SetViewport { .. }
| RenderCommand::SetScissor(_) => {
unreachable!("not supported by a render bundle")
}
}
}
tracing::debug!("Render bundle {:?} = {:#?}", id_in, state.trackers);
let _ = desc.label;
RenderBundle {
base: BasePass {
commands,
dynamic_offsets: state.flat_dynamic_offsets,
string_data: Vec::new(),
push_constant_data: Vec::new(),
},
device_id: Stored {
value: id::Valid(bundle_encoder.parent_id),
ref_count: device.life_guard.add_ref(),
},
used: state.trackers,
context: bundle_encoder.context,
life_guard: LifeGuard::new(),
}
};
let ref_count = render_bundle.life_guard.add_ref();
let id = hub
.render_bundles
.register_identity(id_in, render_bundle, &mut token);
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => {
use crate::device::trace;
let (bundle_guard, _) = hub.render_bundles.read(&mut token);
let bundle = &bundle_guard[id];
let label = desc.label.as_ref().map(|l| l.as_ref());
trace.lock().add(trace::Action::CreateRenderBundle {
id: id.0,
desc: trace::new_render_bundle_encoder_descriptor(label, &bundle.context),
base: BasePass::from_ref(bundle.base.as_ref()),
});
}
None => {}
}
device
.trackers
.lock()
.bundles
.init(id, ref_count, PhantomData)
.unwrap();
Ok(id.0)
}
}
pub mod bundle_ffi {
use super::{RenderBundleEncoder, RenderCommand};
use crate::{id, span, RawString};
use std::{convert::TryInto, slice};
use wgt::{BufferAddress, BufferSize, DynamicOffset};
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_bind_group(
bundle: &mut RenderBundleEncoder,
index: u32,
bind_group_id: id::BindGroupId,
offsets: *const DynamicOffset,
offset_length: usize,
) {
span!(_guard, DEBUG, "RenderBundle::set_bind_group");
bundle.base.commands.push(RenderCommand::SetBindGroup {
index: index.try_into().unwrap(),
num_dynamic_offsets: offset_length.try_into().unwrap(),
bind_group_id,
});
bundle
.base
.dynamic_offsets
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_set_pipeline(
bundle: &mut RenderBundleEncoder,
pipeline_id: id::RenderPipelineId,
) {
span!(_guard, DEBUG, "RenderBundle::set_pipeline");
bundle
.base
.commands
.push(RenderCommand::SetPipeline(pipeline_id));
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_set_index_buffer(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
size: Option<BufferSize>,
) {
span!(_guard, DEBUG, "RenderBundle::set_index_buffer");
bundle.base.commands.push(RenderCommand::SetIndexBuffer {
buffer_id,
offset,
size,
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_set_vertex_buffer(
bundle: &mut RenderBundleEncoder,
slot: u32,
buffer_id: id::BufferId,
offset: BufferAddress,
size: Option<BufferSize>,
) {
span!(_guard, DEBUG, "RenderBundle::set_vertex_buffer");
bundle.base.commands.push(RenderCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
size,
});
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_push_constants(
pass: &mut RenderBundleEncoder,
stages: wgt::ShaderStage,
offset: u32,
size_bytes: u32,
data: *const u32,
) {
span!(_guard, DEBUG, "RenderBundle::set_push_constants");
let data_slice = slice::from_raw_parts(data, (size_bytes / 4) as usize);
let value_offset = pass.base.push_constant_data.len().try_into().expect(
"Ran out of push constant space. Don't set 4gb of push constants per RenderBundle.",
);
pass.base.push_constant_data.extend_from_slice(data_slice);
pass.base.commands.push(RenderCommand::SetPushConstant {
stages,
offset,
size_bytes,
values_offset: Some(value_offset),
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_draw(
bundle: &mut RenderBundleEncoder,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
span!(_guard, DEBUG, "RenderBundle::draw");
bundle.base.commands.push(RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_draw_indexed(
bundle: &mut RenderBundleEncoder,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
span!(_guard, DEBUG, "RenderBundle::draw_indexed");
bundle.base.commands.push(RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_draw_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
span!(_guard, DEBUG, "RenderBundle::draw_indirect");
bundle.base.commands.push(RenderCommand::MultiDrawIndirect {
buffer_id,
offset,
count: None,
indexed: false,
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_pass_bundle_indexed_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
span!(_guard, DEBUG, "RenderBundle::draw_indexed_indirect");
bundle.base.commands.push(RenderCommand::MultiDrawIndirect {
buffer_id,
offset,
count: None,
indexed: true,
});
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_push_debug_group(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
span!(_guard, DEBUG, "RenderBundle::push_debug_group");
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
span!(_guard, DEBUG, "RenderBundle::pop_debug_group");
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_insert_debug_marker(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
span!(_guard, DEBUG, "RenderBundle::insert_debug_marker");
}
}