1 /*! Draw structures - shared between render passes and bundles.
2 !*/
3 
4 use crate::{
5     binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
6     error::ErrorFormatter,
7     id,
8     track::UseExtendError,
9     validation::{MissingBufferUsageError, MissingTextureUsageError},
10 };
11 use wgt::{BufferAddress, BufferSize, Color};
12 
13 use std::num::NonZeroU32;
14 use thiserror::Error;
15 
16 pub type BufferError = UseExtendError<hal::BufferUses>;
17 
18 /// Error validating a draw call.
19 #[derive(Clone, Debug, Error, PartialEq)]
20 pub enum DrawError {
21     #[error("blend constant needs to be set")]
22     MissingBlendConstant,
23     #[error("render pipeline must be set")]
24     MissingPipeline,
25     #[error("vertex buffer {index} must be set")]
26     MissingVertexBuffer { index: u32 },
27     #[error("index buffer must be set")]
28     MissingIndexBuffer,
29     #[error("current render pipeline has a layout which is incompatible with a currently set bind group, first differing at entry index {index}")]
30     IncompatibleBindGroup {
31         index: u32,
32         //expected: BindGroupLayoutId,
33         //provided: Option<(BindGroupLayoutId, BindGroupId)>,
34     },
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: u32,
38         vertex_limit: u32,
39         slot: u32,
40     },
41     #[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?")]
42     InstanceBeyondLimit {
43         last_instance: u32,
44         instance_limit: u32,
45         slot: u32,
46     },
47     #[error("index {last_index} extends beyond limit {index_limit}. Did you bind the correct index buffer?")]
48     IndexBeyondLimit { last_index: u32, index_limit: u32 },
49     #[error(
50         "pipeline index format ({pipeline:?}) and buffer index format ({buffer:?}) do not match"
51     )]
52     UnmatchedIndexFormats {
53         pipeline: wgt::IndexFormat,
54         buffer: wgt::IndexFormat,
55     },
56     #[error(transparent)]
57     BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
58 }
59 
60 /// Error encountered when encoding a render command.
61 /// This is the shared error set between render bundles and passes.
62 #[derive(Clone, Debug, Error)]
63 pub 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: u8, 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("buffer {0:?} is in error {1:?}")]
83     Buffer(id::BufferId, BufferError),
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("Invalid Viewport parameters")]
93     InvalidViewport,
94     #[error("Invalid ScissorRect parameters")]
95     InvalidScissorRect,
96     #[error("Support for {0} is not implemented yet")]
97     Unimplemented(&'static str),
98 }
99 impl crate::error::PrettyError for RenderCommandError {
fmt_pretty(&self, fmt: &mut ErrorFormatter)100     fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
101         fmt.error(self);
102         match *self {
103             Self::InvalidBindGroup(id) => {
104                 fmt.bind_group_label(&id);
105             }
106             Self::InvalidPipeline(id) => {
107                 fmt.render_pipeline_label(&id);
108             }
109             Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => {
110                 fmt.buffer_label(&id);
111             }
112             _ => {}
113         };
114     }
115 }
116 
117 #[derive(Clone, Copy, Debug, Default)]
118 #[cfg_attr(
119     any(feature = "serial-pass", feature = "trace"),
120     derive(serde::Serialize)
121 )]
122 #[cfg_attr(
123     any(feature = "serial-pass", feature = "replay"),
124     derive(serde::Deserialize)
125 )]
126 pub struct Rect<T> {
127     pub x: T,
128     pub y: T,
129     pub w: T,
130     pub h: T,
131 }
132 
133 #[doc(hidden)]
134 #[derive(Clone, Copy, Debug)]
135 #[cfg_attr(
136     any(feature = "serial-pass", feature = "trace"),
137     derive(serde::Serialize)
138 )]
139 #[cfg_attr(
140     any(feature = "serial-pass", feature = "replay"),
141     derive(serde::Deserialize)
142 )]
143 pub enum RenderCommand {
144     SetBindGroup {
145         index: u8,
146         num_dynamic_offsets: u8,
147         bind_group_id: id::BindGroupId,
148     },
149     SetPipeline(id::RenderPipelineId),
150     SetIndexBuffer {
151         buffer_id: id::BufferId,
152         index_format: wgt::IndexFormat,
153         offset: BufferAddress,
154         size: Option<BufferSize>,
155     },
156     SetVertexBuffer {
157         slot: u32,
158         buffer_id: id::BufferId,
159         offset: BufferAddress,
160         size: Option<BufferSize>,
161     },
162     SetBlendConstant(Color),
163     SetStencilReference(u32),
164     SetViewport {
165         rect: Rect<f32>,
166         //TODO: use half-float to reduce the size?
167         depth_min: f32,
168         depth_max: f32,
169     },
170     SetScissor(Rect<u32>),
171     SetPushConstant {
172         stages: wgt::ShaderStages,
173         offset: u32,
174         size_bytes: u32,
175         /// None means there is no data and the data should be an array of zeros.
176         ///
177         /// Facilitates clears in renderbundles which explicitly do their clears.
178         values_offset: Option<u32>,
179     },
180     Draw {
181         vertex_count: u32,
182         instance_count: u32,
183         first_vertex: u32,
184         first_instance: u32,
185     },
186     DrawIndexed {
187         index_count: u32,
188         instance_count: u32,
189         first_index: u32,
190         base_vertex: i32,
191         first_instance: u32,
192     },
193     MultiDrawIndirect {
194         buffer_id: id::BufferId,
195         offset: BufferAddress,
196         /// Count of `None` represents a non-multi call.
197         count: Option<NonZeroU32>,
198         indexed: bool,
199     },
200     MultiDrawIndirectCount {
201         buffer_id: id::BufferId,
202         offset: BufferAddress,
203         count_buffer_id: id::BufferId,
204         count_buffer_offset: BufferAddress,
205         max_count: u32,
206         indexed: bool,
207     },
208     PushDebugGroup {
209         color: u32,
210         len: usize,
211     },
212     PopDebugGroup,
213     InsertDebugMarker {
214         color: u32,
215         len: usize,
216     },
217     WriteTimestamp {
218         query_set_id: id::QuerySetId,
219         query_index: u32,
220     },
221     BeginPipelineStatisticsQuery {
222         query_set_id: id::QuerySetId,
223         query_index: u32,
224     },
225     EndPipelineStatisticsQuery,
226     ExecuteBundle(id::RenderBundleId),
227 }
228