[error] render bundles

This commit is contained in:
Dzmitry Malyshau 2020-11-21 00:05:56 -05:00
parent 6307294e2b
commit c6bc37dbde
11 changed files with 548 additions and 493 deletions

View File

@ -91,7 +91,9 @@ fn main() {
None,
id
));
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
id
}
_ => panic!("Expected Action::Init"),

View File

@ -117,8 +117,12 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
}
}
}
self.command_encoder_finish::<B>(encoder, &wgt::CommandBufferDescriptor { label: None })
.unwrap()
let (cmd_buf, error) = self
.command_encoder_finish::<B>(encoder, &wgt::CommandBufferDescriptor { label: None });
if let Some(e) = error {
panic!("{:?}", e);
}
cmd_buf
}
fn process<B: wgc::hub::GfxBackend>(
@ -138,7 +142,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreateBuffer(id, desc) => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.device_create_buffer::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::FreeBuffer(id) => {
self.buffer_destroy::<B>(id).unwrap();
@ -149,7 +155,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreateTexture(id, desc) => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.device_create_texture::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::FreeTexture(id) => {
self.texture_destroy::<B>(id).unwrap();
@ -164,7 +172,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
} => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.texture_create_view::<B>(parent_id, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyTextureView(id) => {
self.texture_view_drop::<B>(id).unwrap();
@ -172,7 +182,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreateSampler(id, desc) => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.device_create_sampler::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroySampler(id) => {
self.sampler_drop::<B>(id);
@ -187,7 +199,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
}
A::CreateBindGroupLayout(id, desc) => {
let (_, error) = self.device_create_bind_group_layout::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyBindGroupLayout(id) => {
self.bind_group_layout_drop::<B>(id);
@ -195,7 +209,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreatePipelineLayout(id, desc) => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.device_create_pipeline_layout::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyPipelineLayout(id) => {
self.pipeline_layout_drop::<B>(id);
@ -203,7 +219,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreateBindGroup(id, desc) => {
self.device_maintain_ids::<B>(device).unwrap();
let (_, error) = self.device_create_bind_group::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyBindGroup(id) => {
self.bind_group_drop::<B>(id);
@ -224,7 +242,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
label,
};
let (_, error) = self.device_create_shader_module::<B>(device, &desc, id);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyShaderModule(id) => {
self.shader_module_drop::<B>(id);
@ -233,7 +253,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
self.device_maintain_ids::<B>(device).unwrap();
let (_, _, error) =
self.device_create_compute_pipeline::<B>(device, &desc, id, None);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyComputePipeline(id) => {
self.compute_pipeline_drop::<B>(id);
@ -242,7 +264,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
self.device_maintain_ids::<B>(device).unwrap();
let (_, _, error) =
self.device_create_render_pipeline::<B>(device, &desc, id, None);
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyRenderPipeline(id) => {
self.render_pipeline_drop::<B>(id);
@ -250,12 +274,14 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::CreateRenderBundle { id, desc, base } => {
let bundle =
wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
self.render_bundle_encoder_finish::<B>(
let (_, error) = self.render_bundle_encoder_finish::<B>(
bundle,
&wgt::RenderBundleDescriptor { label: desc.label },
id,
)
.unwrap();
);
if let Some(e) = error {
panic!("{:?}", e);
}
}
A::DestroyRenderBundle(id) => {
self.render_bundle_drop::<B>(id);
@ -288,13 +314,14 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
.unwrap();
}
A::Submit(_index, commands) => {
let encoder = self
.device_create_command_encoder::<B>(
device,
&wgt::CommandEncoderDescriptor { label: None },
comb_manager.alloc(device.backend()),
)
.unwrap();
let (encoder, error) = self.device_create_command_encoder::<B>(
device,
&wgt::CommandEncoderDescriptor { label: None },
comb_manager.alloc(device.backend()),
);
if let Some(e) = error {
panic!("{:?}", e);
}
let cmdbuf = self.encode_commands::<B>(encoder, commands);
self.queue_submit::<B>(device, &[cmdbuf]).unwrap();
}

View File

@ -98,7 +98,9 @@ impl Test<'_> {
None,
device
));
assert_eq!(error, None);
if let Some(e) = error {
panic!("{:?}", e);
}
let mut command_buffer_id_manager = wgc::hub::IdentityManager::default();
println!("\t\t\tRunning...");

View File

@ -26,7 +26,7 @@ use std::{
use thiserror::Error;
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateBindGroupLayoutError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -40,7 +40,7 @@ pub enum CreateBindGroupLayoutError {
TooManyBindings(BindingTypeMaxCountError),
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateBindGroupError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -96,7 +96,7 @@ pub enum CreateBindGroupError {
DepthStencilAspect,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum BindingZone {
#[error("stage {0:?}")]
Stage(wgt::ShaderStage),
@ -104,7 +104,7 @@ pub enum BindingZone {
Pipeline,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
#[error("too many bindings of type {kind:?} in {zone}, limit is {count}")]
pub struct BindingTypeMaxCountError {
pub kind: BindingTypeMaxCountErrorKind,
@ -112,7 +112,7 @@ pub struct BindingTypeMaxCountError {
pub count: u32,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub enum BindingTypeMaxCountErrorKind {
DynamicUniformBuffers,
DynamicStorageBuffers,
@ -336,7 +336,7 @@ impl<B: hal::Backend> Resource for BindGroupLayout<B> {
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreatePipelineLayoutError {
#[error(transparent)]
Device(#[from] DeviceError),

View File

@ -44,9 +44,10 @@ use crate::{
},
conv,
device::{
AttachmentData, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS, SHADER_STAGE_COUNT,
AttachmentData, Device, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS,
SHADER_STAGE_COUNT,
},
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Resource, Storage, Token},
hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Resource, Storage, Token},
id,
resource::BufferUse,
span,
@ -55,7 +56,7 @@ use crate::{
Label, LabelHelpers, LifeGuard, Stored, MAX_BIND_GROUPS,
};
use arrayvec::ArrayVec;
use std::{borrow::Cow, iter, marker::PhantomData, ops::Range};
use std::{borrow::Cow, iter, ops::Range};
use thiserror::Error;
/// Describes a [`RenderBundleEncoder`].
@ -111,9 +112,328 @@ impl RenderBundleEncoder {
})
}
pub fn dummy(parent_id: id::DeviceId) -> Self {
Self {
base: BasePass::new(),
parent_id,
context: RenderPassContext {
attachments: AttachmentData {
colors: ArrayVec::new(),
resolves: ArrayVec::new(),
depth_stencil: None,
},
sample_count: 0,
},
}
}
pub fn parent(&self) -> id::DeviceId {
self.parent_id
}
pub(crate) fn finish<B: hal::Backend, G: GlobalIdentityHandlerFactory>(
self,
desc: &RenderBundleDescriptor,
device: &Device<B>,
hub: &Hub<B, G>,
token: &mut Token<Device<B>>,
) -> Result<RenderBundle, RenderBundleError> {
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(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(self.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,
pipeline: StateChange::new(),
};
let mut commands = Vec::new();
let mut base = self.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 scope = PassErrorScope::SetBindGroup(bind_group_id);
let max_bind_groups = device.limits.max_bind_groups;
if (index as u32) >= max_bind_groups {
return Err(RenderCommandError::BindGroupIndexOutOfRange {
index,
max: max_bind_groups,
})
.map_pass_err(scope);
}
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..];
// Check for misaligned offsets.
if let Some(offset) = offsets
.iter()
.map(|offset| *offset as wgt::BufferAddress)
.find(|offset| offset % wgt::BIND_BUFFER_ALIGNMENT != 0)
{
return Err(RenderCommandError::UnalignedBufferOffset(offset))
.map_pass_err(scope);
}
let bind_group = state
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
.map_pass_err(scope)?;
if bind_group.dynamic_binding_info.len() != offsets.len() {
return Err(RenderCommandError::InvalidDynamicOffsetCount {
actual: offsets.len(),
expected: bind_group.dynamic_binding_info.len(),
})
.map_pass_err(scope);
}
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets);
state
.trackers
.merge_extend(&bind_group.used)
.map_pass_err(scope)?;
}
RenderCommand::SetPipeline(pipeline_id) => {
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
if state.pipeline.set_and_check_redundant(pipeline_id) {
continue;
}
let pipeline = state
.trackers
.render_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.unwrap();
self.context
.check_compatible(&pipeline.pass_context)
.map_err(RenderCommandError::IncompatiblePipeline)
.map_pass_err(scope)?;
//TODO: check read-only depth
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 scope = PassErrorScope::SetIndexBuffer(buffer_id);
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX)
.map_pass_err(scope)?;
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 scope = PassErrorScope::SetVertexBuffer(buffer_id);
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX)
.map_pass_err(scope)?;
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 scope = PassErrorScope::SetPushConstant;
let end_offset = offset + size_bytes;
let pipeline_layout_id = pipeline_layout_id
.ok_or(DrawError::MissingPipeline)
.map_pass_err(scope)?;
let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
pipeline_layout
.validate_push_constant_ranges(stages, offset, end_offset)
.map_pass_err(scope)?;
commands.push(command);
}
RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
let scope = PassErrorScope::Draw;
let (vertex_limit, instance_limit) = state.vertex_limits();
let last_vertex = first_vertex + vertex_count;
if last_vertex > vertex_limit {
return Err(DrawError::VertexBeyondLimit {
last_vertex,
vertex_limit,
})
.map_pass_err(scope);
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
return Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})
.map_pass_err(scope);
}
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 scope = PassErrorScope::DrawIndexed;
//TODO: validate that base_vertex + max_index() is within the provided range
let (_, instance_limit) = state.vertex_limits();
let index_limit = state.index.limit();
let last_index = first_index + index_count;
if last_index > index_limit {
return Err(DrawError::IndexBeyondLimit {
last_index,
index_limit,
})
.map_pass_err(scope);
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
return Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})
.map_pass_err(scope);
}
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 scope = PassErrorScope::DrawIndirect;
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_pass_err(scope)?;
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset: _,
count: None,
indexed: true,
} => {
let scope = PassErrorScope::DrawIndexedIndirect;
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.map_err(|err| RenderCommandError::Buffer(buffer_id, err))
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_pass_err(scope)?;
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"),
}
}
let _ = desc.label; //TODO: actually use
Ok(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(self.parent_id),
ref_count: device.life_guard.add_ref(),
},
used: state.trackers,
context: self.context,
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
})
}
}
/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.
@ -150,6 +470,11 @@ unsafe impl Send for RenderBundle {}
unsafe impl Sync for RenderBundle {}
impl RenderBundle {
#[cfg(feature = "trace")]
pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand> {
BasePass::from_ref(self.base.as_ref())
}
/// Actually encode the contents into a native command buffer.
///
/// This is partially duplicating the logic of `command_encoder_run_render_pass`.
@ -685,6 +1010,13 @@ pub struct RenderBundleError {
inner: RenderBundleErrorInner,
}
impl RenderBundleError {
pub(crate) const INVALID_DEVICE: Self = RenderBundleError {
scope: PassErrorScope::Bundle,
inner: RenderBundleErrorInner::Device(DeviceError::Invalid),
};
}
impl<T, E> MapPassErr<T, RenderBundleError> for Result<T, E>
where
E: Into<RenderBundleErrorInner>,
@ -697,356 +1029,6 @@ where
}
}
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 scope = PassErrorScope::Bundle;
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)
.map_pass_err(scope)?;
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,
pipeline: StateChange::new(),
};
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 scope = PassErrorScope::SetBindGroup(bind_group_id);
let max_bind_groups = device.limits.max_bind_groups;
if (index as u32) >= max_bind_groups {
return Err(RenderCommandError::BindGroupIndexOutOfRange {
index,
max: max_bind_groups,
})
.map_pass_err(scope);
}
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets =
&base.dynamic_offsets[num_dynamic_offsets as usize..];
// Check for misaligned offsets.
if let Some(offset) = offsets
.iter()
.map(|offset| *offset as wgt::BufferAddress)
.find(|offset| offset % wgt::BIND_BUFFER_ALIGNMENT != 0)
{
return Err(RenderCommandError::UnalignedBufferOffset(offset))
.map_pass_err(scope);
}
let bind_group = state
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
.map_pass_err(scope)?;
if bind_group.dynamic_binding_info.len() != offsets.len() {
return Err(RenderCommandError::InvalidDynamicOffsetCount {
actual: offsets.len(),
expected: bind_group.dynamic_binding_info.len(),
})
.map_pass_err(scope);
}
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets);
state
.trackers
.merge_extend(&bind_group.used)
.map_pass_err(scope)?;
}
RenderCommand::SetPipeline(pipeline_id) => {
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
if state.pipeline.set_and_check_redundant(pipeline_id) {
continue;
}
let pipeline = state
.trackers
.render_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.unwrap();
bundle_encoder
.context
.check_compatible(&pipeline.pass_context)
.map_err(RenderCommandError::IncompatiblePipeline)
.map_pass_err(scope)?;
//TODO: check read-only depth
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 scope = PassErrorScope::SetIndexBuffer(buffer_id);
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX)
.map_pass_err(scope)?;
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 scope = PassErrorScope::SetVertexBuffer(buffer_id);
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX)
.map_pass_err(scope)?;
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 scope = PassErrorScope::SetPushConstant;
let end_offset = offset + size_bytes;
let pipeline_layout_id = pipeline_layout_id
.ok_or(DrawError::MissingPipeline)
.map_pass_err(scope)?;
let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
pipeline_layout
.validate_push_constant_ranges(stages, offset, end_offset)
.map_pass_err(scope)?;
commands.push(command);
}
RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
first_instance,
} => {
let scope = PassErrorScope::Draw;
let (vertex_limit, instance_limit) = state.vertex_limits();
let last_vertex = first_vertex + vertex_count;
if last_vertex > vertex_limit {
return Err(DrawError::VertexBeyondLimit {
last_vertex,
vertex_limit,
})
.map_pass_err(scope);
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
return Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})
.map_pass_err(scope);
}
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 scope = PassErrorScope::DrawIndexed;
//TODO: validate that base_vertex + max_index() is within the provided range
let (_, instance_limit) = state.vertex_limits();
let index_limit = state.index.limit();
let last_index = first_index + index_count;
if last_index > index_limit {
return Err(DrawError::IndexBeyondLimit {
last_index,
index_limit,
})
.map_pass_err(scope);
}
let last_instance = first_instance + instance_count;
if last_instance > instance_limit {
return Err(DrawError::InstanceBeyondLimit {
last_instance,
instance_limit,
})
.map_pass_err(scope);
}
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 scope = PassErrorScope::DrawIndirect;
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.unwrap();
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_pass_err(scope)?;
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
buffer_id,
offset: _,
count: None,
indexed: true,
} => {
let scope = PassErrorScope::DrawIndexedIndirect;
let buffer = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT)
.map_err(|err| RenderCommandError::Buffer(buffer_id, err))
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT)
.map_pass_err(scope)?;
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; //TODO: actually use
//TODO: check if the device is still alive
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(desc.label.borrow_or_default()),
}
};
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")]
if let Some(ref trace) = device.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()),
});
}
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};

View File

@ -186,7 +186,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
encoder_id: id::CommandEncoderId,
_desc: &wgt::CommandBufferDescriptor<Label>,
) -> Result<id::CommandBufferId, CommandEncoderError> {
) -> (id::CommandBufferId, Option<CommandEncoderError>) {
span!(_guard, INFO, "CommandEncoder::finish");
let hub = B::hub(self);
@ -194,18 +194,25 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (swap_chain_guard, mut token) = hub.swap_chains.read(&mut token);
//TODO: actually close the last recorded command buffer
let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
let cmd_buf = CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id)?;
cmd_buf.is_recording = false;
// stop tracking the swapchain image, if used
if let Some((ref sc_id, _)) = cmd_buf.used_swap_chain {
let view_id = swap_chain_guard[sc_id.value]
.acquired_view_id
.as_ref()
.expect("Used swap chain frame has already presented");
cmd_buf.trackers.views.remove(view_id.value);
}
tracing::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
Ok(encoder_id)
let error = match CommandBuffer::get_encoder(&mut *cmd_buf_guard, encoder_id) {
Ok(cmd_buf) => {
cmd_buf.is_recording = false;
// stop tracking the swapchain image, if used
if let Some((ref sc_id, _)) = cmd_buf.used_swap_chain {
let view_id = swap_chain_guard[sc_id.value]
.acquired_view_id
.as_ref()
.expect("Used swap chain frame has already presented");
cmd_buf.trackers.views.remove(view_id.value);
}
tracing::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
None
}
Err(e) => Some(e),
};
(encoder_id, error)
}
pub fn command_encoder_push_debug_group<B: GfxBackend>(

View File

@ -2206,10 +2206,10 @@ impl<B: hal::Backend> crate::hub::Resource for Device<B> {
}
#[error("device is invalid")]
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub struct InvalidDevice;
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum DeviceError {
#[error("parent device is invalid")]
Invalid,
@ -3288,72 +3288,66 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::CommandEncoderDescriptor<Label>,
id_in: Input<G, id::CommandEncoderId>,
) -> Result<id::CommandEncoderId, command::CommandAllocatorError> {
) -> (id::CommandEncoderId, Option<command::CommandAllocatorError>) {
span!(_guard, INFO, "Device::create_command_encoder");
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(device_id)
.map_err(|_| DeviceError::Invalid)?;
let error = loop {
let device = match device_guard.get(device_id) {
Ok(device) => device,
Err(_) => break DeviceError::Invalid.into(),
};
let dev_stored = Stored {
value: id::Valid(device_id),
ref_count: device.life_guard.add_ref(),
let dev_stored = Stored {
value: id::Valid(device_id),
ref_count: device.life_guard.add_ref(),
};
let mut command_buffer = match device.cmd_allocator.allocate(
dev_stored,
&device.raw,
device.limits.clone(),
device.private_features,
&desc.label,
#[cfg(feature = "trace")]
device.trace.is_some(),
) {
Ok(cmd_buf) => cmd_buf,
Err(e) => break e,
};
unsafe {
let raw_command_buffer = command_buffer.raw.last_mut().unwrap();
if let Some(ref label) = desc.label {
device
.raw
.set_command_buffer_name(raw_command_buffer, label);
}
raw_command_buffer.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT);
}
let id = hub
.command_buffers
.register_identity(id_in, command_buffer, &mut token);
return (id.0, None);
};
let mut command_buffer = device.cmd_allocator.allocate(
dev_stored,
&device.raw,
device.limits.clone(),
device.private_features,
&desc.label,
#[cfg(feature = "trace")]
device.trace.is_some(),
)?;
unsafe {
let raw_command_buffer = command_buffer.raw.last_mut().unwrap();
if let Some(ref label) = desc.label {
device
.raw
.set_command_buffer_name(raw_command_buffer, label);
}
raw_command_buffer.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT);
}
let id = hub
.command_buffers
.register_identity(id_in, command_buffer, &mut token);
Ok(id.0)
}
pub fn command_encoder_error<B: GfxBackend>(
&self,
id_in: Input<G, id::CommandEncoderId>,
) -> id::CommandEncoderId {
B::hub(self)
.command_buffers
.register_error(id_in, "", &mut Token::root())
let id = B::hub(self).command_buffers.register_error(
id_in,
desc.label.borrow_or_default(),
&mut token,
);
(id, Some(error))
}
pub fn command_buffer_label<B: GfxBackend>(&self, id: id::CommandBufferId) -> String {
B::hub(self).command_buffers.label_for_resource(id)
}
pub fn command_buffer_error<B: GfxBackend>(
&self,
id_in: Input<G, id::CommandBufferId>,
label: Option<&str>,
) -> id::CommandBufferId {
B::hub(self)
.command_buffers
.register_error(id_in, label.unwrap_or(""), &mut Token::root())
}
pub fn command_encoder_drop<B: GfxBackend>(&self, command_encoder_id: id::CommandEncoderId) {
span!(_guard, INFO, "CommandEncoder::drop");
@ -3380,28 +3374,81 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
device_id: id::DeviceId,
desc: &command::RenderBundleEncoderDescriptor,
) -> Result<id::RenderBundleEncoderId, command::CreateRenderBundleError> {
) -> (
id::RenderBundleEncoderId,
Option<command::CreateRenderBundleError>,
) {
span!(_guard, INFO, "Device::create_render_bundle_encoder");
let encoder = command::RenderBundleEncoder::new(desc, device_id, None);
encoder.map(|encoder| Box::into_raw(Box::new(encoder)))
let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id, None) {
Ok(encoder) => (encoder, None),
Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)),
};
(Box::into_raw(Box::new(encoder)), error)
}
pub fn render_bundle_encoder_finish<B: GfxBackend>(
&self,
bundle_encoder: command::RenderBundleEncoder,
desc: &command::RenderBundleDescriptor,
id_in: Input<G, id::RenderBundleId>,
) -> (id::RenderBundleId, Option<command::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 error = loop {
let device = match device_guard.get(bundle_encoder.parent()) {
Ok(device) => device,
Err(_) => break command::RenderBundleError::INVALID_DEVICE,
};
let render_bundle = match bundle_encoder.finish(desc, device, &hub, &mut token) {
Ok(bundle) => bundle,
Err(e) => break e,
};
tracing::debug!("Render bundle {:?} = {:#?}", id_in, render_bundle.used);
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")]
if let Some(ref trace) = 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: bundle.to_base_pass(),
});
}
device
.trackers
.lock()
.bundles
.init(id, ref_count, PhantomData)
.unwrap();
return (id.0, None);
};
let id = B::hub(self).render_bundles.register_error(
id_in,
desc.label.borrow_or_default(),
&mut token,
);
(id, Some(error))
}
pub fn render_bundle_label<B: GfxBackend>(&self, id: id::RenderBundleId) -> String {
B::hub(self).render_bundles.label_for_resource(id)
}
pub fn render_bundle_error<B: GfxBackend>(
&self,
id_in: Input<G, id::RenderBundleId>,
label: Option<&str>,
) -> id::RenderBundleId {
let hub = B::hub(self);
let mut token = Token::root();
let (_, mut token) = hub.devices.read(&mut token);
hub.render_bundles
.register_error(id_in, label.unwrap_or(""), &mut token)
}
pub fn render_bundle_drop<B: GfxBackend>(&self, render_bundle_id: id::RenderBundleId) {
span!(_guard, INFO, "RenderBundle::drop");
let hub = B::hub(self);
@ -3529,18 +3576,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
B::hub(self).render_pipelines.label_for_resource(id)
}
pub fn render_pipeline_error<B: GfxBackend>(
&self,
id_in: Input<G, id::RenderPipelineId>,
label: Option<&str>,
) -> id::RenderPipelineId {
let hub = B::hub(self);
let mut token = Token::root();
let (_, mut token) = hub.devices.read(&mut token);
hub.render_pipelines
.register_error(id_in, label.unwrap_or(""), &mut token)
}
pub fn render_pipeline_drop<B: GfxBackend>(&self, render_pipeline_id: id::RenderPipelineId) {
span!(_guard, INFO, "RenderPipeline::drop");
let hub = B::hub(self);

View File

@ -417,7 +417,7 @@ impl AdapterInfo {
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
/// Error when requesting a device from the adaptor
pub enum RequestDeviceError {
#[error("parent adapter is invalid")]

View File

@ -57,7 +57,7 @@ impl<B: hal::Backend> Resource for ShaderModule<B> {
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateShaderModuleError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -80,7 +80,7 @@ pub struct ProgrammableStageDescriptor<'a> {
/// Number of implicit bind groups derived at pipeline creation.
pub type ImplicitBindGroupCount = u8;
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum ImplicitLayoutError {
#[error("missing IDs for deriving {0} bind groups")]
MissingIds(ImplicitBindGroupCount),
@ -104,7 +104,7 @@ pub struct ComputePipelineDescriptor<'a> {
pub compute_stage: ProgrammableStageDescriptor<'a>,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateComputePipelineError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -193,7 +193,7 @@ pub struct RenderPipelineDescriptor<'a> {
pub alpha_to_coverage_enabled: bool,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateRenderPipelineError {
#[error(transparent)]
Device(#[from] DeviceError),

View File

@ -126,7 +126,7 @@ impl BufferMapOperation {
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum BufferAccessError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -166,7 +166,7 @@ pub struct Buffer<B: hal::Backend> {
pub(crate) map_state: BufferMapState<B>,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateBufferError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -207,14 +207,14 @@ pub struct Texture<B: hal::Backend> {
pub(crate) life_guard: LifeGuard,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub enum TextureErrorDimension {
X,
Y,
Z,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum TextureDimensionError {
#[error("Dimension {0:?} is zero")]
Zero(TextureErrorDimension),
@ -224,7 +224,7 @@ pub enum TextureDimensionError {
InvalidSampleCount(u32),
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateTextureError {
#[error(transparent)]
Device(#[from] DeviceError),
@ -305,7 +305,7 @@ pub struct TextureView<B: hal::Backend> {
pub(crate) life_guard: LifeGuard,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateTextureViewError {
#[error("parent texture is invalid or destroyed")]
InvalidTexture,
@ -411,7 +411,7 @@ pub struct Sampler<B: hal::Backend> {
pub(crate) comparison: bool,
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum CreateSamplerError {
#[error(transparent)]
Device(#[from] DeviceError),

View File

@ -8,7 +8,7 @@ use std::collections::hash_map::Entry;
use thiserror::Error;
use wgt::{BindGroupLayoutEntry, BindingType};
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
#[error("buffer usage is {actual:?} which does not contain required usage {expected:?}")]
pub struct MissingBufferUsageError {
pub(crate) actual: wgt::BufferUsage,
@ -28,7 +28,7 @@ pub fn check_buffer_usage(
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
#[error("texture usage is {actual:?} which does not contain required usage {expected:?}")]
pub struct MissingTextureUsageError {
pub(crate) actual: wgt::TextureUsage,
@ -48,7 +48,7 @@ pub fn check_texture_usage(
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum BindingError {
#[error("binding is missing from the pipeline layout")]
Missing,
@ -78,7 +78,7 @@ pub enum BindingError {
BadStorageFormat(wgt::TextureFormat),
}
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum InputError {
#[error("input is not provided by the earlier stage in the pipeline")]
Missing,
@ -87,7 +87,7 @@ pub enum InputError {
}
/// Errors produced when validating a programmable stage of a pipeline.
#[derive(Clone, Debug, Error, PartialEq)]
#[derive(Clone, Debug, Error)]
pub enum StageError {
#[error("shader module is invalid")]
InvalidModule,