bevy_render/render_phase/
draw_state.rs

1use crate::{
2    camera::Viewport,
3    diagnostic::internal::{Pass, PassKind, WritePipelineStatistics, WriteTimestamp},
4    render_resource::{
5        BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,
6        ShaderStages,
7    },
8    renderer::RenderDevice,
9};
10use bevy_color::LinearRgba;
11use bevy_utils::{default, detailed_trace};
12use core::ops::Range;
13use wgpu::{IndexFormat, QuerySet, RenderPass};
14
15/// Tracks the state of a [`TrackedRenderPass`].
16///
17/// This is used to skip redundant operations on the [`TrackedRenderPass`] (e.g. setting an already
18/// set pipeline, binding an already bound bind group). These operations can otherwise be fairly
19/// costly due to IO to the GPU, so deduplicating these calls results in a speedup.
20#[derive(Debug, Default)]
21struct DrawState {
22    pipeline: Option<RenderPipelineId>,
23    bind_groups: Vec<(Option<BindGroupId>, Vec<u32>)>,
24    /// List of vertex buffers by [`BufferId`], offset, and size. See [`DrawState::buffer_slice_key`]
25    vertex_buffers: Vec<Option<(BufferId, u64, u64)>>,
26    index_buffer: Option<(BufferId, u64, IndexFormat)>,
27
28    /// Stores whether this state is populated or empty for quick state invalidation
29    stores_state: bool,
30}
31
32impl DrawState {
33    /// Marks the `pipeline` as bound.
34    fn set_pipeline(&mut self, pipeline: RenderPipelineId) {
35        // TODO: do these need to be cleared?
36        // self.bind_groups.clear();
37        // self.vertex_buffers.clear();
38        // self.index_buffer = None;
39        self.pipeline = Some(pipeline);
40        self.stores_state = true;
41    }
42
43    /// Checks, whether the `pipeline` is already bound.
44    fn is_pipeline_set(&self, pipeline: RenderPipelineId) -> bool {
45        self.pipeline == Some(pipeline)
46    }
47
48    /// Marks the `bind_group` as bound to the `index`.
49    fn set_bind_group(&mut self, index: usize, bind_group: BindGroupId, dynamic_indices: &[u32]) {
50        let group = &mut self.bind_groups[index];
51        group.0 = Some(bind_group);
52        group.1.clear();
53        group.1.extend(dynamic_indices);
54        self.stores_state = true;
55    }
56
57    /// Checks, whether the `bind_group` is already bound to the `index`.
58    fn is_bind_group_set(
59        &self,
60        index: usize,
61        bind_group: BindGroupId,
62        dynamic_indices: &[u32],
63    ) -> bool {
64        if let Some(current_bind_group) = self.bind_groups.get(index) {
65            current_bind_group.0 == Some(bind_group) && dynamic_indices == current_bind_group.1
66        } else {
67            false
68        }
69    }
70
71    /// Marks the vertex `buffer` as bound to the `index`.
72    fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice) {
73        self.vertex_buffers[index] = Some(self.buffer_slice_key(&buffer_slice));
74        self.stores_state = true;
75    }
76
77    /// Checks, whether the vertex `buffer` is already bound to the `index`.
78    fn is_vertex_buffer_set(&self, index: usize, buffer_slice: &BufferSlice) -> bool {
79        if let Some(current) = self.vertex_buffers.get(index) {
80            *current == Some(self.buffer_slice_key(buffer_slice))
81        } else {
82            false
83        }
84    }
85
86    /// Returns the value used for checking whether `BufferSlice`s are equivalent.
87    fn buffer_slice_key(&self, buffer_slice: &BufferSlice) -> (BufferId, u64, u64) {
88        (
89            buffer_slice.id(),
90            buffer_slice.offset(),
91            buffer_slice.size(),
92        )
93    }
94
95    /// Marks the index `buffer` as bound.
96    fn set_index_buffer(&mut self, buffer: BufferId, offset: u64, index_format: IndexFormat) {
97        self.index_buffer = Some((buffer, offset, index_format));
98        self.stores_state = true;
99    }
100
101    /// Checks, whether the index `buffer` is already bound.
102    fn is_index_buffer_set(
103        &self,
104        buffer: BufferId,
105        offset: u64,
106        index_format: IndexFormat,
107    ) -> bool {
108        self.index_buffer == Some((buffer, offset, index_format))
109    }
110
111    /// Resets tracking state
112    pub fn reset_tracking(&mut self) {
113        if !self.stores_state {
114            return;
115        }
116        self.pipeline = None;
117        self.bind_groups.iter_mut().for_each(|val| {
118            val.0 = None;
119            val.1.clear();
120        });
121        self.vertex_buffers.iter_mut().for_each(|val| {
122            *val = None;
123        });
124        self.index_buffer = None;
125        self.stores_state = false;
126    }
127}
128
129/// A [`RenderPass`], which tracks the current pipeline state to skip redundant operations.
130///
131/// It is used to set the current [`RenderPipeline`], [`BindGroup`]s and [`Buffer`]s.
132/// After all requirements are specified, draw calls can be issued.
133pub struct TrackedRenderPass<'a> {
134    pass: RenderPass<'a>,
135    state: DrawState,
136}
137
138impl<'a> TrackedRenderPass<'a> {
139    /// Tracks the supplied render pass.
140    pub fn new(device: &RenderDevice, pass: RenderPass<'a>) -> Self {
141        let limits = device.limits();
142        let max_bind_groups = limits.max_bind_groups as usize;
143        let max_vertex_buffers = limits.max_vertex_buffers as usize;
144        Self {
145            state: DrawState {
146                bind_groups: vec![(None, Vec::new()); max_bind_groups],
147                vertex_buffers: vec![None; max_vertex_buffers],
148                ..default()
149            },
150            pass,
151        }
152    }
153
154    /// Returns the wgpu [`RenderPass`].
155    ///
156    /// Function invalidates internal tracking state,
157    /// some redundant pipeline operations may not be skipped.
158    pub fn wgpu_pass(&mut self) -> &mut RenderPass<'a> {
159        self.state.reset_tracking();
160        &mut self.pass
161    }
162
163    /// Sets the active [`RenderPipeline`].
164    ///
165    /// Subsequent draw calls will exhibit the behavior defined by the `pipeline`.
166    pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {
167        detailed_trace!("set pipeline: {:?}", pipeline);
168        if self.state.is_pipeline_set(pipeline.id()) {
169            return;
170        }
171        self.pass.set_pipeline(pipeline);
172        self.state.set_pipeline(pipeline.id());
173    }
174
175    /// Sets the active bind group for a given bind group index. The bind group layout
176    /// in the active pipeline when any `draw()` function is called must match the layout of
177    /// this bind group.
178    ///
179    /// If the bind group have dynamic offsets, provide them in binding order.
180    /// These offsets have to be aligned to [`WgpuLimits::min_uniform_buffer_offset_alignment`](crate::settings::WgpuLimits::min_uniform_buffer_offset_alignment)
181    /// or [`WgpuLimits::min_storage_buffer_offset_alignment`](crate::settings::WgpuLimits::min_storage_buffer_offset_alignment) appropriately.
182    pub fn set_bind_group(
183        &mut self,
184        index: usize,
185        bind_group: &'a BindGroup,
186        dynamic_uniform_indices: &[u32],
187    ) {
188        if self
189            .state
190            .is_bind_group_set(index, bind_group.id(), dynamic_uniform_indices)
191        {
192            detailed_trace!(
193                "set bind_group {} (already set): {:?} ({:?})",
194                index,
195                bind_group,
196                dynamic_uniform_indices
197            );
198            return;
199        }
200        detailed_trace!(
201            "set bind_group {}: {:?} ({:?})",
202            index,
203            bind_group,
204            dynamic_uniform_indices
205        );
206
207        self.pass
208            .set_bind_group(index as u32, bind_group, dynamic_uniform_indices);
209        self.state
210            .set_bind_group(index, bind_group.id(), dynamic_uniform_indices);
211    }
212
213    /// Assign a vertex buffer to a slot.
214    ///
215    /// Subsequent calls to [`draw`] and [`draw_indexed`] on this
216    /// [`TrackedRenderPass`] will use `buffer` as one of the source vertex buffers.
217    ///
218    /// The `slot_index` refers to the index of the matching descriptor in
219    /// [`VertexState::buffers`](crate::render_resource::VertexState::buffers).
220    ///
221    /// [`draw`]: TrackedRenderPass::draw
222    /// [`draw_indexed`]: TrackedRenderPass::draw_indexed
223    pub fn set_vertex_buffer(&mut self, slot_index: usize, buffer_slice: BufferSlice<'a>) {
224        if self.state.is_vertex_buffer_set(slot_index, &buffer_slice) {
225            detailed_trace!(
226                "set vertex buffer {} (already set): {:?} (offset = {}, size = {})",
227                slot_index,
228                buffer_slice.id(),
229                buffer_slice.offset(),
230                buffer_slice.size(),
231            );
232            return;
233        }
234        detailed_trace!(
235            "set vertex buffer {}: {:?} (offset = {}, size = {})",
236            slot_index,
237            buffer_slice.id(),
238            buffer_slice.offset(),
239            buffer_slice.size(),
240        );
241
242        self.pass
243            .set_vertex_buffer(slot_index as u32, *buffer_slice);
244        self.state.set_vertex_buffer(slot_index, buffer_slice);
245    }
246
247    /// Sets the active index buffer.
248    ///
249    /// Subsequent calls to [`TrackedRenderPass::draw_indexed`] will use the buffer referenced by
250    /// `buffer_slice` as the source index buffer.
251    pub fn set_index_buffer(
252        &mut self,
253        buffer_slice: BufferSlice<'a>,
254        offset: u64,
255        index_format: IndexFormat,
256    ) {
257        if self
258            .state
259            .is_index_buffer_set(buffer_slice.id(), offset, index_format)
260        {
261            detailed_trace!(
262                "set index buffer (already set): {:?} ({})",
263                buffer_slice.id(),
264                offset
265            );
266            return;
267        }
268        detailed_trace!("set index buffer: {:?} ({})", buffer_slice.id(), offset);
269        self.pass.set_index_buffer(*buffer_slice, index_format);
270        self.state
271            .set_index_buffer(buffer_slice.id(), offset, index_format);
272    }
273
274    /// Draws primitives from the active vertex buffer(s).
275    ///
276    /// The active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].
277    pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
278        detailed_trace!("draw: {:?} {:?}", vertices, instances);
279        self.pass.draw(vertices, instances);
280    }
281
282    /// Draws indexed primitives using the active index buffer and the active vertex buffer(s).
283    ///
284    /// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the
285    /// active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].
286    pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
287        detailed_trace!(
288            "draw indexed: {:?} {} {:?}",
289            indices,
290            base_vertex,
291            instances
292        );
293        self.pass.draw_indexed(indices, base_vertex, instances);
294    }
295
296    /// Draws primitives from the active vertex buffer(s) based on the contents of the
297    /// `indirect_buffer`.
298    ///
299    /// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
300    ///
301    /// The structure expected in `indirect_buffer` is the following:
302    ///
303    /// ```
304    /// #[repr(C)]
305    /// struct DrawIndirect {
306    ///     vertex_count: u32, // The number of vertices to draw.
307    ///     instance_count: u32, // The number of instances to draw.
308    ///     first_vertex: u32, // The Index of the first vertex to draw.
309    ///     first_instance: u32, // The instance ID of the first instance to draw.
310    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
311    /// }
312    /// ```
313    pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {
314        detailed_trace!("draw indirect: {:?} {}", indirect_buffer, indirect_offset);
315        self.pass.draw_indirect(indirect_buffer, indirect_offset);
316    }
317
318    /// Draws indexed primitives using the active index buffer and the active vertex buffers,
319    /// based on the contents of the `indirect_buffer`.
320    ///
321    /// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the
322    /// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
323    ///
324    /// The structure expected in `indirect_buffer` is the following:
325    ///
326    /// ```
327    /// #[repr(C)]
328    /// struct DrawIndexedIndirect {
329    ///     vertex_count: u32, // The number of vertices to draw.
330    ///     instance_count: u32, // The number of instances to draw.
331    ///     first_index: u32, // The base index within the index buffer.
332    ///     vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.
333    ///     first_instance: u32, // The instance ID of the first instance to draw.
334    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
335    /// }
336    /// ```
337    pub fn draw_indexed_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {
338        detailed_trace!(
339            "draw indexed indirect: {:?} {}",
340            indirect_buffer,
341            indirect_offset
342        );
343        self.pass
344            .draw_indexed_indirect(indirect_buffer, indirect_offset);
345    }
346
347    /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the
348    /// `indirect_buffer`.`count` draw calls are issued.
349    ///
350    /// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
351    ///
352    /// `indirect_buffer` should contain `count` tightly packed elements of the following structure:
353    ///
354    /// ```
355    /// #[repr(C)]
356    /// struct DrawIndirect {
357    ///     vertex_count: u32, // The number of vertices to draw.
358    ///     instance_count: u32, // The number of instances to draw.
359    ///     first_vertex: u32, // The Index of the first vertex to draw.
360    ///     first_instance: u32, // The instance ID of the first instance to draw.
361    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
362    /// }
363    /// ```
364    pub fn multi_draw_indirect(
365        &mut self,
366        indirect_buffer: &'a Buffer,
367        indirect_offset: u64,
368        count: u32,
369    ) {
370        detailed_trace!(
371            "multi draw indirect: {:?} {}, {}x",
372            indirect_buffer,
373            indirect_offset,
374            count
375        );
376        self.pass
377            .multi_draw_indirect(indirect_buffer, indirect_offset, count);
378    }
379
380    /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of
381    /// the `indirect_buffer`.
382    /// The count buffer is read to determine how many draws to issue.
383    ///
384    /// The indirect buffer must be long enough to account for `max_count` draws, however only
385    /// `count` elements will be read, where `count` is the value read from `count_buffer` capped
386    /// at `max_count`.
387    ///
388    /// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
389    ///
390    /// `indirect_buffer` should contain `count` tightly packed elements of the following structure:
391    ///
392    /// ```
393    /// #[repr(C)]
394    /// struct DrawIndirect {
395    ///     vertex_count: u32, // The number of vertices to draw.
396    ///     instance_count: u32, // The number of instances to draw.
397    ///     first_vertex: u32, // The Index of the first vertex to draw.
398    ///     first_instance: u32, // The instance ID of the first instance to draw.
399    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
400    /// }
401    /// ```
402    pub fn multi_draw_indirect_count(
403        &mut self,
404        indirect_buffer: &'a Buffer,
405        indirect_offset: u64,
406        count_buffer: &'a Buffer,
407        count_offset: u64,
408        max_count: u32,
409    ) {
410        detailed_trace!(
411            "multi draw indirect count: {:?} {}, ({:?} {})x, max {}x",
412            indirect_buffer,
413            indirect_offset,
414            count_buffer,
415            count_offset,
416            max_count
417        );
418        self.pass.multi_draw_indirect_count(
419            indirect_buffer,
420            indirect_offset,
421            count_buffer,
422            count_offset,
423            max_count,
424        );
425    }
426
427    /// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,
428    /// based on the contents of the `indirect_buffer`. `count` draw calls are issued.
429    ///
430    /// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the
431    /// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
432    ///
433    /// `indirect_buffer` should contain `count` tightly packed elements of the following structure:
434    ///
435    /// ```
436    /// #[repr(C)]
437    /// struct DrawIndexedIndirect {
438    ///     vertex_count: u32, // The number of vertices to draw.
439    ///     instance_count: u32, // The number of instances to draw.
440    ///     first_index: u32, // The base index within the index buffer.
441    ///     vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.
442    ///     first_instance: u32, // The instance ID of the first instance to draw.
443    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
444    /// }
445    /// ```
446    pub fn multi_draw_indexed_indirect(
447        &mut self,
448        indirect_buffer: &'a Buffer,
449        indirect_offset: u64,
450        count: u32,
451    ) {
452        detailed_trace!(
453            "multi draw indexed indirect: {:?} {}, {}x",
454            indirect_buffer,
455            indirect_offset,
456            count
457        );
458        self.pass
459            .multi_draw_indexed_indirect(indirect_buffer, indirect_offset, count);
460    }
461
462    /// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,
463    /// based on the contents of the `indirect_buffer`.
464    /// The count buffer is read to determine how many draws to issue.
465    ///
466    /// The indirect buffer must be long enough to account for `max_count` draws, however only
467    /// `count` elements will be read, where `count` is the value read from `count_buffer` capped
468    /// at `max_count`.
469    ///
470    /// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the
471    /// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].
472    ///
473    /// `indirect_buffer` should contain `count` tightly packed elements of the following structure:
474    ///
475    /// ```
476    /// #[repr(C)]
477    /// struct DrawIndexedIndirect {
478    ///     vertex_count: u32, // The number of vertices to draw.
479    ///     instance_count: u32, // The number of instances to draw.
480    ///     first_index: u32, // The base index within the index buffer.
481    ///     vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.
482    ///     first_instance: u32, // The instance ID of the first instance to draw.
483    ///     // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.
484    /// }
485    /// ```
486    pub fn multi_draw_indexed_indirect_count(
487        &mut self,
488        indirect_buffer: &'a Buffer,
489        indirect_offset: u64,
490        count_buffer: &'a Buffer,
491        count_offset: u64,
492        max_count: u32,
493    ) {
494        detailed_trace!(
495            "multi draw indexed indirect count: {:?} {}, ({:?} {})x, max {}x",
496            indirect_buffer,
497            indirect_offset,
498            count_buffer,
499            count_offset,
500            max_count
501        );
502        self.pass.multi_draw_indexed_indirect_count(
503            indirect_buffer,
504            indirect_offset,
505            count_buffer,
506            count_offset,
507            max_count,
508        );
509    }
510
511    /// Sets the stencil reference.
512    ///
513    /// Subsequent stencil tests will test against this value.
514    pub fn set_stencil_reference(&mut self, reference: u32) {
515        detailed_trace!("set stencil reference: {}", reference);
516        self.pass.set_stencil_reference(reference);
517    }
518
519    /// Sets the scissor region.
520    ///
521    /// Subsequent draw calls will discard any fragments that fall outside this region.
522    pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
523        detailed_trace!("set_scissor_rect: {} {} {} {}", x, y, width, height);
524        self.pass.set_scissor_rect(x, y, width, height);
525    }
526
527    /// Set push constant data.
528    ///
529    /// `Features::PUSH_CONSTANTS` must be enabled on the device in order to call these functions.
530    pub fn set_push_constants(&mut self, stages: ShaderStages, offset: u32, data: &[u8]) {
531        detailed_trace!(
532            "set push constants: {:?} offset: {} data.len: {}",
533            stages,
534            offset,
535            data.len()
536        );
537        self.pass.set_push_constants(stages, offset, data);
538    }
539
540    /// Set the rendering viewport.
541    ///
542    /// Subsequent draw calls will be projected into that viewport.
543    pub fn set_viewport(
544        &mut self,
545        x: f32,
546        y: f32,
547        width: f32,
548        height: f32,
549        min_depth: f32,
550        max_depth: f32,
551    ) {
552        detailed_trace!(
553            "set viewport: {} {} {} {} {} {}",
554            x,
555            y,
556            width,
557            height,
558            min_depth,
559            max_depth
560        );
561        self.pass
562            .set_viewport(x, y, width, height, min_depth, max_depth);
563    }
564
565    /// Set the rendering viewport to the given camera [`Viewport`].
566    ///
567    /// Subsequent draw calls will be projected into that viewport.
568    pub fn set_camera_viewport(&mut self, viewport: &Viewport) {
569        self.set_viewport(
570            viewport.physical_position.x as f32,
571            viewport.physical_position.y as f32,
572            viewport.physical_size.x as f32,
573            viewport.physical_size.y as f32,
574            viewport.depth.start,
575            viewport.depth.end,
576        );
577    }
578
579    /// Insert a single debug marker.
580    ///
581    /// This is a GPU debugging feature. This has no effect on the rendering itself.
582    pub fn insert_debug_marker(&mut self, label: &str) {
583        detailed_trace!("insert debug marker: {}", label);
584        self.pass.insert_debug_marker(label);
585    }
586
587    /// Start a new debug group.
588    ///
589    /// Push a new debug group over the internal stack. Subsequent render commands and debug
590    /// markers are grouped into this new group, until [`pop_debug_group`] is called.
591    ///
592    /// ```
593    /// # fn example(mut pass: bevy_render::render_phase::TrackedRenderPass<'static>) {
594    /// pass.push_debug_group("Render the car");
595    /// // [setup pipeline etc...]
596    /// pass.draw(0..64, 0..1);
597    /// pass.pop_debug_group();
598    /// # }
599    /// ```
600    ///
601    /// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.
602    ///
603    /// This is a GPU debugging feature. This has no effect on the rendering itself.
604    ///
605    /// [`push_debug_group`]: TrackedRenderPass::push_debug_group
606    /// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group
607    pub fn push_debug_group(&mut self, label: &str) {
608        detailed_trace!("push_debug_group marker: {}", label);
609        self.pass.push_debug_group(label);
610    }
611
612    /// End the current debug group.
613    ///
614    /// Subsequent render commands and debug markers are not grouped anymore in
615    /// this group, but in the previous one (if any) or the default top-level one
616    /// if the debug group was the last one on the stack.
617    ///
618    /// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.
619    ///
620    /// This is a GPU debugging feature. This has no effect on the rendering itself.
621    ///
622    /// [`push_debug_group`]: TrackedRenderPass::push_debug_group
623    /// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group
624    pub fn pop_debug_group(&mut self) {
625        detailed_trace!("pop_debug_group");
626        self.pass.pop_debug_group();
627    }
628
629    /// Sets the blend color as used by some of the blending modes.
630    ///
631    /// Subsequent blending tests will test against this value.
632    pub fn set_blend_constant(&mut self, color: LinearRgba) {
633        detailed_trace!("set blend constant: {:?}", color);
634        self.pass.set_blend_constant(wgpu::Color::from(color));
635    }
636}
637
638impl WriteTimestamp for TrackedRenderPass<'_> {
639    fn write_timestamp(&mut self, query_set: &QuerySet, index: u32) {
640        self.pass.write_timestamp(query_set, index);
641    }
642}
643
644impl WritePipelineStatistics for TrackedRenderPass<'_> {
645    fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, index: u32) {
646        self.pass.begin_pipeline_statistics_query(query_set, index);
647    }
648
649    fn end_pipeline_statistics_query(&mut self) {
650        self.pass.end_pipeline_statistics_query();
651    }
652}
653
654impl Pass for TrackedRenderPass<'_> {
655    const KIND: PassKind = PassKind::Render;
656}