1use alloc::boxed::Box;
2use core::num::NonZeroU32;
3
4use thiserror::Error;
5
6use wgt::error::{ErrorType, WebGpuError};
7
8use super::bind::BinderError;
9use crate::command::pass;
10use crate::resource::InvalidResourceError;
11use crate::validation::InvalidWorkgroupSizeError;
12use crate::{
13 binding_model::{BindingError, ImmediateUploadError, LateMinBufferBindingSizeMismatch},
14 device::Device,
15 resource::{
16 DestroyedResourceError, MissingBufferUsageError, MissingTextureUsageError,
17 ResourceErrorIdent,
18 },
19 track::ResourceUsageCompatibilityError,
20};
21
22#[derive(Clone, Debug, Error)]
24#[non_exhaustive]
25pub enum DrawError {
26 #[error("Blend constant needs to be set")]
27 MissingBlendConstant,
28 #[error("Render pipeline must be set")]
29 MissingPipeline(#[from] pass::MissingPipeline),
30 #[error("Currently set {pipeline} requires vertex buffer {index} to be set")]
31 MissingVertexBuffer {
32 pipeline: ResourceErrorIdent,
33 index: usize,
34 },
35 #[error("Index buffer must be set")]
36 MissingIndexBuffer,
37 #[error(transparent)]
38 IncompatibleBindGroup(#[from] Box<BinderError>),
39 #[error("Vertex {last_vertex} extends beyond limit {vertex_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Vertex` step-rate vertex buffer?")]
40 VertexBeyondLimit {
41 last_vertex: u64,
42 vertex_limit: u64,
43 slot: u32,
44 },
45 #[error("Instance {last_instance} extends beyond limit {instance_limit} imposed by the buffer in slot {slot}. Did you bind the correct `Instance` step-rate vertex buffer?")]
46 InstanceBeyondLimit {
47 last_instance: u64,
48 instance_limit: u64,
49 slot: u32,
50 },
51 #[error("Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")]
52 IndexBeyondLimit { last_index: u64, index_limit: u64 },
53 #[error("For indexed drawing with strip topology, {pipeline}'s strip index format {strip_index_format:?} must match index buffer format {buffer_format:?}")]
54 UnmatchedStripIndexFormat {
55 pipeline: ResourceErrorIdent,
56 strip_index_format: Option<wgt::IndexFormat>,
57 buffer_format: wgt::IndexFormat,
58 },
59 #[error(transparent)]
60 BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
61 #[error(
62 "Wrong pipeline type for this draw command. Attempted to call {} draw command on {} pipeline",
63 if *wanted_mesh_pipeline {"mesh shader"} else {"standard"},
64 if *wanted_mesh_pipeline {"standard"} else {"mesh shader"},
65 )]
66 WrongPipelineType { wanted_mesh_pipeline: bool },
67 #[error(transparent)]
68 InvalidGroupSize(#[from] InvalidWorkgroupSizeError),
69 #[error(
70 "Mesh shader calls in multiview render passes require enabling the `EXPERIMENTAL_MESH_SHADER_MULTIVIEW` feature, and the highest bit ({highest_view_index}) in the multiview mask must be less than `Limits::max_mesh_multiview_view_count` ({max_multiviews})"
71 )]
72 MeshPipelineMultiviewLimitsViolated {
73 highest_view_index: u32,
74 max_multiviews: u32,
75 },
76 #[error("Not all immediate data required by the pipeline has been set via set_immediates (missing byte ranges: {missing})")]
77 MissingImmediateData {
78 missing: naga::valid::ImmediateSlots,
79 },
80 #[error("The number of bind groups + vertex buffers {given} exceeds the limit {limit}")]
81 TooManyBindGroupsPlusVertexBuffers { given: u32, limit: u32 },
82}
83
84impl WebGpuError for DrawError {
85 fn webgpu_error_type(&self) -> ErrorType {
86 ErrorType::Validation
87 }
88}
89
90#[derive(Clone, Debug, Error)]
93#[non_exhaustive]
94pub enum RenderCommandError {
95 #[error(transparent)]
96 BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange),
97 #[error("Vertex buffer index {index} is greater than the device's requested `max_vertex_buffers` limit {max}")]
98 VertexBufferIndexOutOfRange { index: u32, max: u32 },
99 #[error(
100 "Offset {offset} for vertex buffer in slot {slot} is not a multiple of `VERTEX_ALIGNMENT`"
101 )]
102 UnalignedVertexBuffer { slot: u32, offset: u64 },
103 #[error("Offset {offset} for index buffer is not a multiple of {alignment}")]
104 UnalignedIndexBuffer { offset: u64, alignment: usize },
105 #[error("Render pipeline targets are incompatible with render pass")]
106 IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),
107 #[error("{0} writes to depth, while the pass has read-only depth access")]
108 IncompatibleDepthAccess(ResourceErrorIdent),
109 #[error("{0} writes to stencil, while the pass has read-only stencil access")]
110 IncompatibleStencilAccess(ResourceErrorIdent),
111 #[error(transparent)]
112 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
113 #[error(transparent)]
114 DestroyedResource(#[from] DestroyedResourceError),
115 #[error(transparent)]
116 InvalidResource(#[from] InvalidResourceError),
117 #[error(transparent)]
118 MissingBufferUsage(#[from] MissingBufferUsageError),
119 #[error(transparent)]
120 MissingTextureUsage(#[from] MissingTextureUsageError),
121 #[error(transparent)]
122 ImmediateData(#[from] ImmediateUploadError),
123 #[error(transparent)]
124 BindingError(#[from] BindingError),
125 #[error("Viewport size {{ w: {w}, h: {h} }} greater than device's requested `max_texture_dimension_2d` limit {max}, or less than zero")]
126 InvalidViewportRectSize { w: f32, h: f32, max: u32 },
127 #[error("Viewport has invalid rect {rect:?} for device's requested `max_texture_dimension_2d` limit; Origin less than -2 * `max_texture_dimension_2d` ({min}), or rect extends past 2 * `max_texture_dimension_2d` - 1 ({max})")]
128 InvalidViewportRectPosition { rect: Rect<f32>, min: f32, max: f32 },
129 #[error("Viewport minDepth {0} and/or maxDepth {1} are not in [0, 1]")]
130 InvalidViewportDepth(f32, f32),
131 #[error("Scissor {0:?} is not contained in the render target {1:?}")]
132 InvalidScissorRect(Rect<u32>, wgt::Extent3d),
133 #[error("Indirect buffer offset {0:?} is not a multiple of 4")]
134 UnalignedIndirectBufferOffset(wgt::BufferAddress),
135 #[error("Indirect draw arguments of {args_size} bytes (count = {count}) starting at {offset} would overrun buffer size of {buffer_size}")]
136 IndirectBufferOverrun {
137 count: u32,
138 offset: u64,
139 args_size: u64,
140 buffer_size: u64,
141 },
142 #[error("Support for {0} is not implemented yet")]
143 Unimplemented(&'static str),
144}
145
146impl WebGpuError for RenderCommandError {
147 fn webgpu_error_type(&self) -> ErrorType {
148 match self {
149 Self::IncompatiblePipelineTargets(e) => e.webgpu_error_type(),
150 Self::ResourceUsageCompatibility(e) => e.webgpu_error_type(),
151 Self::DestroyedResource(e) => e.webgpu_error_type(),
152 Self::InvalidResource(e) => e.webgpu_error_type(),
153 Self::MissingBufferUsage(e) => e.webgpu_error_type(),
154 Self::MissingTextureUsage(e) => e.webgpu_error_type(),
155 Self::ImmediateData(e) => e.webgpu_error_type(),
156 Self::BindingError(e) => e.webgpu_error_type(),
157
158 Self::BindGroupIndexOutOfRange { .. }
159 | Self::VertexBufferIndexOutOfRange { .. }
160 | Self::UnalignedIndexBuffer { .. }
161 | Self::UnalignedVertexBuffer { .. }
162 | Self::IncompatibleDepthAccess(..)
163 | Self::IncompatibleStencilAccess(..)
164 | Self::InvalidViewportRectSize { .. }
165 | Self::InvalidViewportRectPosition { .. }
166 | Self::InvalidViewportDepth(..)
167 | Self::InvalidScissorRect(..)
168 | Self::UnalignedIndirectBufferOffset(..)
169 | Self::IndirectBufferOverrun { .. }
170 | Self::Unimplemented(..) => ErrorType::Validation,
171 }
172 }
173}
174
175#[derive(Clone, Copy, Debug, Default)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct Rect<T> {
178 pub x: T,
179 pub y: T,
180 pub w: T,
181 pub h: T,
182}
183
184pub(crate) fn validate_mesh_draw_multiview(
187 device: &Device,
188 multiview_mask: Option<NonZeroU32>,
189) -> Result<(), DrawError> {
190 if let Some(mv) = multiview_mask {
191 let highest_bit = 31 - mv.leading_zeros();
192
193 if !device
194 .features
195 .contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)
196 || highest_bit >= device.limits.max_mesh_multiview_view_count
197 {
198 return Err(DrawError::MeshPipelineMultiviewLimitsViolated {
199 highest_view_index: highest_bit,
200 max_multiviews: device.limits.max_mesh_multiview_view_count,
201 });
202 }
203 }
204 Ok(())
205}