Improve RenderPass attachment errors

This commit is contained in:
Connor Fitzgerald 2023-03-01 17:47:51 -05:00
parent 38f1f0ede0
commit c98f4ed662
3 changed files with 175 additions and 65 deletions

View File

@ -18,7 +18,7 @@ use crate::{
id, id,
init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction}, init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
pipeline::{self, PipelineFlags}, pipeline::{self, PipelineFlags},
resource::{self, Buffer, Texture, TextureView}, resource::{self, Buffer, Texture, TextureView, TextureViewNotRenderableReason},
track::{TextureSelector, UsageConflict, UsageScope}, track::{TextureSelector, UsageConflict, UsageScope},
validation::{ validation::{
check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError, check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
@ -436,6 +436,34 @@ impl State {
} }
} }
/// Describes an attachment location in words.
///
/// Can be used as "the {loc} has..." or "{loc} has..."
#[derive(Debug, Copy, Clone)]
pub enum AttachmentErrorLocation {
Color { index: usize, resolve: bool },
Depth,
}
impl fmt::Display for AttachmentErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
AttachmentErrorLocation::Color {
index,
resolve: false,
} => write!(f, "color attachment at index {index}'s texture view"),
AttachmentErrorLocation::Color {
index,
resolve: true,
} => write!(
f,
"color attachment at index {index}'s resolve texture view"
),
AttachmentErrorLocation::Depth => write!(f, "depth attachment's texture view"),
}
}
}
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub enum ColorAttachmentError { pub enum ColorAttachmentError {
#[error("attachment format {0:?} is not a color format")] #[error("attachment format {0:?} is not a color format")]
@ -453,27 +481,46 @@ pub enum RenderPassErrorInner {
Encoder(#[from] CommandEncoderError), Encoder(#[from] CommandEncoderError),
#[error("attachment texture view {0:?} is invalid")] #[error("attachment texture view {0:?} is invalid")]
InvalidAttachment(id::TextureViewId), InvalidAttachment(id::TextureViewId),
#[error("attachment format {0:?} is not a depth-stencil format")] #[error("the format of the depth-stencil attachment ({0:?}) is not a depth-stencil format")]
InvalidDepthStencilAttachmentFormat(wgt::TextureFormat), InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
#[error("attachment format {0:?} can not be resolved")] #[error("the format of the {location} ({format:?}) is not resolvable")]
UnsupportedResolveTargetFormat(wgt::TextureFormat), UnsupportedResolveTargetFormat {
#[error("missing color or depth_stencil attachments, at least one is required.")] location: AttachmentErrorLocation,
MissingAttachments, format: wgt::TextureFormat,
#[error("attachment texture view is not renderable")] },
TextureViewIsNotRenderable, #[error("no color attachments or depth attachments were provided, at least one attachment of any kind must be provided")]
#[error("attachments have differing sizes: {previous:?} is followed by {mismatch:?}")] MissingAttachments,
AttachmentsDimensionMismatch { #[error("the {location} is not renderable:")]
previous: (&'static str, wgt::Extent3d), TextureViewIsNotRenderable {
mismatch: (&'static str, wgt::Extent3d), location: AttachmentErrorLocation,
#[source]
reason: TextureViewNotRenderableReason,
},
#[error("attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}")]
AttachmentsDimensionMismatch {
expected_location: AttachmentErrorLocation,
expected_extent: wgt::Extent3d,
actual_location: AttachmentErrorLocation,
actual_extent: wgt::Extent3d,
},
#[error("attachments have differing sample counts: the {expected_location} has count {expected_samples:?} but is followed by the {actual_location} which has count {actual_samples:?}")]
AttachmentSampleCountMismatch {
expected_location: AttachmentErrorLocation,
expected_samples: u32,
actual_location: AttachmentErrorLocation,
actual_samples: u32,
},
#[error("the resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
InvalidResolveSampleCounts {
location: AttachmentErrorLocation,
src: u32,
dst: u32,
}, },
#[error("attachment's sample count {0} is invalid")]
InvalidSampleCount(u32),
#[error("resolve source must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
InvalidResolveSampleCounts { src: u32, dst: u32 },
#[error( #[error(
"resource source format ({src:?}) must match the resolve destination format ({dst:?})" "resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})"
)] )]
MismatchedResolveTextureFormat { MismatchedResolveTextureFormat {
location: AttachmentErrorLocation,
src: wgt::TextureFormat, src: wgt::TextureFormat,
dst: wgt::TextureFormat, dst: wgt::TextureFormat,
}, },
@ -485,8 +532,6 @@ pub enum RenderPassErrorInner {
InvalidDepthOps, InvalidDepthOps,
#[error("unable to clear non-present/read-only stencil")] #[error("unable to clear non-present/read-only stencil")]
InvalidStencilOps, InvalidStencilOps,
#[error("all attachments must have the same sample count, found {actual} != {expected}")]
SampleCountMismatch { actual: u32, expected: u32 },
#[error("setting `values_offset` to be `None` is only for internal use in render bundles")] #[error("setting `values_offset` to be `None` is only for internal use in render bundles")]
InvalidValuesOffset, InvalidValuesOffset,
#[error(transparent)] #[error(transparent)]
@ -519,7 +564,7 @@ pub enum RenderPassErrorInner {
while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \ while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
Read-only renderpasses are only compatible with read-only bundles for that aspect." Read-only renderpasses are only compatible with read-only bundles for that aspect."
)] )]
IncompatibleBundleRods { IncompatibleBundleReadOnlyDepthStencil {
pass_depth: bool, pass_depth: bool,
pass_stencil: bool, pass_stencil: bool,
bundle_depth: bool, bundle_depth: bool,
@ -686,7 +731,10 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
let mut pending_discard_init_fixups = SurfacesInDiscardState::new(); let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
let mut divergent_discarded_depth_stencil_aspect = None; let mut divergent_discarded_depth_stencil_aspect = None;
let mut attachment_type_name = ""; let mut attachment_location = AttachmentErrorLocation::Color {
index: usize::MAX,
resolve: false,
};
let mut extent = None; let mut extent = None;
let mut sample_count = 0; let mut sample_count = 0;
@ -723,15 +771,17 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
Ok(()) Ok(())
}; };
let mut add_view = |view: &TextureView<A>, type_name| { let mut add_view = |view: &TextureView<A>, location| {
let render_extent = view let render_extent = view.render_extent.map_err(|reason| {
.render_extent RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
.ok_or(RenderPassErrorInner::TextureViewIsNotRenderable)?; })?;
if let Some(ex) = extent { if let Some(ex) = extent {
if ex != render_extent { if ex != render_extent {
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch { return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous: (attachment_type_name, ex), expected_location: attachment_location,
mismatch: (type_name, render_extent), expected_extent: ex,
actual_location: location,
actual_extent: render_extent,
}); });
} }
} else { } else {
@ -740,12 +790,14 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
if sample_count == 0 { if sample_count == 0 {
sample_count = view.samples; sample_count = view.samples;
} else if sample_count != view.samples { } else if sample_count != view.samples {
return Err(RenderPassErrorInner::SampleCountMismatch { return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
actual: view.samples, expected_location: attachment_location,
expected: sample_count, expected_samples: sample_count,
actual_location: location,
actual_samples: view.samples,
}); });
} }
attachment_type_name = type_name; attachment_location = location;
Ok(()) Ok(())
}; };
@ -760,7 +812,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
.add_single(view_guard, at.view) .add_single(view_guard, at.view)
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?; .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
check_multiview(view)?; check_multiview(view)?;
add_view(view, "depth")?; add_view(view, AttachmentErrorLocation::Depth)?;
let ds_aspects = view.desc.aspects(); let ds_aspects = view.desc.aspects();
if ds_aspects.contains(hal::FormatAspects::COLOR) { if ds_aspects.contains(hal::FormatAspects::COLOR) {
@ -879,8 +931,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
}); });
} }
for at in color_attachments { for (index, attachment) in color_attachments.iter().enumerate() {
let at = if let Some(attachment) = at.as_ref() { let at = if let Some(attachment) = attachment.as_ref() {
attachment attachment
} else { } else {
colors.push(None); colors.push(None);
@ -892,7 +944,13 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
.add_single(view_guard, at.view) .add_single(view_guard, at.view)
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?; .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
check_multiview(color_view)?; check_multiview(color_view)?;
add_view(color_view, "color")?; add_view(
color_view,
AttachmentErrorLocation::Color {
index,
resolve: false,
},
)?;
if !color_view if !color_view
.desc .desc
@ -924,23 +982,35 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
check_multiview(resolve_view)?; check_multiview(resolve_view)?;
let render_extent = resolve_view let resolve_location = AttachmentErrorLocation::Color {
.render_extent index,
.ok_or(RenderPassErrorInner::TextureViewIsNotRenderable)?; resolve: true,
};
let render_extent = resolve_view.render_extent.map_err(|reason| {
RenderPassErrorInner::TextureViewIsNotRenderable {
location: resolve_location,
reason,
}
})?;
if color_view.render_extent.unwrap() != render_extent { if color_view.render_extent.unwrap() != render_extent {
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch { return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous: (attachment_type_name, extent.unwrap_or_default()), expected_location: attachment_location,
mismatch: ("resolve", render_extent), expected_extent: extent.unwrap_or_default(),
actual_location: resolve_location,
actual_extent: render_extent,
}); });
} }
if color_view.samples == 1 || resolve_view.samples != 1 { if color_view.samples == 1 || resolve_view.samples != 1 {
return Err(RenderPassErrorInner::InvalidResolveSampleCounts { return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
location: resolve_location,
src: color_view.samples, src: color_view.samples,
dst: resolve_view.samples, dst: resolve_view.samples,
}); });
} }
if color_view.desc.format != resolve_view.desc.format { if color_view.desc.format != resolve_view.desc.format {
return Err(RenderPassErrorInner::MismatchedResolveTextureFormat { return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
location: resolve_location,
src: color_view.desc.format, src: color_view.desc.format,
dst: resolve_view.desc.format, dst: resolve_view.desc.format,
}); });
@ -950,9 +1020,10 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
.flags .flags
.contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE) .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
{ {
return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat( return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
resolve_view.desc.format, location: resolve_location,
)); format: resolve_view.desc.format,
});
} }
cmd_buf.texture_memory_actions.register_implicit_init( cmd_buf.texture_memory_actions.register_implicit_init(
@ -1999,12 +2070,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if (info.is_depth_read_only && !bundle.is_depth_read_only) if (info.is_depth_read_only && !bundle.is_depth_read_only)
|| (info.is_stencil_read_only && !bundle.is_stencil_read_only) || (info.is_stencil_read_only && !bundle.is_stencil_read_only)
{ {
return Err(RenderPassErrorInner::IncompatibleBundleRods { return Err(
pass_depth: info.is_depth_read_only, RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
pass_stencil: info.is_stencil_read_only, pass_depth: info.is_depth_read_only,
bundle_depth: bundle.is_depth_read_only, pass_stencil: info.is_stencil_read_only,
bundle_stencil: bundle.is_stencil_read_only, bundle_depth: bundle.is_depth_read_only,
}) bundle_stencil: bundle.is_stencil_read_only,
},
)
.map_pass_err(scope); .map_pass_err(scope);
} }

View File

@ -9,7 +9,7 @@ use crate::{
}, },
instance::{self, Adapter, Surface}, instance::{self, Adapter, Surface},
pipeline, present, pipeline, present,
resource::{self, BufferAccessResult, BufferMapState}, resource::{self, BufferAccessResult, BufferMapState, TextureViewNotRenderableReason},
resource::{BufferAccessError, BufferMapOperation}, resource::{BufferAccessError, BufferMapOperation},
track::{BindGroupStates, TextureSelector, Tracker}, track::{BindGroupStates, TextureSelector, Tracker},
validation::{self, check_buffer_usage, check_texture_usage}, validation::{self, check_buffer_usage, check_texture_usage},
@ -1175,20 +1175,41 @@ impl<A: HalApi> Device<A> {
}; };
// https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view
let is_renderable = texture let render_extent = 'b: loop {
.desc if !texture
.usage
.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
&& resolved_dimension == TextureViewDimension::D2
&& resolved_mip_level_count == 1
&& resolved_array_layer_count == 1
&& aspects == hal::FormatAspects::from(texture.desc.format);
let render_extent = is_renderable.then(|| {
texture
.desc .desc
.compute_render_extent(desc.range.base_mip_level) .usage
}); .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
{
break 'b Err(TextureViewNotRenderableReason::Usage(texture.desc.usage));
}
if resolved_dimension != TextureViewDimension::D2 {
break 'b Err(TextureViewNotRenderableReason::Dimension(
resolved_dimension,
));
}
if resolved_mip_level_count != 1 {
break 'b Err(TextureViewNotRenderableReason::MipLevelCount(
resolved_mip_level_count,
));
}
if resolved_array_layer_count != 1 {
break 'b Err(TextureViewNotRenderableReason::ArrayLayerCount(
resolved_array_layer_count,
));
}
if aspects != hal::FormatAspects::from(texture.desc.format) {
break 'b Err(TextureViewNotRenderableReason::Aspects(aspects));
}
break 'b Ok(texture
.desc
.compute_render_extent(desc.range.base_mip_level));
};
// filter the usages based on the other criteria // filter the usages based on the other criteria
let usage = { let usage = {

View File

@ -576,6 +576,22 @@ impl HalTextureViewDescriptor {
} }
} }
#[derive(Debug, Copy, Clone, Error)]
pub enum TextureViewNotRenderableReason {
#[error("the texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
Usage(wgt::TextureUsages),
#[error("the dimension of this texture view is not 2D. View dimension: {0:?}")]
Dimension(wgt::TextureViewDimension),
#[error("this texture view has more than one mipmap level. View mipmap levels: {0:?}")]
MipLevelCount(u32),
#[error("this texture view has more than one array layer. View array layers: {0:?}")]
ArrayLayerCount(u32),
#[error(
"the aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
)]
Aspects(hal::FormatAspects),
}
#[derive(Debug)] #[derive(Debug)]
pub struct TextureView<A: hal::Api> { pub struct TextureView<A: hal::Api> {
pub(crate) raw: A::TextureView, pub(crate) raw: A::TextureView,
@ -586,8 +602,8 @@ pub struct TextureView<A: hal::Api> {
//TODO: store device_id for quick access? //TODO: store device_id for quick access?
pub(crate) desc: HalTextureViewDescriptor, pub(crate) desc: HalTextureViewDescriptor,
pub(crate) format_features: wgt::TextureFormatFeatures, pub(crate) format_features: wgt::TextureFormatFeatures,
/// This is `None` only if the texture view is not renderable /// This is `Err` only if the texture view is not renderable
pub(crate) render_extent: Option<wgt::Extent3d>, pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
pub(crate) samples: u32, pub(crate) samples: u32,
pub(crate) selector: TextureSelector, pub(crate) selector: TextureSelector,
pub(crate) life_guard: LifeGuard, pub(crate) life_guard: LifeGuard,