Path: blob/main/crates/bevy_render/src/render_phase/draw_state.rs
9298 views
use crate::{1diagnostic::internal::{Pass, PassKind, WritePipelineStatistics, WriteTimestamp},2render_resource::{3BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,4},5renderer::RenderDevice,6};7use bevy_camera::Viewport;8use bevy_color::LinearRgba;9use bevy_utils::default;10use core::ops::Range;11use wgpu::{IndexFormat, QuerySet, RenderPass};1213#[cfg(feature = "detailed_trace")]14use bevy_log::trace;1516type BufferSliceKey = (BufferId, wgpu::BufferAddress, wgpu::BufferSize);1718/// Tracks the state of a [`TrackedRenderPass`].19///20/// This is used to skip redundant operations on the [`TrackedRenderPass`] (e.g. setting an already21/// set pipeline, binding an already bound bind group). These operations can otherwise be fairly22/// costly due to IO to the GPU, so deduplicating these calls results in a speedup.23#[derive(Debug, Default)]24struct DrawState {25pipeline: Option<RenderPipelineId>,26bind_groups: Vec<(Option<BindGroupId>, Vec<u32>)>,27/// List of vertex buffers by [`BufferId`], offset, and size. See [`DrawState::buffer_slice_key`]28vertex_buffers: Vec<Option<BufferSliceKey>>,29index_buffer: Option<(BufferSliceKey, IndexFormat)>,3031/// Stores whether this state is populated or empty for quick state invalidation32stores_state: bool,33}3435impl DrawState {36/// Marks the `pipeline` as bound.37fn set_pipeline(&mut self, pipeline: RenderPipelineId) {38// TODO: do these need to be cleared?39// self.bind_groups.clear();40// self.vertex_buffers.clear();41// self.index_buffer = None;42self.pipeline = Some(pipeline);43self.stores_state = true;44}4546/// Checks, whether the `pipeline` is already bound.47fn is_pipeline_set(&self, pipeline: RenderPipelineId) -> bool {48self.pipeline == Some(pipeline)49}5051/// Marks the `bind_group` as bound to the `index`.52fn set_bind_group(&mut self, index: usize, bind_group: BindGroupId, dynamic_indices: &[u32]) {53let group = &mut self.bind_groups[index];54group.0 = Some(bind_group);55group.1.clear();56group.1.extend(dynamic_indices);57self.stores_state = true;58}5960/// Checks, whether the `bind_group` is already bound to the `index`.61fn is_bind_group_set(62&self,63index: usize,64bind_group: BindGroupId,65dynamic_indices: &[u32],66) -> bool {67if let Some(current_bind_group) = self.bind_groups.get(index) {68current_bind_group.0 == Some(bind_group) && dynamic_indices == current_bind_group.169} else {70false71}72}7374/// Marks the vertex `buffer` as bound to the `index`.75fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice) {76self.vertex_buffers[index] = Some(self.buffer_slice_key(&buffer_slice));77self.stores_state = true;78}7980/// Checks, whether the vertex `buffer` is already bound to the `index`.81fn is_vertex_buffer_set(&self, index: usize, buffer_slice: &BufferSlice) -> bool {82if let Some(current) = self.vertex_buffers.get(index) {83*current == Some(self.buffer_slice_key(buffer_slice))84} else {85false86}87}8889/// Returns the value used for checking whether `BufferSlice`s are equivalent.90fn buffer_slice_key(&self, buffer_slice: &BufferSlice) -> BufferSliceKey {91(92buffer_slice.id(),93buffer_slice.offset(),94buffer_slice.size(),95)96}9798/// Marks the index `buffer` as bound.99fn set_index_buffer(&mut self, buffer_slice: &BufferSlice, index_format: IndexFormat) {100self.index_buffer = Some((self.buffer_slice_key(buffer_slice), index_format));101self.stores_state = true;102}103104/// Checks, whether the index `buffer` is already bound.105fn is_index_buffer_set(&self, buffer: &BufferSlice, index_format: IndexFormat) -> bool {106self.index_buffer == Some((self.buffer_slice_key(buffer), index_format))107}108109/// Resets tracking state110pub fn reset_tracking(&mut self) {111if !self.stores_state {112return;113}114self.pipeline = None;115self.bind_groups.iter_mut().for_each(|val| {116val.0 = None;117val.1.clear();118});119self.vertex_buffers.iter_mut().for_each(|val| {120*val = None;121});122self.index_buffer = None;123self.stores_state = false;124}125}126127/// A [`RenderPass`], which tracks the current pipeline state to skip redundant operations.128///129/// It is used to set the current [`RenderPipeline`], [`BindGroup`]s and [`Buffer`]s.130/// After all requirements are specified, draw calls can be issued.131pub struct TrackedRenderPass<'a> {132pass: RenderPass<'a>,133state: DrawState,134}135136impl<'a> TrackedRenderPass<'a> {137/// Tracks the supplied render pass.138pub fn new(device: &RenderDevice, pass: RenderPass<'a>) -> Self {139let limits = device.limits();140let max_bind_groups = limits.max_bind_groups as usize;141let max_vertex_buffers = limits.max_vertex_buffers as usize;142Self {143state: DrawState {144bind_groups: vec![(None, Vec::new()); max_bind_groups],145vertex_buffers: vec![None; max_vertex_buffers],146..default()147},148pass,149}150}151152/// Returns the wgpu [`RenderPass`].153///154/// Function invalidates internal tracking state,155/// some redundant pipeline operations may not be skipped.156pub fn wgpu_pass(&mut self) -> &mut RenderPass<'a> {157self.state.reset_tracking();158&mut self.pass159}160161/// Sets the active [`RenderPipeline`].162///163/// Subsequent draw calls will exhibit the behavior defined by the `pipeline`.164pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {165#[cfg(feature = "detailed_trace")]166trace!("set pipeline: {:?}", pipeline);167if self.state.is_pipeline_set(pipeline.id()) {168return;169}170self.pass.set_pipeline(pipeline);171self.state.set_pipeline(pipeline.id());172}173174/// Sets the active bind group for a given bind group index. The bind group layout175/// in the active pipeline when any `draw()` function is called must match the layout of176/// this bind group.177///178/// If the bind group have dynamic offsets, provide them in binding order.179/// These offsets have to be aligned to [`WgpuLimits::min_uniform_buffer_offset_alignment`](crate::settings::WgpuLimits::min_uniform_buffer_offset_alignment)180/// or [`WgpuLimits::min_storage_buffer_offset_alignment`](crate::settings::WgpuLimits::min_storage_buffer_offset_alignment) appropriately.181pub fn set_bind_group(182&mut self,183index: usize,184bind_group: &'a BindGroup,185dynamic_uniform_indices: &[u32],186) {187if self188.state189.is_bind_group_set(index, bind_group.id(), dynamic_uniform_indices)190{191#[cfg(feature = "detailed_trace")]192trace!(193"set bind_group {} (already set): {:?} ({:?})",194index,195bind_group,196dynamic_uniform_indices197);198return;199}200#[cfg(feature = "detailed_trace")]201trace!(202"set bind_group {}: {:?} ({:?})",203index,204bind_group,205dynamic_uniform_indices206);207208self.pass209.set_bind_group(index as u32, bind_group, dynamic_uniform_indices);210self.state211.set_bind_group(index, bind_group.id(), dynamic_uniform_indices);212}213214/// Assign a vertex buffer to a slot.215///216/// Subsequent calls to [`draw`] and [`draw_indexed`] on this217/// [`TrackedRenderPass`] will use `buffer` as one of the source vertex buffers.218///219/// The `slot_index` refers to the index of the matching descriptor in220/// [`VertexState::buffers`](crate::render_resource::VertexState::buffers).221///222/// [`draw`]: TrackedRenderPass::draw223/// [`draw_indexed`]: TrackedRenderPass::draw_indexed224pub fn set_vertex_buffer(&mut self, slot_index: usize, buffer_slice: BufferSlice<'a>) {225if self.state.is_vertex_buffer_set(slot_index, &buffer_slice) {226#[cfg(feature = "detailed_trace")]227trace!(228"set vertex buffer {} (already set): {:?} (offset = {}, size = {})",229slot_index,230buffer_slice.id(),231buffer_slice.offset(),232buffer_slice.size(),233);234return;235}236#[cfg(feature = "detailed_trace")]237trace!(238"set vertex buffer {}: {:?} (offset = {}, size = {})",239slot_index,240buffer_slice.id(),241buffer_slice.offset(),242buffer_slice.size(),243);244245self.pass246.set_vertex_buffer(slot_index as u32, *buffer_slice);247self.state.set_vertex_buffer(slot_index, buffer_slice);248}249250/// Sets the active index buffer.251///252/// Subsequent calls to [`TrackedRenderPass::draw_indexed`] will use the buffer referenced by253/// `buffer_slice` as the source index buffer.254pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat) {255let already_set = self.state.is_index_buffer_set(&buffer_slice, index_format);256#[cfg(feature = "detailed_trace")]257trace!(258"set index buffer{}: {:?} (offset = {}, size = {})",259if already_set { " (already set)" } else { "" },260buffer_slice.id(),261buffer_slice.offset(),262buffer_slice.size(),263);264if already_set {265return;266}267self.pass.set_index_buffer(*buffer_slice, index_format);268self.state.set_index_buffer(&buffer_slice, index_format);269}270271/// Draws primitives from the active vertex buffer(s).272///273/// The active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].274pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {275#[cfg(feature = "detailed_trace")]276trace!("draw: {:?} {:?}", vertices, instances);277self.pass.draw(vertices, instances);278}279280/// Draws indexed primitives using the active index buffer and the active vertex buffer(s).281///282/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the283/// active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].284pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {285#[cfg(feature = "detailed_trace")]286trace!(287"draw indexed: {:?} {} {:?}",288indices,289base_vertex,290instances291);292self.pass.draw_indexed(indices, base_vertex, instances);293}294295/// Draws primitives from the active vertex buffer(s) based on the contents of the296/// `indirect_buffer`.297///298/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].299///300/// The structure expected in `indirect_buffer` is the following:301///302/// ```303/// #[repr(C)]304/// struct DrawIndirect {305/// vertex_count: u32, // The number of vertices to draw.306/// instance_count: u32, // The number of instances to draw.307/// first_vertex: u32, // The Index of the first vertex to draw.308/// first_instance: u32, // The instance ID of the first instance to draw.309/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.310/// }311/// ```312pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {313#[cfg(feature = "detailed_trace")]314trace!("draw indirect: {:?} {}", indirect_buffer, indirect_offset);315self.pass.draw_indirect(indirect_buffer, indirect_offset);316}317318/// Draws indexed primitives using the active index buffer and the active vertex buffers,319/// based on the contents of the `indirect_buffer`.320///321/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the322/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].323///324/// The structure expected in `indirect_buffer` is the following:325///326/// ```327/// #[repr(C)]328/// struct DrawIndexedIndirect {329/// vertex_count: u32, // The number of vertices to draw.330/// instance_count: u32, // The number of instances to draw.331/// first_index: u32, // The base index within the index buffer.332/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.333/// first_instance: u32, // The instance ID of the first instance to draw.334/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.335/// }336/// ```337pub fn draw_indexed_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {338#[cfg(feature = "detailed_trace")]339trace!(340"draw indexed indirect: {:?} {}",341indirect_buffer,342indirect_offset343);344self.pass345.draw_indexed_indirect(indirect_buffer, indirect_offset);346}347348/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the349/// `indirect_buffer`.`count` draw calls are issued.350///351/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].352///353/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:354///355/// ```356/// #[repr(C)]357/// struct DrawIndirect {358/// vertex_count: u32, // The number of vertices to draw.359/// instance_count: u32, // The number of instances to draw.360/// first_vertex: u32, // The Index of the first vertex to draw.361/// first_instance: u32, // The instance ID of the first instance to draw.362/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.363/// }364/// ```365pub fn multi_draw_indirect(366&mut self,367indirect_buffer: &'a Buffer,368indirect_offset: u64,369count: u32,370) {371#[cfg(feature = "detailed_trace")]372trace!(373"multi draw indirect: {:?} {}, {}x",374indirect_buffer,375indirect_offset,376count377);378self.pass379.multi_draw_indirect(indirect_buffer, indirect_offset, count);380}381382/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of383/// the `indirect_buffer`.384/// The count buffer is read to determine how many draws to issue.385///386/// The indirect buffer must be long enough to account for `max_count` draws, however only387/// `count` elements will be read, where `count` is the value read from `count_buffer` capped388/// at `max_count`.389///390/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].391///392/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:393///394/// ```395/// #[repr(C)]396/// struct DrawIndirect {397/// vertex_count: u32, // The number of vertices to draw.398/// instance_count: u32, // The number of instances to draw.399/// first_vertex: u32, // The Index of the first vertex to draw.400/// first_instance: u32, // The instance ID of the first instance to draw.401/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.402/// }403/// ```404pub fn multi_draw_indirect_count(405&mut self,406indirect_buffer: &'a Buffer,407indirect_offset: u64,408count_buffer: &'a Buffer,409count_offset: u64,410max_count: u32,411) {412#[cfg(feature = "detailed_trace")]413trace!(414"multi draw indirect count: {:?} {}, ({:?} {})x, max {}x",415indirect_buffer,416indirect_offset,417count_buffer,418count_offset,419max_count420);421self.pass.multi_draw_indirect_count(422indirect_buffer,423indirect_offset,424count_buffer,425count_offset,426max_count,427);428}429430/// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,431/// based on the contents of the `indirect_buffer`. `count` draw calls are issued.432///433/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the434/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].435///436/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:437///438/// ```439/// #[repr(C)]440/// struct DrawIndexedIndirect {441/// vertex_count: u32, // The number of vertices to draw.442/// instance_count: u32, // The number of instances to draw.443/// first_index: u32, // The base index within the index buffer.444/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.445/// first_instance: u32, // The instance ID of the first instance to draw.446/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.447/// }448/// ```449pub fn multi_draw_indexed_indirect(450&mut self,451indirect_buffer: &'a Buffer,452indirect_offset: u64,453count: u32,454) {455#[cfg(feature = "detailed_trace")]456trace!(457"multi draw indexed indirect: {:?} {}, {}x",458indirect_buffer,459indirect_offset,460count461);462self.pass463.multi_draw_indexed_indirect(indirect_buffer, indirect_offset, count);464}465466/// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,467/// based on the contents of the `indirect_buffer`.468/// The count buffer is read to determine how many draws to issue.469///470/// The indirect buffer must be long enough to account for `max_count` draws, however only471/// `count` elements will be read, where `count` is the value read from `count_buffer` capped472/// at `max_count`.473///474/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the475/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].476///477/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:478///479/// ```480/// #[repr(C)]481/// struct DrawIndexedIndirect {482/// vertex_count: u32, // The number of vertices to draw.483/// instance_count: u32, // The number of instances to draw.484/// first_index: u32, // The base index within the index buffer.485/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.486/// first_instance: u32, // The instance ID of the first instance to draw.487/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.488/// }489/// ```490pub fn multi_draw_indexed_indirect_count(491&mut self,492indirect_buffer: &'a Buffer,493indirect_offset: u64,494count_buffer: &'a Buffer,495count_offset: u64,496max_count: u32,497) {498#[cfg(feature = "detailed_trace")]499trace!(500"multi draw indexed indirect count: {:?} {}, ({:?} {})x, max {}x",501indirect_buffer,502indirect_offset,503count_buffer,504count_offset,505max_count506);507self.pass.multi_draw_indexed_indirect_count(508indirect_buffer,509indirect_offset,510count_buffer,511count_offset,512max_count,513);514}515516/// Sets the stencil reference.517///518/// Subsequent stencil tests will test against this value.519pub fn set_stencil_reference(&mut self, reference: u32) {520#[cfg(feature = "detailed_trace")]521trace!("set stencil reference: {}", reference);522self.pass.set_stencil_reference(reference);523}524525/// Sets the scissor region.526///527/// Subsequent draw calls will discard any fragments that fall outside this region.528pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {529#[cfg(feature = "detailed_trace")]530trace!("set_scissor_rect: {} {} {} {}", x, y, width, height);531self.pass.set_scissor_rect(x, y, width, height);532}533534/// Set immediates data.535///536/// `Features::IMMEDIATES` must be enabled on the device in order to call these functions.537pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {538#[cfg(feature = "detailed_trace")]539trace!("set immediates offset: {} data.len: {}", offset, data.len());540self.pass.set_immediates(offset, data);541}542543/// Set the rendering viewport.544///545/// Subsequent draw calls will be projected into that viewport.546pub fn set_viewport(547&mut self,548x: f32,549y: f32,550width: f32,551height: f32,552min_depth: f32,553max_depth: f32,554) {555#[cfg(feature = "detailed_trace")]556trace!(557"set viewport: {} {} {} {} {} {}",558x,559y,560width,561height,562min_depth,563max_depth564);565self.pass566.set_viewport(x, y, width, height, min_depth, max_depth);567}568569/// Set the rendering viewport to the given camera [`Viewport`].570///571/// Subsequent draw calls will be projected into that viewport.572pub fn set_camera_viewport(&mut self, viewport: &Viewport) {573self.set_viewport(574viewport.physical_position.x as f32,575viewport.physical_position.y as f32,576viewport.physical_size.x as f32,577viewport.physical_size.y as f32,578viewport.depth.start,579viewport.depth.end,580);581}582583/// Insert a single debug marker.584///585/// This is a GPU debugging feature. This has no effect on the rendering itself.586pub fn insert_debug_marker(&mut self, label: &str) {587#[cfg(feature = "detailed_trace")]588trace!("insert debug marker: {}", label);589self.pass.insert_debug_marker(label);590}591592/// Start a new debug group.593///594/// Push a new debug group over the internal stack. Subsequent render commands and debug595/// markers are grouped into this new group, until [`pop_debug_group`] is called.596///597/// ```598/// # fn example(mut pass: bevy_render::render_phase::TrackedRenderPass<'static>) {599/// pass.push_debug_group("Render the car");600/// // [setup pipeline etc...]601/// pass.draw(0..64, 0..1);602/// pass.pop_debug_group();603/// # }604/// ```605///606/// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.607///608/// This is a GPU debugging feature. This has no effect on the rendering itself.609///610/// [`push_debug_group`]: TrackedRenderPass::push_debug_group611/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group612pub fn push_debug_group(&mut self, label: &str) {613#[cfg(feature = "detailed_trace")]614trace!("push_debug_group marker: {}", label);615self.pass.push_debug_group(label);616}617618/// End the current debug group.619///620/// Subsequent render commands and debug markers are not grouped anymore in621/// this group, but in the previous one (if any) or the default top-level one622/// if the debug group was the last one on the stack.623///624/// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.625///626/// This is a GPU debugging feature. This has no effect on the rendering itself.627///628/// [`push_debug_group`]: TrackedRenderPass::push_debug_group629/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group630pub fn pop_debug_group(&mut self) {631#[cfg(feature = "detailed_trace")]632trace!("pop_debug_group");633self.pass.pop_debug_group();634}635636/// Sets the blend color as used by some of the blending modes.637///638/// Subsequent blending tests will test against this value.639pub fn set_blend_constant(&mut self, color: LinearRgba) {640#[cfg(feature = "detailed_trace")]641trace!("set blend constant: {:?}", color);642self.pass.set_blend_constant(wgpu::Color::from(color));643}644}645646impl WriteTimestamp for TrackedRenderPass<'_> {647fn write_timestamp(&mut self, query_set: &QuerySet, index: u32) {648self.pass.write_timestamp(query_set, index);649}650}651652impl WritePipelineStatistics for TrackedRenderPass<'_> {653fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, index: u32) {654self.pass.begin_pipeline_statistics_query(query_set, index);655}656657fn end_pipeline_statistics_query(&mut self) {658self.pass.end_pipeline_statistics_query();659}660}661662impl Pass for TrackedRenderPass<'_> {663const KIND: PassKind = PassKind::Render;664}665666667