wgpu_core/command/
draw.rs

1/*! Draw structures - shared between render passes and bundles.
2!*/
3
4use crate::{
5    binding_model::{BindGroup, LateMinBufferBindingSizeMismatch, PushConstantUploadError},
6    error::ErrorFormatter,
7    hal_api::HalApi,
8    id,
9    pipeline::RenderPipeline,
10    resource::{Buffer, QuerySet},
11    track::UsageConflict,
12    validation::{MissingBufferUsageError, MissingTextureUsageError},
13};
14use wgt::{BufferAddress, BufferSize, Color, VertexStepMode};
15
16use std::{num::NonZeroU32, sync::Arc};
17use thiserror::Error;
18
19use super::RenderBundle;
20
21/// Error validating a draw call.
22#[derive(Clone, Debug, Error, Eq, PartialEq)]
23#[non_exhaustive]
24pub enum DrawError {
25    #[error("Blend constant needs to be set")]
26    MissingBlendConstant,
27    #[error("Render pipeline must be set")]
28    MissingPipeline,
29    #[error("Vertex buffer {index} must be set")]
30    MissingVertexBuffer { index: u32 },
31    #[error("Index buffer must be set")]
32    MissingIndexBuffer,
33    #[error("Incompatible bind group at index {index} in the current render pipeline")]
34    IncompatibleBindGroup { index: u32, diff: Vec<String> },
35    #[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?")]
36    VertexBeyondLimit {
37        last_vertex: u64,
38        vertex_limit: u64,
39        slot: u32,
40    },
41    #[error("{step_mode:?} buffer out of bounds at slot {slot}. Offset {offset} beyond limit {limit}. Did you bind the correct `Vertex` step-rate vertex buffer?")]
42    VertexOutOfBounds {
43        step_mode: VertexStepMode,
44        offset: u64,
45        limit: u64,
46        slot: u32,
47    },
48    #[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?")]
49    InstanceBeyondLimit {
50        last_instance: u64,
51        instance_limit: u64,
52        slot: u32,
53    },
54    #[error("Index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")]
55    IndexBeyondLimit { last_index: u64, index_limit: u64 },
56    #[error(
57        "Pipeline index format ({pipeline:?}) and buffer index format ({buffer:?}) do not match"
58    )]
59    UnmatchedIndexFormats {
60        pipeline: wgt::IndexFormat,
61        buffer: wgt::IndexFormat,
62    },
63    #[error(transparent)]
64    BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
65}
66
67/// Error encountered when encoding a render command.
68/// This is the shared error set between render bundles and passes.
69#[derive(Clone, Debug, Error)]
70#[non_exhaustive]
71pub enum RenderCommandError {
72    #[error("Bind group {0:?} is invalid")]
73    InvalidBindGroup(id::BindGroupId),
74    #[error("Render bundle {0:?} is invalid")]
75    InvalidRenderBundle(id::RenderBundleId),
76    #[error("Bind group index {index} is greater than the device's requested `max_bind_group` limit {max}")]
77    BindGroupIndexOutOfRange { index: u32, max: u32 },
78    #[error("Vertex buffer index {index} is greater than the device's requested `max_vertex_buffers` limit {max}")]
79    VertexBufferIndexOutOfRange { index: u32, max: u32 },
80    #[error("Dynamic buffer offset {0} does not respect device's requested `{1}` limit {2}")]
81    UnalignedBufferOffset(u64, &'static str, u32),
82    #[error("Number of buffer offsets ({actual}) does not match the number of dynamic bindings ({expected})")]
83    InvalidDynamicOffsetCount { actual: usize, expected: usize },
84    #[error("Render pipeline {0:?} is invalid")]
85    InvalidPipeline(id::RenderPipelineId),
86    #[error("QuerySet {0:?} is invalid")]
87    InvalidQuerySet(id::QuerySetId),
88    #[error("Render pipeline targets are incompatible with render pass")]
89    IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),
90    #[error("Pipeline writes to depth/stencil, while the pass has read-only depth/stencil")]
91    IncompatiblePipelineRods,
92    #[error(transparent)]
93    UsageConflict(#[from] UsageConflict),
94    #[error("Buffer {0:?} is destroyed")]
95    DestroyedBuffer(id::BufferId),
96    #[error(transparent)]
97    MissingBufferUsage(#[from] MissingBufferUsageError),
98    #[error(transparent)]
99    MissingTextureUsage(#[from] MissingTextureUsageError),
100    #[error(transparent)]
101    PushConstants(#[from] PushConstantUploadError),
102    #[error("Viewport has invalid rect {0:?}; origin and/or size is less than or equal to 0, and/or is not contained in the render target {1:?}")]
103    InvalidViewportRect(Rect<f32>, wgt::Extent3d),
104    #[error("Viewport minDepth {0} and/or maxDepth {1} are not in [0, 1]")]
105    InvalidViewportDepth(f32, f32),
106    #[error("Scissor {0:?} is not contained in the render target {1:?}")]
107    InvalidScissorRect(Rect<u32>, wgt::Extent3d),
108    #[error("Support for {0} is not implemented yet")]
109    Unimplemented(&'static str),
110}
111impl crate::error::PrettyError for RenderCommandError {
112    fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
113        fmt.error(self);
114        match *self {
115            Self::InvalidBindGroup(id) => {
116                fmt.bind_group_label(&id);
117            }
118            Self::InvalidPipeline(id) => {
119                fmt.render_pipeline_label(&id);
120            }
121            Self::UsageConflict(UsageConflict::TextureInvalid { id }) => {
122                fmt.texture_label(&id);
123            }
124            Self::UsageConflict(UsageConflict::BufferInvalid { id })
125            | Self::DestroyedBuffer(id) => {
126                fmt.buffer_label(&id);
127            }
128            _ => {}
129        };
130    }
131}
132
133#[derive(Clone, Copy, Debug, Default)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135pub struct Rect<T> {
136    pub x: T,
137    pub y: T,
138    pub w: T,
139    pub h: T,
140}
141
142#[doc(hidden)]
143#[derive(Clone, Copy, Debug)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145pub enum RenderCommand {
146    SetBindGroup {
147        index: u32,
148        num_dynamic_offsets: usize,
149        bind_group_id: id::BindGroupId,
150    },
151    SetPipeline(id::RenderPipelineId),
152    SetIndexBuffer {
153        buffer_id: id::BufferId,
154        index_format: wgt::IndexFormat,
155        offset: BufferAddress,
156        size: Option<BufferSize>,
157    },
158    SetVertexBuffer {
159        slot: u32,
160        buffer_id: id::BufferId,
161        offset: BufferAddress,
162        size: Option<BufferSize>,
163    },
164    SetBlendConstant(Color),
165    SetStencilReference(u32),
166    SetViewport {
167        rect: Rect<f32>,
168        //TODO: use half-float to reduce the size?
169        depth_min: f32,
170        depth_max: f32,
171    },
172    SetScissor(Rect<u32>),
173
174    /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
175    ///
176    /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
177    /// of the restrictions these commands must satisfy.
178    SetPushConstant {
179        /// Which stages we are setting push constant values for.
180        stages: wgt::ShaderStages,
181
182        /// The byte offset within the push constant storage to write to.  This
183        /// must be a multiple of four.
184        offset: u32,
185
186        /// The number of bytes to write. This must be a multiple of four.
187        size_bytes: u32,
188
189        /// Index in [`BasePass::push_constant_data`] of the start of the data
190        /// to be written.
191        ///
192        /// Note: this is not a byte offset like `offset`. Rather, it is the
193        /// index of the first `u32` element in `push_constant_data` to read.
194        ///
195        /// `None` means zeros should be written to the destination range, and
196        /// there is no corresponding data in `push_constant_data`. This is used
197        /// by render bundles, which explicitly clear out any state that
198        /// post-bundle code might see.
199        values_offset: Option<u32>,
200    },
201    Draw {
202        vertex_count: u32,
203        instance_count: u32,
204        first_vertex: u32,
205        first_instance: u32,
206    },
207    DrawIndexed {
208        index_count: u32,
209        instance_count: u32,
210        first_index: u32,
211        base_vertex: i32,
212        first_instance: u32,
213    },
214    MultiDrawIndirect {
215        buffer_id: id::BufferId,
216        offset: BufferAddress,
217        /// Count of `None` represents a non-multi call.
218        count: Option<NonZeroU32>,
219        indexed: bool,
220    },
221    MultiDrawIndirectCount {
222        buffer_id: id::BufferId,
223        offset: BufferAddress,
224        count_buffer_id: id::BufferId,
225        count_buffer_offset: BufferAddress,
226        max_count: u32,
227        indexed: bool,
228    },
229    PushDebugGroup {
230        color: u32,
231        len: usize,
232    },
233    PopDebugGroup,
234    InsertDebugMarker {
235        color: u32,
236        len: usize,
237    },
238    WriteTimestamp {
239        query_set_id: id::QuerySetId,
240        query_index: u32,
241    },
242    BeginOcclusionQuery {
243        query_index: u32,
244    },
245    EndOcclusionQuery,
246    BeginPipelineStatisticsQuery {
247        query_set_id: id::QuerySetId,
248        query_index: u32,
249    },
250    EndPipelineStatisticsQuery,
251    ExecuteBundle(id::RenderBundleId),
252}
253
254/// Equivalent to `RenderCommand` with the Ids resolved into resource Arcs.
255#[doc(hidden)]
256#[derive(Clone, Debug)]
257pub enum ArcRenderCommand<A: HalApi> {
258    SetBindGroup {
259        index: u32,
260        num_dynamic_offsets: usize,
261        bind_group: Arc<BindGroup<A>>,
262    },
263    SetPipeline(Arc<RenderPipeline<A>>),
264    SetIndexBuffer {
265        buffer: Arc<Buffer<A>>,
266        index_format: wgt::IndexFormat,
267        offset: BufferAddress,
268        size: Option<BufferSize>,
269    },
270    SetVertexBuffer {
271        slot: u32,
272        buffer: Arc<Buffer<A>>,
273        offset: BufferAddress,
274        size: Option<BufferSize>,
275    },
276    SetBlendConstant(Color),
277    SetStencilReference(u32),
278    SetViewport {
279        rect: Rect<f32>,
280        depth_min: f32,
281        depth_max: f32,
282    },
283    SetScissor(Rect<u32>),
284
285    /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
286    ///
287    /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
288    /// of the restrictions these commands must satisfy.
289    SetPushConstant {
290        /// Which stages we are setting push constant values for.
291        stages: wgt::ShaderStages,
292
293        /// The byte offset within the push constant storage to write to.  This
294        /// must be a multiple of four.
295        offset: u32,
296
297        /// The number of bytes to write. This must be a multiple of four.
298        size_bytes: u32,
299
300        /// Index in [`BasePass::push_constant_data`] of the start of the data
301        /// to be written.
302        ///
303        /// Note: this is not a byte offset like `offset`. Rather, it is the
304        /// index of the first `u32` element in `push_constant_data` to read.
305        ///
306        /// `None` means zeros should be written to the destination range, and
307        /// there is no corresponding data in `push_constant_data`. This is used
308        /// by render bundles, which explicitly clear out any state that
309        /// post-bundle code might see.
310        values_offset: Option<u32>,
311    },
312    Draw {
313        vertex_count: u32,
314        instance_count: u32,
315        first_vertex: u32,
316        first_instance: u32,
317    },
318    DrawIndexed {
319        index_count: u32,
320        instance_count: u32,
321        first_index: u32,
322        base_vertex: i32,
323        first_instance: u32,
324    },
325    MultiDrawIndirect {
326        buffer: Arc<Buffer<A>>,
327        offset: BufferAddress,
328        /// Count of `None` represents a non-multi call.
329        count: Option<NonZeroU32>,
330        indexed: bool,
331    },
332    MultiDrawIndirectCount {
333        buffer: Arc<Buffer<A>>,
334        offset: BufferAddress,
335        count_buffer: Arc<Buffer<A>>,
336        count_buffer_offset: BufferAddress,
337        max_count: u32,
338        indexed: bool,
339    },
340    PushDebugGroup {
341        color: u32,
342        len: usize,
343    },
344    PopDebugGroup,
345    InsertDebugMarker {
346        color: u32,
347        len: usize,
348    },
349    WriteTimestamp {
350        query_set: Arc<QuerySet<A>>,
351        query_index: u32,
352    },
353    BeginOcclusionQuery {
354        query_index: u32,
355    },
356    EndOcclusionQuery,
357    BeginPipelineStatisticsQuery {
358        query_set: Arc<QuerySet<A>>,
359        query_index: u32,
360    },
361    EndPipelineStatisticsQuery,
362    ExecuteBundle(Arc<RenderBundle<A>>),
363}