810: Convert the existing error types to `thiserror` r=kvark a=GabrielMajeri

**Connections**
Part of #638

**Description**
Converts the crate's existing error types from a manual `Display` implementation to using `thiserror`.

**Testing**
Tested with core and `wgpu-rs`.

Co-authored-by: Gabriel Majeri <gabriel.majeri6@gmail.com>
This commit is contained in:
bors[bot] 2020-07-18 04:51:46 +00:00 committed by GitHub
commit a689aea3f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 231 additions and 430 deletions

View File

@ -17,28 +17,31 @@ use serde::Deserialize;
#[cfg(feature = "trace")]
use serde::Serialize;
use std::{borrow::Borrow, fmt};
use std::{borrow::Borrow, ops::Range};
#[derive(Clone, Debug)]
use thiserror::Error;
#[derive(Clone, Debug, Error)]
pub enum BindGroupLayoutError {
#[error("conflicting binding at index {0}")]
ConflictBinding(u32),
#[error("required device feature is missing: {0:?}")]
MissingFeature(wgt::Features),
/// Arrays of bindings can't be 0 elements long
#[error("arrays of bindings can't be 0 elements long")]
ZeroCount,
/// Arrays of bindings unsupported for this type of binding
#[error("arrays of bindings unsupported for this type of binding")]
ArrayUnsupported,
/// Bindings go over binding count limits
#[error(transparent)]
TooManyBindings(BindingTypeMaxCountError),
}
#[derive(Clone, Debug)]
pub enum BindGroupError {
/// Number of bindings in bind group descriptor does not match
/// the number of bindings defined in the bind group layout.
#[derive(Clone, Debug, Error)]
pub enum CreateBindGroupError {
#[error("number of bindings in bind group descriptor ({actual}) does not match the number of bindings defined in the bind group layout ({expected})")]
BindingsNumMismatch { actual: usize, expected: usize },
/// Unable to find a corresponding declaration for the given binding,
#[error("unable to find a corresponding declaration for the given binding {0}")]
MissingBindingDeclaration(u32),
/// The given binding has a different type than the one in the layout.
#[error("binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
WrongBindingType {
// Index of the binding
binding: u32,
@ -47,14 +50,14 @@ pub enum BindGroupError {
// Human-readable description of expected types
expected: &'static str,
},
/// The given sampler is/is not a comparison sampler,
/// while the layout type indicates otherwise.
#[error("the given sampler is/is not a comparison sampler, while the layout type indicates otherwise")]
WrongSamplerComparison,
/// Uniform buffer binding range exceeds [`wgt::Limits::max_uniform_buffer_binding_size`] limit
#[error("uniform buffer binding range exceeds `max_uniform_buffer_binding_size` limit")]
UniformBufferRangeTooLarge,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
#[error("too many bindings of type {kind:?} in stage {stage:?}, limit is {count}")]
pub struct BindingTypeMaxCountError {
pub kind: BindingTypeMaxCountErrorKind,
pub stage: wgt::ShaderStage,
@ -78,6 +81,7 @@ pub(crate) struct PerStageBindingTypeCounter {
fragment: u32,
compute: u32,
}
impl PerStageBindingTypeCounter {
pub(crate) fn add(&mut self, stage: wgt::ShaderStage, count: u32) {
if stage.contains(wgt::ShaderStage::VERTEX) {
@ -231,84 +235,66 @@ pub struct BindGroupLayout<B: hal::Backend> {
pub(crate) count_validator: BindingTypeMaxCountValidator,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum PipelineLayoutError {
TooManyGroups(usize),
#[error("bind group layout count {actual} exceeds device bind group limit {max}")]
TooManyGroups { actual: usize, max: usize },
#[error(transparent)]
TooManyBindings(BindingTypeMaxCountError),
PushConstantRangeTooLarge { index: usize },
MoreThanOnePushConstantRangePerStage { index: usize },
MisalignedPushConstantRange { index: usize },
#[error("push constant at index {index} has range {}..{} which exceeds device push constant size limit 0..{max}", range.start, range.end)]
PushConstantRangeTooLarge {
index: usize,
range: Range<u32>,
max: u32,
},
#[error("push constant range (index {index}) provides for stage(s) {provided:?} but there exists another range that provides stage(s) {intersected:?}. Each stage may only be provided by one range")]
MoreThanOnePushConstantRangePerStage {
index: usize,
provided: wgt::ShaderStage,
intersected: wgt::ShaderStage,
},
#[error(
"push constant at index {index} has range bound {bound} not aligned to {}",
wgt::PUSH_CONSTANT_ALIGNMENT
)]
MisalignedPushConstantRange { index: usize, bound: u32 },
#[error("device does not have required feature: {0:?}")]
MissingFeature(wgt::Features),
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum PushConstantUploadError {
#[error("provided push constant with indices {offset}..{end_offset} overruns matching push constant range at index {idx}, with stage(s) {:?} and indices {:?}", range.stages, range.range)]
TooLarge {
offset: u32,
end_offset: u32,
idx: usize,
range: wgt::PushConstantRange,
},
#[error("provided push constant is for stage(s) {actual:?}, stage with a partial match found at index {idx} with stage(s) {matched:?}, however push constants must be complete matches")]
PartialRangeMatch {
actual: wgt::ShaderStage,
idx: usize,
matched: wgt::ShaderStage,
},
#[error("provided push constant is for stage(s) {actual:?}, but intersects a push constant range (at index {idx}) with stage(s) {missing:?}. Push constants must provide the stages for all ranges they intersect")]
MissingStages {
actual: wgt::ShaderStage,
idx: usize,
missing: wgt::ShaderStage,
},
#[error("provided push constant is for stage(s) {actual:?}, however the pipeline layout has no push constant range for the stage(s) {unmatched:?}")]
UnmatchedStages {
actual: wgt::ShaderStage,
unmatched: wgt::ShaderStage,
},
#[error(
"provided push constant offset {0} must be aligned to {}",
wgt::PUSH_CONSTANT_ALIGNMENT
)]
Unaligned(u32),
}
impl fmt::Display for PushConstantUploadError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooLarge { offset, end_offset, idx, range } => write!(
f,
"provided push constant with indices {}..{} overruns matching push constant range (index {}) with stage(s) {:?} and indices {}..{}",
offset,
end_offset,
idx,
range.stages,
range.range.start,
range.range.end,
),
Self::PartialRangeMatch { actual, idx, matched } => write!(
f,
"provided push constant is for stage(s) {:?}, stage with a partial match found at index {} with stage(s) {:?}, however push constants must be complete matches",
actual,
idx,
matched,
),
Self::MissingStages { actual, idx, missing } => write!(
f,
"provided push constant is for stage(s) {:?}, but intersects a push constant range (at index {}) with stage(s) {:?}. Push constants must provide the stages for all ranges they intersect",
actual,
idx,
missing,
),
Self::UnmatchedStages { actual, unmatched } => write!(
f,
"provided push constant is for stage(s) {:?}, however the pipeline layout has no push constant range for the stage(s) {:?}",
actual,
unmatched,
),
Self::Unaligned(offset) => write!(
f,
"provided push constant offset {} must be aligned to {}",
offset,
wgt::PUSH_CONSTANT_ALIGNMENT,
)
}
}
}
#[derive(Debug)]
pub struct PipelineLayout<B: hal::Backend> {
pub(crate) raw: B::PipelineLayout,
@ -423,29 +409,16 @@ pub type BindGroupEntry<'a> = wgt::BindGroupEntry<BindingResource<'a>>;
pub type BindGroupDescriptor<'a> =
wgt::BindGroupDescriptor<'a, BindGroupLayoutId, BindGroupEntry<'a>>;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum BindError {
#[error("number of dynamic offsets ({actual}) doesn't match the number of dynamic bindings in the bind group layout ({expected})")]
MismatchedDynamicOffsetCount { actual: usize, expected: usize },
#[error("dynamic binding at index {idx} is not properly aligned")]
UnalignedDynamicBinding { idx: usize },
#[error("dynamic binding at index {idx} would overrun the buffer")]
DynamicBindingOutOfBounds { idx: usize },
}
impl fmt::Display for BindError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::MismatchedDynamicOffsetCount { actual, expected } =>
write!(
f,
"number of dynamic offsets ({}) doesn't match the number of dynamic bindings in the bind group layout ({})",
actual,
expected,
),
Self::UnalignedDynamicBinding { idx } => write!(f, "dynamic binding at index {} is not properly aligned", idx),
Self::DynamicBindingOutOfBounds { idx } => write!(f, "dynamic binding at index {} would overrun the buffer", idx),
}
}
}
#[derive(Debug)]
pub struct BindGroupDynamicBindingData {
/// The maximum value the dynamic offset can have before running off the end of the buffer.

View File

@ -46,10 +46,12 @@ use crate::{
resource::BufferUse,
span,
track::TrackerSet,
validation::{check_buffer_usage, MissingBufferUsageError, MissingTextureUsageError},
LifeGuard, RefCount, Stored, MAX_BIND_GROUPS,
};
use arrayvec::ArrayVec;
use std::{borrow::Borrow, fmt, iter, marker::PhantomData, ops::Range};
use std::{borrow::Borrow, iter, marker::PhantomData, ops::Range};
use thiserror::Error;
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub struct RenderBundleEncoder {
@ -91,19 +93,12 @@ impl RenderBundleEncoder {
}
/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum CreateRenderBundleError {
#[error("invalid number of samples {0}")]
InvalidSampleCount(u32),
}
impl fmt::Display for CreateRenderBundleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidSampleCount(count) => write!(f, "invalid number of samples {}", count),
}
}
}
//Note: here, `RenderBundle` is just wrapping a raw stream of render commands.
// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle,
// or Metal indirect command buffer.
@ -599,119 +594,38 @@ impl State {
}
/// Error encountered when encoding a render command.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum RenderCommandError {
BindGroupIndexOutOfRange {
index: u8,
max: u32,
},
#[error("bind group index {index} is greater than the device's requested `max_bind_group` limit {max}")]
BindGroupIndexOutOfRange { index: u8, max: u32 },
#[error(
"dynamic buffer offset {0} is not a multiple of the required buffer alignment {}",
wgt::BIND_BUFFER_ALIGNMENT
)]
UnalignedBufferOffset(u64),
InvalidDynamicOffsetCount {
actual: usize,
expected: usize,
},
#[error("number of buffer offsets ({actual}) does not match the number of dynamic bindings ({expected})")]
InvalidDynamicOffsetCount { actual: usize, expected: usize },
#[error("render pipeline output formats and sample counts do not match render pass attachment formats")]
IncompatiblePipeline,
#[error("pipeline is not compatible with the depth-stencil read-only render pass")]
IncompatibleReadOnlyDepthStencil,
MissingBufferUsage {
actual: wgt::BufferUsage,
expected: wgt::BufferUsage,
},
InvalidTextureUsage {
actual: wgt::TextureUsage,
expected: wgt::TextureUsage,
},
#[error(transparent)]
MissingBufferUsage(#[from] MissingBufferUsageError),
#[error(transparent)]
MissingTextureUsage(#[from] MissingTextureUsageError),
#[error("a render pipeline must be bound")]
UnboundPipeline,
PushConstants(PushConstantUploadError),
VertexBeyondLimit {
last_vertex: u32,
vertex_limit: u32,
},
#[error(transparent)]
PushConstants(#[from] PushConstantUploadError),
#[error("vertex {last_vertex} extends beyond limit {vertex_limit}")]
VertexBeyondLimit { last_vertex: u32, vertex_limit: u32 },
#[error("instance {last_instance} extends beyond limit {instance_limit}")]
InstanceBeyondLimit {
last_instance: u32,
instance_limit: u32,
},
IndexBeyondLimit {
last_index: u32,
index_limit: u32,
},
}
impl From<PushConstantUploadError> for RenderCommandError {
fn from(error: PushConstantUploadError) -> Self {
Self::PushConstants(error)
}
}
impl fmt::Display for RenderCommandError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BindGroupIndexOutOfRange { index, max } => write!(
f,
"bind group index {} is greater than the device's requested `max_bind_group` limit {}",
index,
max,
),
Self::UnalignedBufferOffset(offset) => write!(
f,
"dynamic buffer offset {} is not a multiple of the required buffer alignment {}",
offset,
wgt::BIND_BUFFER_ALIGNMENT,
),
Self::InvalidDynamicOffsetCount { actual, expected } => write!(
f,
"number of buffer offsets ({}) does not match the number of dynamic bindings ({})",
actual,
expected,
),
Self::IncompatiblePipeline => write!(f, "render pipeline output formats and sample counts do not match render pass attachment formats"),
Self::IncompatibleReadOnlyDepthStencil => write!(f, "pipeline is not compatible with the depth-stencil read-only render pass"),
Self::MissingBufferUsage { actual, expected } => write!(
f,
"buffer usage is {:?} which does not contain required usage {:?}",
actual,
expected,
),
Self::InvalidTextureUsage { actual, expected } => write!(
f,
"texture usage is {:?} which does not contain required usage {:?}",
actual,
expected,
),
Self::UnboundPipeline => write!(f, "a render pipeline must be bound"),
Self::PushConstants(error) => write!(f, "{}", error),
Self::VertexBeyondLimit { last_vertex, vertex_limit } => write!(
f,
"vertex {} extends beyond limit {}",
last_vertex,
vertex_limit,
),
Self::InstanceBeyondLimit { last_instance, instance_limit } => write!(
f,
"instance {} extends beyond limit {}",
last_instance,
instance_limit,
),
Self::IndexBeyondLimit { last_index, index_limit } => write!(
f,
"index {} extends beyond limit {}",
last_index,
index_limit,
),
}
}
}
/// Checks that the given buffer usage contains the required buffer usage,
/// returns an error otherwise.
pub fn check_buffer_usage(
actual: wgt::BufferUsage,
expected: wgt::BufferUsage,
) -> Result<(), RenderCommandError> {
if !actual.contains(expected) {
Err(RenderCommandError::MissingBufferUsage { actual, expected })
} else {
Ok(())
}
#[error("index {last_index} extends beyond limit {index_limit}")]
IndexBeyondLimit { last_index: u32, index_limit: u32 },
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {

View File

@ -13,9 +13,11 @@ use crate::{
id,
resource::BufferUse,
span,
validation::{check_buffer_usage, MissingBufferUsageError},
};
use hal::command::CommandBuffer as _;
use thiserror::Error;
use wgt::{BufferAddress, BufferUsage};
use std::{fmt, iter, str};
@ -108,55 +110,20 @@ struct State {
debug_scope_depth: u32,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum ComputePassError {
BindGroupIndexOutOfRange {
index: u8,
max: u32,
},
#[error("bind group index {index} is greater than the device's requested `max_bind_group` limit {max}")]
BindGroupIndexOutOfRange { index: u8, max: u32 },
#[error("a compute pipeline must be bound")]
UnboundPipeline,
MissingBufferUsage {
actual: BufferUsage,
expected: BufferUsage,
},
#[error(transparent)]
MissingBufferUsage(#[from] MissingBufferUsageError),
#[error("cannot pop debug group, because number of pushed debug groups is zero")]
InvalidPopDebugGroup,
Bind(BindError),
PushConstants(PushConstantUploadError),
}
impl From<PushConstantUploadError> for ComputePassError {
fn from(error: PushConstantUploadError) -> Self {
Self::PushConstants(error)
}
}
impl From<BindError> for ComputePassError {
fn from(error: BindError) -> Self {
Self::Bind(error)
}
}
impl fmt::Display for ComputePassError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BindGroupIndexOutOfRange { index, max } => write!(
f,
"bind group index {} is greater than the device's requested `max_bind_group` limit {}",
index,
max,
),
Self::UnboundPipeline => write!(f, "a compute pipeline must be bound"),
Self::MissingBufferUsage { actual, expected } => write!(
f,
"buffer usage is {:?} which does not contain required usage {:?}",
actual,
expected,
),
Self::InvalidPopDebugGroup => write!(f, "cannot pop debug group, because number of pushed debug groups is zero"),
Self::Bind(error) => write!(f, "{}", error),
Self::PushConstants(error) => write!(f, "{}", error),
}
}
#[error(transparent)]
Bind(#[from] BindError),
#[error(transparent)]
PushConstants(#[from] PushConstantUploadError),
}
// Common routines between render/compute
@ -381,12 +348,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
(),
BufferUse::INDIRECT,
);
if !src_buffer.usage.contains(BufferUsage::INDIRECT) {
return Err(ComputePassError::MissingBufferUsage {
actual: src_buffer.usage,
expected: BufferUsage::INDIRECT,
});
}
check_buffer_usage(src_buffer.usage, BufferUsage::INDIRECT)?;
let barriers = src_pending.map(|pending| pending.into_hal(src_buffer));

View File

@ -6,7 +6,7 @@ use crate::{
binding_model::BindError,
command::{
bind::{Binder, LayoutChange},
check_buffer_usage, BasePass, BasePassRef, RenderCommandError,
BasePass, BasePassRef, RenderCommandError,
},
conv,
device::{
@ -19,11 +19,15 @@ use crate::{
resource::{BufferUse, TextureUse, TextureViewInner},
span,
track::TrackerSet,
validation::{
check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
},
Stored,
};
use arrayvec::ArrayVec;
use hal::command::CommandBuffer as _;
use thiserror::Error;
use wgt::{
BufferAddress, BufferSize, BufferUsage, Color, IndexFormat, InputStepMode, TextureUsage,
};
@ -270,11 +274,15 @@ impl OptionalState {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, Error, PartialEq)]
pub enum DrawError {
#[error("blend color needs to be set")]
MissingBlendColor,
#[error("stencil reference needs to be set")]
MissingStencilReference,
#[error("render pipeline must be set")]
MissingPipeline,
#[error("current render pipeline has a layout which is incompatible with a currently set bind group, first differing at entry index {index}")]
IncompatibleBindGroup {
index: u32,
//expected: BindGroupLayoutId,
@ -282,17 +290,6 @@ pub enum DrawError {
},
}
impl fmt::Display for DrawError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DrawError::MissingBlendColor => write!(f, "blend color needs to be set"),
DrawError::MissingStencilReference => write!(f, "stencil reference needs to be set"),
DrawError::MissingPipeline => write!(f, "render pipeline must be set"),
DrawError::IncompatibleBindGroup { index } => write!(f, "current render pipeline has a layout which is incompatible with a currently set bind group, first differing at entry index {}", index),
}
}
}
#[derive(Debug, Default)]
struct IndexState {
bound_buffer_view: Option<(id::BufferId, Range<BufferAddress>)>,
@ -408,25 +405,34 @@ impl State {
}
/// Error encountered when performing a render pass.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
pub enum RenderPassError {
#[error("attachment's sample count {0} is invalid")]
InvalidSampleCount(u8),
#[error("attachment with resolve target must be multi-sampled")]
InvalidResolveSourceSampleCount,
#[error("resolve target must have a sample count of 1")]
InvalidResolveTargetSampleCount,
#[error("extent state {state_extent:?} must match extent from view {view_extent:?}")]
ExtentStateMismatch {
state_extent: hal::image::Extent,
view_extent: hal::image::Extent,
},
#[error("attempted to use a swap chain image as a depth/stencil attachment")]
SwapChainImageAsDepthStencil,
#[error("unable to clear non-present/read-only depth")]
InvalidDepthOps,
#[error("unable to clear non-present/read-only stencil")]
InvalidStencilOps,
SampleCountMismatch {
actual: u8,
expected: u8,
},
#[error("all attachments must have the same sample count, found {actual} != {expected}")]
SampleCountMismatch { actual: u8, expected: u8 },
#[error("texture view's swap chain must match swap chain in use")]
SwapChainMismatch,
#[error("setting `values_offset` to be `None` is only for internal use in render bundles")]
InvalidValuesOffset,
#[error("required device features not enabled: {0:?}")]
MissingDeviceFeatures(wgt::Features),
#[error("indirect draw with offset {offset}{} uses bytes {begin_offset}..{end_offset} which overruns indirect buffer of size {buffer_size}", count.map_or_else(String::new, |v| format!(" and count {}", v)))]
IndirectBufferOverrun {
offset: u64,
count: Option<u32>,
@ -434,90 +440,33 @@ pub enum RenderPassError {
end_offset: u64,
buffer_size: u64,
},
#[error("indirect draw uses bytes {begin_count_offset}..{end_count_offset} which overruns indirect buffer of size {count_buffer_size}")]
IndirectCountBufferOverrun {
begin_count_offset: u64,
end_count_offset: u64,
count_buffer_size: u64,
},
#[error("cannot pop debug group, because number of pushed debug groups is zero")]
InvalidPopDebugGroup,
#[error("render bundle output formats do not match render pass attachment formats")]
IncompatibleRenderBundle,
RenderCommand(RenderCommandError),
Draw(DrawError),
Bind(BindError),
#[error(transparent)]
RenderCommand(#[from] RenderCommandError),
#[error(transparent)]
Draw(#[from] DrawError),
#[error(transparent)]
Bind(#[from] BindError),
}
impl From<RenderCommandError> for RenderPassError {
fn from(error: RenderCommandError) -> Self {
Self::RenderCommand(error)
impl From<MissingBufferUsageError> for RenderPassError {
fn from(error: MissingBufferUsageError) -> Self {
Self::RenderCommand(error.into())
}
}
impl From<DrawError> for RenderPassError {
fn from(error: DrawError) -> Self {
Self::Draw(error)
}
}
impl From<BindError> for RenderPassError {
fn from(error: BindError) -> Self {
Self::Bind(error)
}
}
impl fmt::Display for RenderPassError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidSampleCount(count) => write!(
f,
"attachment's sample count {} is invalid",
count,
),
Self::InvalidResolveSourceSampleCount => write!(f, "attachment with resolve target must be multi-sampled"),
Self::InvalidResolveTargetSampleCount => write!(f, "resolve target must have a sample count of 1"),
Self::ExtentStateMismatch { state_extent, view_extent } => write!(
f,
"extent state {:?} must match extent from view {:?}",
state_extent,
view_extent,
),
Self::SwapChainImageAsDepthStencil => write!(f, "attempted to use a swap chain image as a depth/stencil attachment"),
Self::InvalidDepthOps => write!(f, "unable to clear non-present/read-only depth"),
Self::InvalidStencilOps => write!(f, "unable to clear non-present/read-only stencil"),
Self::SampleCountMismatch { actual, expected } => write!(
f,
"all attachments must have the same sample count, found {} != {}",
actual,
expected,
),
Self::SwapChainMismatch => write!(f, "texture view's swap chain must match swap chain in use"),
Self::InvalidValuesOffset => write!(f, "setting `values_offset` to be `None` is only for internal use in render bundles"),
Self::MissingDeviceFeatures(expected) => write!(
f,
"required device features not enabled: {:?}",
expected,
),
Self::IndirectBufferOverrun { offset, count, begin_offset, end_offset, buffer_size } => write!(
f,
"indirect draw with offset {}{} uses bytes {}..{} which overruns indirect buffer of size {}",
offset,
count.map_or_else(String::new, |v| format!(" and count {}", v)),
begin_offset,
end_offset,
buffer_size,
),
Self::IndirectCountBufferOverrun { begin_count_offset, end_count_offset, count_buffer_size } => write!(
f,
"indirect draw uses bytes {}..{} which overruns indirect buffer of size {}",
begin_count_offset,
end_count_offset,
count_buffer_size,
),
Self::InvalidPopDebugGroup => write!(f, "cannot pop debug group, because number of pushed debug groups is zero"),
Self::IncompatibleRenderBundle => write!(f, "render bundle output formats do not match render pass attachment formats"),
Self::RenderCommand(error) => write!(f, "{}", error),
Self::Draw(error) => write!(f, "{}", error),
Self::Bind(error) => write!(f, "{}", error),
}
impl From<MissingTextureUsageError> for RenderPassError {
fn from(error: MissingTextureUsageError) -> Self {
Self::RenderCommand(error.into())
}
}
@ -1642,13 +1591,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
for ot in output_attachments {
let texture = &texture_guard[ot.texture_id.value];
if !texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT) {
return Err(RenderCommandError::InvalidTextureUsage {
actual: texture.usage,
expected: TextureUsage::OUTPUT_ATTACHMENT,
}
.into());
}
check_texture_usage(texture.usage, TextureUsage::OUTPUT_ATTACHMENT)?;
// the tracker set of the pass is always in "extend" mode
trackers

View File

@ -14,6 +14,7 @@ use crate::{
};
use hal::command::CommandBuffer as _;
use thiserror::Error;
use wgt::{BufferAddress, BufferUsage, Extent3d, TextureDataLayout, TextureUsage};
use std::iter;
@ -27,36 +28,33 @@ pub type BufferCopyView = wgt::BufferCopyView<BufferId>;
pub type TextureCopyView = wgt::TextureCopyView<TextureId>;
/// Error encountered while attempting a data transfer.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
pub enum TransferError {
/// The source buffer/texture is missing the `COPY_SRC` usage flag.
#[error("source buffer/texture is missing the `COPY_SRC` usage flag")]
MissingCopySrcUsageFlag,
/// The destination buffer/texture is missing the `COPY_DST` usage flag.
#[error("destination buffer/texture is missing the `COPY_DST` usage flag")]
MissingCopyDstUsageFlag,
/// Copy would end up overruning the bounds of the destination buffer/texture.
#[error("copy would end up overruning the bounds of the destination buffer/texture")]
BufferOverrun,
/// Buffer offset is not aligned to block size.
#[error("buffer offset is not aligned to block size")]
UnalignedBufferOffset,
/// Copy size is not a multiple of block size.
#[error("copy size is not a multiple of block size")]
UnalignedCopySize,
/// Copy width is not a multiple of block size.
#[error("copy width is not a multiple of block size")]
UnalignedCopyWidth,
/// Copy height is not a multiple of block size.
#[error("copy height is not a multiple of block size")]
UnalignedCopyHeight,
/// Bytes per row is not a multiple of the required alignment.
#[error("bytes per row is not a multiple of the required alignment")]
UnalignedBytesPerRow,
/// Number of rows per image is not a multiple of the required alignment.
#[error("number of rows per image is not a multiple of the required alignment")]
UnalignedRowsPerImage,
/// Number of bytes per row is less than the number of bytes in a complete row.
#[error("number of bytes per row is less than the number of bytes in a complete row")]
InvalidBytesPerRow,
/// Copy size is invalid.
///
/// This can happen if the image is 1D and the copy height and depth
/// are not both set to 1.
#[error("image is 1D and the copy height and depth are not both set to 1")]
InvalidCopySize,
/// Number of rows per image is invalid.
#[error("number of rows per image is invalid")]
InvalidRowsPerImage,
/// The source and destination layers have different aspects.
#[error("source and destination layers have different aspects")]
MismatchedAspects,
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{
binding_model::{self, BindGroupError},
binding_model::{self, CreateBindGroupError, PipelineLayoutError},
command, conv,
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Hub, Input, Token},
id, pipeline, resource, span, swap_chain,
@ -1350,7 +1350,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::PipelineLayoutDescriptor<id::BindGroupLayoutId>,
id_in: Input<G, id::PipelineLayoutId>,
) -> Result<id::PipelineLayoutId, binding_model::PipelineLayoutError> {
) -> Result<id::PipelineLayoutId, PipelineLayoutError> {
span!(_guard, INFO, "Device::create_pipeline_layout");
let hub = B::hub(self);
@ -1358,75 +1358,53 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
if desc.bind_group_layouts.len() > (device.limits.max_bind_groups as usize) {
log::error!(
"Bind group layout count {} exceeds device bind group limit {}",
desc.bind_group_layouts.len(),
device.limits.max_bind_groups
);
return Err(binding_model::PipelineLayoutError::TooManyGroups(
desc.bind_group_layouts.len(),
));
let bind_group_layouts_count = desc.bind_group_layouts.len();
let device_max_bind_groups = device.limits.max_bind_groups as usize;
if bind_group_layouts_count > device_max_bind_groups {
return Err(PipelineLayoutError::TooManyGroups {
actual: bind_group_layouts_count,
max: device_max_bind_groups,
});
}
if !desc.push_constant_ranges.is_empty()
&& !device.features.contains(wgt::Features::PUSH_CONSTANTS)
{
return Err(binding_model::PipelineLayoutError::MissingFeature(
return Err(PipelineLayoutError::MissingFeature(
wgt::Features::PUSH_CONSTANTS,
));
}
let mut used_stages = wgt::ShaderStage::empty();
for (index, pc) in desc.push_constant_ranges.iter().enumerate() {
if pc.stages.intersects(used_stages) {
log::error!(
"Push constant range (index {}) provides for stage(s) {:?} but there exists another range that provides stage(s) {:?}. Each stage may only be provided by one range.",
return Err(PipelineLayoutError::MoreThanOnePushConstantRangePerStage {
index,
pc.stages,
pc.stages & used_stages,
);
return Err(
binding_model::PipelineLayoutError::MoreThanOnePushConstantRangePerStage {
index,
},
);
provided: pc.stages,
intersected: pc.stages & used_stages,
});
}
used_stages |= pc.stages;
if device.limits.max_push_constant_size < pc.range.end {
log::error!(
"Push constant range (index {}) has range {}..{} which exceeds device push constant size limit 0..{}",
let device_max_pc_size = device.limits.max_push_constant_size;
if device_max_pc_size < pc.range.end {
return Err(PipelineLayoutError::PushConstantRangeTooLarge {
index,
pc.range.start,
pc.range.end,
device.limits.max_push_constant_size
);
return Err(
binding_model::PipelineLayoutError::PushConstantRangeTooLarge { index },
);
range: pc.range.clone(),
max: device_max_pc_size,
});
}
if pc.range.start % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
log::error!(
"Push constant range (index {}) start {} must be aligned to {}",
return Err(PipelineLayoutError::MisalignedPushConstantRange {
index,
pc.range.start,
wgt::PUSH_CONSTANT_ALIGNMENT
);
return Err(
binding_model::PipelineLayoutError::MisalignedPushConstantRange { index },
);
bound: pc.range.start,
});
}
if pc.range.end % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
log::error!(
"Push constant range (index {}) end {} must be aligned to {}",
return Err(PipelineLayoutError::MisalignedPushConstantRange {
index,
pc.range.end,
wgt::PUSH_CONSTANT_ALIGNMENT
);
return Err(
binding_model::PipelineLayoutError::MisalignedPushConstantRange { index },
);
bound: pc.range.end,
});
}
}
@ -1454,7 +1432,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
count_validator
.validate(&device.limits)
.map_err(binding_model::PipelineLayoutError::TooManyBindings)?;
.map_err(PipelineLayoutError::TooManyBindings)?;
let layout = binding_model::PipelineLayout {
raw: pipeline_layout,
@ -1521,7 +1499,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &binding_model::BindGroupDescriptor,
id_in: Input<G, id::BindGroupId>,
) -> Result<id::BindGroupId, BindGroupError> {
) -> Result<id::BindGroupId, CreateBindGroupError> {
use crate::binding_model::BindingResource as Br;
span!(_guard, INFO, "Device::create_bind_group");
@ -1539,7 +1517,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let actual = desc.entries.len();
let expected = bind_group_layout.entries.len();
if actual != expected {
return Err(BindGroupError::BindingsNumMismatch { expected, actual });
return Err(CreateBindGroupError::BindingsNumMismatch { expected, actual });
}
let mut desc_set = {
@ -1590,7 +1568,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let decl = bind_group_layout
.entries
.get(&binding)
.ok_or(BindGroupError::MissingBindingDeclaration(binding))?;
.ok_or(CreateBindGroupError::MissingBindingDeclaration(binding))?;
let descriptors: SmallVec<[_; 1]> = match entry.resource {
Br::Buffer(ref bb) => {
let (pub_usage, internal_use, min_size, dynamic) = match decl.ty {
@ -1618,7 +1596,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
dynamic,
),
_ => {
return Err(BindGroupError::WrongBindingType {
return Err(CreateBindGroupError::WrongBindingType {
binding,
actual: decl.ty.clone(),
expected:
@ -1661,7 +1639,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if pub_usage == wgt::BufferUsage::UNIFORM
&& (device.limits.max_uniform_buffer_binding_size as u64) < bind_size
{
return Err(BindGroupError::UniformBufferRangeTooLarge);
return Err(CreateBindGroupError::UniformBufferRangeTooLarge);
}
// Record binding info for validating dynamic offsets
@ -1696,13 +1674,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// Check the actual sampler to also (not) be a comparison sampler
if sampler.comparison != comparison {
return Err(BindGroupError::WrongSamplerComparison);
return Err(CreateBindGroupError::WrongSamplerComparison);
}
SmallVec::from([hal::pso::Descriptor::Sampler(&sampler.raw)])
}
_ => {
return Err(BindGroupError::WrongBindingType {
return Err(CreateBindGroupError::WrongBindingType {
binding,
actual: decl.ty.clone(),
expected: "Sampler",
@ -1728,7 +1706,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
resource::TextureUse::STORAGE_STORE
},
),
_ => return Err(BindGroupError::WrongBindingType {
_ => return Err(CreateBindGroupError::WrongBindingType {
binding,
actual: decl.ty.clone(),
expected: "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture"
@ -1790,7 +1768,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
(wgt::TextureUsage::SAMPLED, resource::TextureUse::SAMPLED)
}
_ => {
return Err(BindGroupError::WrongBindingType {
return Err(CreateBindGroupError::WrongBindingType {
binding,
actual: decl.ty.clone(),
expected: "SampledTextureArray",

View File

@ -2,23 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt;
use thiserror::Error;
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum Error {
#[error("battery status is unsupported on this platform")]
Unsupported,
#[error("battery status retrieval failed: {0}")]
Error(Box<dyn std::error::Error>),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Unsupported => write!(f, "Battery status is unsupported on this platform"),
Error::Error(err) => write!(f, "Battery status retrieval failed: {}", err),
}
}
}
#[cfg(all(
feature = "battery",
any(

View File

@ -7,6 +7,46 @@ use spirv_headers as spirv;
use thiserror::Error;
use wgt::{BindGroupLayoutEntry, BindingType};
#[derive(Clone, Debug, Error)]
#[error("buffer usage is {actual:?} which does not contain required usage {expected:?}")]
pub struct MissingBufferUsageError {
pub(crate) actual: wgt::BufferUsage,
pub(crate) expected: wgt::BufferUsage,
}
/// Checks that the given buffer usage contains the required buffer usage,
/// returns an error otherwise.
pub fn check_buffer_usage(
actual: wgt::BufferUsage,
expected: wgt::BufferUsage,
) -> Result<(), MissingBufferUsageError> {
if !actual.contains(expected) {
Err(MissingBufferUsageError { actual, expected })
} else {
Ok(())
}
}
#[derive(Clone, Debug, Error)]
#[error("texture usage is {actual:?} which does not contain required usage {expected:?}")]
pub struct MissingTextureUsageError {
pub(crate) actual: wgt::TextureUsage,
pub(crate) expected: wgt::TextureUsage,
}
/// Checks that the given texture usage contains the required texture usage,
/// returns an error otherwise.
pub fn check_texture_usage(
actual: wgt::TextureUsage,
expected: wgt::TextureUsage,
) -> Result<(), MissingTextureUsageError> {
if !actual.contains(expected) {
Err(MissingTextureUsageError { actual, expected })
} else {
Ok(())
}
}
#[derive(Clone, Debug, Error)]
pub enum BindingError {
#[error("binding is missing from the pipeline layout")]