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