wgpu_core/command/
draw.rs

1/*! Draw structures - shared between render passes and bundles.
2!*/
3
4use crate::{
5    binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
6    error::ErrorFormatter,
7    id,
8    track::UsageConflict,
9    validation::{MissingBufferUsageError, MissingTextureUsageError},
10};
11use wgt::{BufferAddress, BufferSize, Color};
12
13use std::num::NonZeroU32;
14use thiserror::Error;
15
16/// Error validating a draw call.
17#[derive(Clone, Debug, Error, Eq, PartialEq)]
18#[non_exhaustive]
19pub enum DrawError {
20    #[error("Blend constant needs to be set")]
21    MissingBlendConstant,
22    #[error("Render pipeline must be set")]
23    MissingPipeline,
24    #[error("Vertex buffer {index} must be set")]
25    MissingVertexBuffer { index: u32 },
26    #[error("Index buffer must be set")]
27    MissingIndexBuffer,
28    #[error("The pipeline layout, associated with the current render pipeline, contains a bind group layout at index {index} which is incompatible with the bind group layout associated with the bind group at {index}")]
29    IncompatibleBindGroup {
30        index: u32,
31        //expected: BindGroupLayoutId,
32        //provided: Option<(BindGroupLayoutId, BindGroupId)>,
33    },
34    #[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?")]
35    VertexBeyondLimit {
36        last_vertex: u32,
37        vertex_limit: u32,
38        slot: u32,
39    },
40    #[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?")]
41    InstanceBeyondLimit {
42        last_instance: u32,
43        instance_limit: u32,
44        slot: u32,
45    },
46    #[error("Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")]
47    IndexBeyondLimit { last_index: u32, index_limit: u32 },
48    #[error(
49        "Pipeline index format ({pipeline:?}) and buffer index format ({buffer:?}) do not match"
50    )]
51    UnmatchedIndexFormats {
52        pipeline: wgt::IndexFormat,
53        buffer: wgt::IndexFormat,
54    },
55    #[error(transparent)]
56    BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
57}
58
59/// Error encountered when encoding a render command.
60/// This is the shared error set between render bundles and passes.
61#[derive(Clone, Debug, Error)]
62#[non_exhaustive]
63pub enum RenderCommandError {
64    #[error("Bind group {0:?} is invalid")]
65    InvalidBindGroup(id::BindGroupId),
66    #[error("Render bundle {0:?} is invalid")]
67    InvalidRenderBundle(id::RenderBundleId),
68    #[error("Bind group index {index} is greater than the device's requested `max_bind_group` limit {max}")]
69    BindGroupIndexOutOfRange { index: u32, max: u32 },
70    #[error("Dynamic buffer offset {0} does not respect device's requested `{1}` limit {2}")]
71    UnalignedBufferOffset(u64, &'static str, u32),
72    #[error("Number of buffer offsets ({actual}) does not match the number of dynamic bindings ({expected})")]
73    InvalidDynamicOffsetCount { actual: usize, expected: usize },
74    #[error("Render pipeline {0:?} is invalid")]
75    InvalidPipeline(id::RenderPipelineId),
76    #[error("QuerySet {0:?} is invalid")]
77    InvalidQuerySet(id::QuerySetId),
78    #[error("Render pipeline targets are incompatible with render pass")]
79    IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),
80    #[error("Pipeline writes to depth/stencil, while the pass has read-only depth/stencil")]
81    IncompatiblePipelineRods,
82    #[error(transparent)]
83    UsageConflict(#[from] UsageConflict),
84    #[error("Buffer {0:?} is destroyed")]
85    DestroyedBuffer(id::BufferId),
86    #[error(transparent)]
87    MissingBufferUsage(#[from] MissingBufferUsageError),
88    #[error(transparent)]
89    MissingTextureUsage(#[from] MissingTextureUsageError),
90    #[error(transparent)]
91    PushConstants(#[from] PushConstantUploadError),
92    #[error("Viewport width {0} and/or height {1} are less than or equal to 0")]
93    InvalidViewportDimension(f32, f32),
94    #[error("Viewport minDepth {0} and/or maxDepth {1} are not in [0, 1]")]
95    InvalidViewportDepth(f32, f32),
96    #[error("Scissor {0:?} is not contained in the render target {1:?}")]
97    InvalidScissorRect(Rect<u32>, wgt::Extent3d),
98    #[error("Support for {0} is not implemented yet")]
99    Unimplemented(&'static str),
100}
101impl crate::error::PrettyError for RenderCommandError {
102    fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
103        fmt.error(self);
104        match *self {
105            Self::InvalidBindGroup(id) => {
106                fmt.bind_group_label(&id);
107            }
108            Self::InvalidPipeline(id) => {
109                fmt.render_pipeline_label(&id);
110            }
111            Self::UsageConflict(UsageConflict::TextureInvalid { id }) => {
112                fmt.texture_label(&id);
113            }
114            Self::UsageConflict(UsageConflict::BufferInvalid { id })
115            | Self::DestroyedBuffer(id) => {
116                fmt.buffer_label(&id);
117            }
118            _ => {}
119        };
120    }
121}
122
123#[derive(Clone, Copy, Debug, Default)]
124#[cfg_attr(
125    any(feature = "serial-pass", feature = "trace"),
126    derive(serde::Serialize)
127)]
128#[cfg_attr(
129    any(feature = "serial-pass", feature = "replay"),
130    derive(serde::Deserialize)
131)]
132pub struct Rect<T> {
133    pub x: T,
134    pub y: T,
135    pub w: T,
136    pub h: T,
137}
138
139#[doc(hidden)]
140#[derive(Clone, Copy, Debug)]
141#[cfg_attr(
142    any(feature = "serial-pass", feature = "trace"),
143    derive(serde::Serialize)
144)]
145#[cfg_attr(
146    any(feature = "serial-pass", feature = "replay"),
147    derive(serde::Deserialize)
148)]
149pub enum RenderCommand {
150    SetBindGroup {
151        index: u32,
152        num_dynamic_offsets: u8,
153        bind_group_id: id::BindGroupId,
154    },
155    SetPipeline(id::RenderPipelineId),
156    SetIndexBuffer {
157        buffer_id: id::BufferId,
158        index_format: wgt::IndexFormat,
159        offset: BufferAddress,
160        size: Option<BufferSize>,
161    },
162    SetVertexBuffer {
163        slot: u32,
164        buffer_id: id::BufferId,
165        offset: BufferAddress,
166        size: Option<BufferSize>,
167    },
168    SetBlendConstant(Color),
169    SetStencilReference(u32),
170    SetViewport {
171        rect: Rect<f32>,
172        //TODO: use half-float to reduce the size?
173        depth_min: f32,
174        depth_max: f32,
175    },
176    SetScissor(Rect<u32>),
177
178    /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
179    ///
180    /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
181    /// of the restrictions these commands must satisfy.
182    SetPushConstant {
183        /// Which stages we are setting push constant values for.
184        stages: wgt::ShaderStages,
185
186        /// The byte offset within the push constant storage to write to.  This
187        /// must be a multiple of four.
188        offset: u32,
189
190        /// The number of bytes to write. This must be a multiple of four.
191        size_bytes: u32,
192
193        /// Index in [`BasePass::push_constant_data`] of the start of the data
194        /// to be written.
195        ///
196        /// Note: this is not a byte offset like `offset`. Rather, it is the
197        /// index of the first `u32` element in `push_constant_data` to read.
198        ///
199        /// `None` means zeros should be written to the destination range, and
200        /// there is no corresponding data in `push_constant_data`. This is used
201        /// by render bundles, which explicitly clear out any state that
202        /// post-bundle code might see.
203        values_offset: Option<u32>,
204    },
205    Draw {
206        vertex_count: u32,
207        instance_count: u32,
208        first_vertex: u32,
209        first_instance: u32,
210    },
211    DrawIndexed {
212        index_count: u32,
213        instance_count: u32,
214        first_index: u32,
215        base_vertex: i32,
216        first_instance: u32,
217    },
218    MultiDrawIndirect {
219        buffer_id: id::BufferId,
220        offset: BufferAddress,
221        /// Count of `None` represents a non-multi call.
222        count: Option<NonZeroU32>,
223        indexed: bool,
224    },
225    MultiDrawIndirectCount {
226        buffer_id: id::BufferId,
227        offset: BufferAddress,
228        count_buffer_id: id::BufferId,
229        count_buffer_offset: BufferAddress,
230        max_count: u32,
231        indexed: bool,
232    },
233    PushDebugGroup {
234        color: u32,
235        len: usize,
236    },
237    PopDebugGroup,
238    InsertDebugMarker {
239        color: u32,
240        len: usize,
241    },
242    WriteTimestamp {
243        query_set_id: id::QuerySetId,
244        query_index: u32,
245    },
246    BeginPipelineStatisticsQuery {
247        query_set_id: id::QuerySetId,
248        query_index: u32,
249    },
250    EndPipelineStatisticsQuery,
251    ExecuteBundle(id::RenderBundleId),
252}