wgpu_core/command/
render_command.rs

1use alloc::sync::Arc;
2
3use wgt::{BufferAddress, BufferSize, Color};
4
5use super::{Rect, RenderBundle};
6use crate::{
7    binding_model::BindGroup,
8    id,
9    pipeline::RenderPipeline,
10    resource::{Buffer, QuerySet},
11};
12
13#[doc(hidden)]
14#[derive(Clone, Copy, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub enum RenderCommand {
17    SetBindGroup {
18        index: u32,
19        num_dynamic_offsets: usize,
20        bind_group_id: Option<id::BindGroupId>,
21    },
22    SetPipeline(id::RenderPipelineId),
23    SetIndexBuffer {
24        buffer_id: id::BufferId,
25        index_format: wgt::IndexFormat,
26        offset: BufferAddress,
27        size: Option<BufferSize>,
28    },
29    SetVertexBuffer {
30        slot: u32,
31        buffer_id: id::BufferId,
32        offset: BufferAddress,
33        size: Option<BufferSize>,
34    },
35    SetBlendConstant(Color),
36    SetStencilReference(u32),
37    SetViewport {
38        rect: Rect<f32>,
39        //TODO: use half-float to reduce the size?
40        depth_min: f32,
41        depth_max: f32,
42    },
43    SetScissor(Rect<u32>),
44
45    /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
46    ///
47    /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
48    /// of the restrictions these commands must satisfy.
49    SetPushConstant {
50        /// Which stages we are setting push constant values for.
51        stages: wgt::ShaderStages,
52
53        /// The byte offset within the push constant storage to write to.  This
54        /// must be a multiple of four.
55        offset: u32,
56
57        /// The number of bytes to write. This must be a multiple of four.
58        size_bytes: u32,
59
60        /// Index in [`BasePass::push_constant_data`] of the start of the data
61        /// to be written.
62        ///
63        /// Note: this is not a byte offset like `offset`. Rather, it is the
64        /// index of the first `u32` element in `push_constant_data` to read.
65        ///
66        /// `None` means zeros should be written to the destination range, and
67        /// there is no corresponding data in `push_constant_data`. This is used
68        /// by render bundles, which explicitly clear out any state that
69        /// post-bundle code might see.
70        values_offset: Option<u32>,
71    },
72    Draw {
73        vertex_count: u32,
74        instance_count: u32,
75        first_vertex: u32,
76        first_instance: u32,
77    },
78    DrawIndexed {
79        index_count: u32,
80        instance_count: u32,
81        first_index: u32,
82        base_vertex: i32,
83        first_instance: u32,
84    },
85    DrawIndirect {
86        buffer_id: id::BufferId,
87        offset: BufferAddress,
88        count: u32,
89        indexed: bool,
90    },
91    MultiDrawIndirectCount {
92        buffer_id: id::BufferId,
93        offset: BufferAddress,
94        count_buffer_id: id::BufferId,
95        count_buffer_offset: BufferAddress,
96        max_count: u32,
97        indexed: bool,
98    },
99    PushDebugGroup {
100        color: u32,
101        len: usize,
102    },
103    PopDebugGroup,
104    InsertDebugMarker {
105        color: u32,
106        len: usize,
107    },
108    WriteTimestamp {
109        query_set_id: id::QuerySetId,
110        query_index: u32,
111    },
112    BeginOcclusionQuery {
113        query_index: u32,
114    },
115    EndOcclusionQuery,
116    BeginPipelineStatisticsQuery {
117        query_set_id: id::QuerySetId,
118        query_index: u32,
119    },
120    EndPipelineStatisticsQuery,
121    ExecuteBundle(id::RenderBundleId),
122}
123
124impl RenderCommand {
125    /// Resolves all ids in a list of commands into the corresponding resource Arc.
126    #[cfg(any(feature = "serde", feature = "replay"))]
127    pub fn resolve_render_command_ids(
128        hub: &crate::hub::Hub,
129        commands: &[RenderCommand],
130    ) -> Result<alloc::vec::Vec<ArcRenderCommand>, super::RenderPassError> {
131        use super::{DrawKind, PassErrorScope, RenderPassError};
132        use alloc::vec::Vec;
133
134        let buffers_guard = hub.buffers.read();
135        let bind_group_guard = hub.bind_groups.read();
136        let query_set_guard = hub.query_sets.read();
137        let pipelines_guard = hub.render_pipelines.read();
138        let render_bundles_guard = hub.render_bundles.read();
139
140        let resolved_commands: Vec<ArcRenderCommand> =
141            commands
142                .iter()
143                .map(|c| -> Result<ArcRenderCommand, RenderPassError> {
144                    Ok(match *c {
145                        RenderCommand::SetBindGroup {
146                            index,
147                            num_dynamic_offsets,
148                            bind_group_id,
149                        } => {
150                            if bind_group_id.is_none() {
151                                return Ok(ArcRenderCommand::SetBindGroup {
152                                    index,
153                                    num_dynamic_offsets,
154                                    bind_group: None,
155                                });
156                            }
157
158                            let bind_group_id = bind_group_id.unwrap();
159                            let bg = bind_group_guard.get(bind_group_id).get().map_err(|e| {
160                                RenderPassError {
161                                    scope: PassErrorScope::SetBindGroup,
162                                    inner: e.into(),
163                                }
164                            })?;
165
166                            ArcRenderCommand::SetBindGroup {
167                                index,
168                                num_dynamic_offsets,
169                                bind_group: Some(bg),
170                            }
171                        }
172
173                        RenderCommand::SetPipeline(pipeline_id) => ArcRenderCommand::SetPipeline(
174                            pipelines_guard.get(pipeline_id).get().map_err(|e| {
175                                RenderPassError {
176                                    scope: PassErrorScope::SetPipelineRender,
177                                    inner: e.into(),
178                                }
179                            })?,
180                        ),
181
182                        RenderCommand::SetPushConstant {
183                            offset,
184                            size_bytes,
185                            values_offset,
186                            stages,
187                        } => ArcRenderCommand::SetPushConstant {
188                            offset,
189                            size_bytes,
190                            values_offset,
191                            stages,
192                        },
193
194                        RenderCommand::PushDebugGroup { color, len } => {
195                            ArcRenderCommand::PushDebugGroup { color, len }
196                        }
197
198                        RenderCommand::PopDebugGroup => ArcRenderCommand::PopDebugGroup,
199
200                        RenderCommand::InsertDebugMarker { color, len } => {
201                            ArcRenderCommand::InsertDebugMarker { color, len }
202                        }
203
204                        RenderCommand::WriteTimestamp {
205                            query_set_id,
206                            query_index,
207                        } => ArcRenderCommand::WriteTimestamp {
208                            query_set: query_set_guard.get(query_set_id).get().map_err(|e| {
209                                RenderPassError {
210                                    scope: PassErrorScope::WriteTimestamp,
211                                    inner: e.into(),
212                                }
213                            })?,
214                            query_index,
215                        },
216
217                        RenderCommand::BeginPipelineStatisticsQuery {
218                            query_set_id,
219                            query_index,
220                        } => ArcRenderCommand::BeginPipelineStatisticsQuery {
221                            query_set: query_set_guard.get(query_set_id).get().map_err(|e| {
222                                RenderPassError {
223                                    scope: PassErrorScope::BeginPipelineStatisticsQuery,
224                                    inner: e.into(),
225                                }
226                            })?,
227                            query_index,
228                        },
229
230                        RenderCommand::EndPipelineStatisticsQuery => {
231                            ArcRenderCommand::EndPipelineStatisticsQuery
232                        }
233
234                        RenderCommand::SetIndexBuffer {
235                            buffer_id,
236                            index_format,
237                            offset,
238                            size,
239                        } => ArcRenderCommand::SetIndexBuffer {
240                            buffer: buffers_guard.get(buffer_id).get().map_err(|e| {
241                                RenderPassError {
242                                    scope: PassErrorScope::SetIndexBuffer,
243                                    inner: e.into(),
244                                }
245                            })?,
246                            index_format,
247                            offset,
248                            size,
249                        },
250
251                        RenderCommand::SetVertexBuffer {
252                            slot,
253                            buffer_id,
254                            offset,
255                            size,
256                        } => ArcRenderCommand::SetVertexBuffer {
257                            slot,
258                            buffer: buffers_guard.get(buffer_id).get().map_err(|e| {
259                                RenderPassError {
260                                    scope: PassErrorScope::SetVertexBuffer,
261                                    inner: e.into(),
262                                }
263                            })?,
264                            offset,
265                            size,
266                        },
267
268                        RenderCommand::SetBlendConstant(color) => {
269                            ArcRenderCommand::SetBlendConstant(color)
270                        }
271
272                        RenderCommand::SetStencilReference(reference) => {
273                            ArcRenderCommand::SetStencilReference(reference)
274                        }
275
276                        RenderCommand::SetViewport {
277                            rect,
278                            depth_min,
279                            depth_max,
280                        } => ArcRenderCommand::SetViewport {
281                            rect,
282                            depth_min,
283                            depth_max,
284                        },
285
286                        RenderCommand::SetScissor(scissor) => ArcRenderCommand::SetScissor(scissor),
287
288                        RenderCommand::Draw {
289                            vertex_count,
290                            instance_count,
291                            first_vertex,
292                            first_instance,
293                        } => ArcRenderCommand::Draw {
294                            vertex_count,
295                            instance_count,
296                            first_vertex,
297                            first_instance,
298                        },
299
300                        RenderCommand::DrawIndexed {
301                            index_count,
302                            instance_count,
303                            first_index,
304                            base_vertex,
305                            first_instance,
306                        } => ArcRenderCommand::DrawIndexed {
307                            index_count,
308                            instance_count,
309                            first_index,
310                            base_vertex,
311                            first_instance,
312                        },
313
314                        RenderCommand::DrawIndirect {
315                            buffer_id,
316                            offset,
317                            count,
318                            indexed,
319                        } => ArcRenderCommand::DrawIndirect {
320                            buffer: buffers_guard.get(buffer_id).get().map_err(|e| {
321                                RenderPassError {
322                                    scope: PassErrorScope::Draw {
323                                        kind: if count != 1 {
324                                            DrawKind::MultiDrawIndirect
325                                        } else {
326                                            DrawKind::DrawIndirect
327                                        },
328                                        indexed,
329                                    },
330                                    inner: e.into(),
331                                }
332                            })?,
333                            offset,
334                            count,
335                            indexed,
336
337                            vertex_or_index_limit: 0,
338                            instance_limit: 0,
339                        },
340
341                        RenderCommand::MultiDrawIndirectCount {
342                            buffer_id,
343                            offset,
344                            count_buffer_id,
345                            count_buffer_offset,
346                            max_count,
347                            indexed,
348                        } => {
349                            let scope = PassErrorScope::Draw {
350                                kind: DrawKind::MultiDrawIndirectCount,
351                                indexed,
352                            };
353                            ArcRenderCommand::MultiDrawIndirectCount {
354                                buffer: buffers_guard.get(buffer_id).get().map_err(|e| {
355                                    RenderPassError {
356                                        scope,
357                                        inner: e.into(),
358                                    }
359                                })?,
360                                offset,
361                                count_buffer: buffers_guard.get(count_buffer_id).get().map_err(
362                                    |e| RenderPassError {
363                                        scope,
364                                        inner: e.into(),
365                                    },
366                                )?,
367                                count_buffer_offset,
368                                max_count,
369                                indexed,
370                            }
371                        }
372
373                        RenderCommand::BeginOcclusionQuery { query_index } => {
374                            ArcRenderCommand::BeginOcclusionQuery { query_index }
375                        }
376
377                        RenderCommand::EndOcclusionQuery => ArcRenderCommand::EndOcclusionQuery,
378
379                        RenderCommand::ExecuteBundle(bundle) => ArcRenderCommand::ExecuteBundle(
380                            render_bundles_guard.get(bundle).get().map_err(|e| {
381                                RenderPassError {
382                                    scope: PassErrorScope::ExecuteBundle,
383                                    inner: e.into(),
384                                }
385                            })?,
386                        ),
387                    })
388                })
389                .collect::<Result<Vec<_>, RenderPassError>>()?;
390        Ok(resolved_commands)
391    }
392}
393
394/// Equivalent to `RenderCommand` with the Ids resolved into resource Arcs.
395#[doc(hidden)]
396#[derive(Clone, Debug)]
397pub enum ArcRenderCommand {
398    SetBindGroup {
399        index: u32,
400        num_dynamic_offsets: usize,
401        bind_group: Option<Arc<BindGroup>>,
402    },
403    SetPipeline(Arc<RenderPipeline>),
404    SetIndexBuffer {
405        buffer: Arc<Buffer>,
406        index_format: wgt::IndexFormat,
407        offset: BufferAddress,
408        size: Option<BufferSize>,
409    },
410    SetVertexBuffer {
411        slot: u32,
412        buffer: Arc<Buffer>,
413        offset: BufferAddress,
414        size: Option<BufferSize>,
415    },
416    SetBlendConstant(Color),
417    SetStencilReference(u32),
418    SetViewport {
419        rect: Rect<f32>,
420        depth_min: f32,
421        depth_max: f32,
422    },
423    SetScissor(Rect<u32>),
424
425    /// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
426    ///
427    /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
428    /// of the restrictions these commands must satisfy.
429    SetPushConstant {
430        /// Which stages we are setting push constant values for.
431        stages: wgt::ShaderStages,
432
433        /// The byte offset within the push constant storage to write to.  This
434        /// must be a multiple of four.
435        offset: u32,
436
437        /// The number of bytes to write. This must be a multiple of four.
438        size_bytes: u32,
439
440        /// Index in [`BasePass::push_constant_data`] of the start of the data
441        /// to be written.
442        ///
443        /// Note: this is not a byte offset like `offset`. Rather, it is the
444        /// index of the first `u32` element in `push_constant_data` to read.
445        ///
446        /// `None` means zeros should be written to the destination range, and
447        /// there is no corresponding data in `push_constant_data`. This is used
448        /// by render bundles, which explicitly clear out any state that
449        /// post-bundle code might see.
450        values_offset: Option<u32>,
451    },
452    Draw {
453        vertex_count: u32,
454        instance_count: u32,
455        first_vertex: u32,
456        first_instance: u32,
457    },
458    DrawIndexed {
459        index_count: u32,
460        instance_count: u32,
461        first_index: u32,
462        base_vertex: i32,
463        first_instance: u32,
464    },
465    DrawIndirect {
466        buffer: Arc<Buffer>,
467        offset: BufferAddress,
468        count: u32,
469        indexed: bool,
470
471        /// This limit is only populated for commands in a [`RenderBundle`].
472        vertex_or_index_limit: u64,
473        /// This limit is only populated for commands in a [`RenderBundle`].
474        instance_limit: u64,
475    },
476    MultiDrawIndirectCount {
477        buffer: Arc<Buffer>,
478        offset: BufferAddress,
479        count_buffer: Arc<Buffer>,
480        count_buffer_offset: BufferAddress,
481        max_count: u32,
482        indexed: bool,
483    },
484    PushDebugGroup {
485        #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))]
486        color: u32,
487        len: usize,
488    },
489    PopDebugGroup,
490    InsertDebugMarker {
491        #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))]
492        color: u32,
493        len: usize,
494    },
495    WriteTimestamp {
496        query_set: Arc<QuerySet>,
497        query_index: u32,
498    },
499    BeginOcclusionQuery {
500        query_index: u32,
501    },
502    EndOcclusionQuery,
503    BeginPipelineStatisticsQuery {
504        query_set: Arc<QuerySet>,
505        query_index: u32,
506    },
507    EndPipelineStatisticsQuery,
508    ExecuteBundle(Arc<RenderBundle>),
509}