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}