Skip to main content

wgpu_core/command/
draw.rs

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/// Error validating a draw call.
23#[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/// Error encountered when encoding a render command.
91/// This is the shared error set between render bundles and passes.
92#[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
184/// Validates a mesh shader draw against the multiview mask of the render pass or render bundle
185/// it is recorded in.
186pub(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}