Skip to main content

bevy_render/render_phase/
draw_state.rs

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