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}