Path: blob/main/crates/bevy_render/src/render_phase/draw_state.rs
6596 views
use crate::{1diagnostic::internal::{Pass, PassKind, WritePipelineStatistics, WriteTimestamp},2render_resource::{3BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,4ShaderStages,5},6renderer::RenderDevice,7};8use bevy_camera::Viewport;9use bevy_color::LinearRgba;10use bevy_utils::default;11use core::ops::Range;12use wgpu::{IndexFormat, QuerySet, RenderPass};1314#[cfg(feature = "detailed_trace")]15use tracing::trace;1617/// Tracks the state of a [`TrackedRenderPass`].18///19/// This is used to skip redundant operations on the [`TrackedRenderPass`] (e.g. setting an already20/// set pipeline, binding an already bound bind group). These operations can otherwise be fairly21/// costly due to IO to the GPU, so deduplicating these calls results in a speedup.22#[derive(Debug, Default)]23struct DrawState {24pipeline: Option<RenderPipelineId>,25bind_groups: Vec<(Option<BindGroupId>, Vec<u32>)>,26/// List of vertex buffers by [`BufferId`], offset, and size. See [`DrawState::buffer_slice_key`]27vertex_buffers: Vec<Option<(BufferId, u64, u64)>>,28index_buffer: Option<(BufferId, u64, IndexFormat)>,2930/// Stores whether this state is populated or empty for quick state invalidation31stores_state: bool,32}3334impl DrawState {35/// Marks the `pipeline` as bound.36fn set_pipeline(&mut self, pipeline: RenderPipelineId) {37// TODO: do these need to be cleared?38// self.bind_groups.clear();39// self.vertex_buffers.clear();40// self.index_buffer = None;41self.pipeline = Some(pipeline);42self.stores_state = true;43}4445/// Checks, whether the `pipeline` is already bound.46fn is_pipeline_set(&self, pipeline: RenderPipelineId) -> bool {47self.pipeline == Some(pipeline)48}4950/// Marks the `bind_group` as bound to the `index`.51fn set_bind_group(&mut self, index: usize, bind_group: BindGroupId, dynamic_indices: &[u32]) {52let group = &mut self.bind_groups[index];53group.0 = Some(bind_group);54group.1.clear();55group.1.extend(dynamic_indices);56self.stores_state = true;57}5859/// Checks, whether the `bind_group` is already bound to the `index`.60fn is_bind_group_set(61&self,62index: usize,63bind_group: BindGroupId,64dynamic_indices: &[u32],65) -> bool {66if let Some(current_bind_group) = self.bind_groups.get(index) {67current_bind_group.0 == Some(bind_group) && dynamic_indices == current_bind_group.168} else {69false70}71}7273/// Marks the vertex `buffer` as bound to the `index`.74fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice) {75self.vertex_buffers[index] = Some(self.buffer_slice_key(&buffer_slice));76self.stores_state = true;77}7879/// Checks, whether the vertex `buffer` is already bound to the `index`.80fn is_vertex_buffer_set(&self, index: usize, buffer_slice: &BufferSlice) -> bool {81if let Some(current) = self.vertex_buffers.get(index) {82*current == Some(self.buffer_slice_key(buffer_slice))83} else {84false85}86}8788/// Returns the value used for checking whether `BufferSlice`s are equivalent.89fn buffer_slice_key(&self, buffer_slice: &BufferSlice) -> (BufferId, u64, u64) {90(91buffer_slice.id(),92buffer_slice.offset(),93buffer_slice.size(),94)95}9697/// Marks the index `buffer` as bound.98fn set_index_buffer(&mut self, buffer: BufferId, offset: u64, index_format: IndexFormat) {99self.index_buffer = Some((buffer, offset, index_format));100self.stores_state = true;101}102103/// Checks, whether the index `buffer` is already bound.104fn is_index_buffer_set(105&self,106buffer: BufferId,107offset: u64,108index_format: IndexFormat,109) -> bool {110self.index_buffer == Some((buffer, offset, index_format))111}112113/// Resets tracking state114pub fn reset_tracking(&mut self) {115if !self.stores_state {116return;117}118self.pipeline = None;119self.bind_groups.iter_mut().for_each(|val| {120val.0 = None;121val.1.clear();122});123self.vertex_buffers.iter_mut().for_each(|val| {124*val = None;125});126self.index_buffer = None;127self.stores_state = false;128}129}130131/// A [`RenderPass`], which tracks the current pipeline state to skip redundant operations.132///133/// It is used to set the current [`RenderPipeline`], [`BindGroup`]s and [`Buffer`]s.134/// After all requirements are specified, draw calls can be issued.135pub struct TrackedRenderPass<'a> {136pass: RenderPass<'a>,137state: DrawState,138}139140impl<'a> TrackedRenderPass<'a> {141/// Tracks the supplied render pass.142pub fn new(device: &RenderDevice, pass: RenderPass<'a>) -> Self {143let limits = device.limits();144let max_bind_groups = limits.max_bind_groups as usize;145let max_vertex_buffers = limits.max_vertex_buffers as usize;146Self {147state: DrawState {148bind_groups: vec![(None, Vec::new()); max_bind_groups],149vertex_buffers: vec![None; max_vertex_buffers],150..default()151},152pass,153}154}155156/// Returns the wgpu [`RenderPass`].157///158/// Function invalidates internal tracking state,159/// some redundant pipeline operations may not be skipped.160pub fn wgpu_pass(&mut self) -> &mut RenderPass<'a> {161self.state.reset_tracking();162&mut self.pass163}164165/// Sets the active [`RenderPipeline`].166///167/// Subsequent draw calls will exhibit the behavior defined by the `pipeline`.168pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {169#[cfg(feature = "detailed_trace")]170trace!("set pipeline: {:?}", pipeline);171if self.state.is_pipeline_set(pipeline.id()) {172return;173}174self.pass.set_pipeline(pipeline);175self.state.set_pipeline(pipeline.id());176}177178/// Sets the active bind group for a given bind group index. The bind group layout179/// in the active pipeline when any `draw()` function is called must match the layout of180/// this bind group.181///182/// If the bind group have dynamic offsets, provide them in binding order.183/// These offsets have to be aligned to [`WgpuLimits::min_uniform_buffer_offset_alignment`](crate::settings::WgpuLimits::min_uniform_buffer_offset_alignment)184/// or [`WgpuLimits::min_storage_buffer_offset_alignment`](crate::settings::WgpuLimits::min_storage_buffer_offset_alignment) appropriately.185pub fn set_bind_group(186&mut self,187index: usize,188bind_group: &'a BindGroup,189dynamic_uniform_indices: &[u32],190) {191if self192.state193.is_bind_group_set(index, bind_group.id(), dynamic_uniform_indices)194{195#[cfg(feature = "detailed_trace")]196trace!(197"set bind_group {} (already set): {:?} ({:?})",198index,199bind_group,200dynamic_uniform_indices201);202return;203}204#[cfg(feature = "detailed_trace")]205trace!(206"set bind_group {}: {:?} ({:?})",207index,208bind_group,209dynamic_uniform_indices210);211212self.pass213.set_bind_group(index as u32, bind_group, dynamic_uniform_indices);214self.state215.set_bind_group(index, bind_group.id(), dynamic_uniform_indices);216}217218/// Assign a vertex buffer to a slot.219///220/// Subsequent calls to [`draw`] and [`draw_indexed`] on this221/// [`TrackedRenderPass`] will use `buffer` as one of the source vertex buffers.222///223/// The `slot_index` refers to the index of the matching descriptor in224/// [`VertexState::buffers`](crate::render_resource::VertexState::buffers).225///226/// [`draw`]: TrackedRenderPass::draw227/// [`draw_indexed`]: TrackedRenderPass::draw_indexed228pub fn set_vertex_buffer(&mut self, slot_index: usize, buffer_slice: BufferSlice<'a>) {229if self.state.is_vertex_buffer_set(slot_index, &buffer_slice) {230#[cfg(feature = "detailed_trace")]231trace!(232"set vertex buffer {} (already set): {:?} (offset = {}, size = {})",233slot_index,234buffer_slice.id(),235buffer_slice.offset(),236buffer_slice.size(),237);238return;239}240#[cfg(feature = "detailed_trace")]241trace!(242"set vertex buffer {}: {:?} (offset = {}, size = {})",243slot_index,244buffer_slice.id(),245buffer_slice.offset(),246buffer_slice.size(),247);248249self.pass250.set_vertex_buffer(slot_index as u32, *buffer_slice);251self.state.set_vertex_buffer(slot_index, buffer_slice);252}253254/// Sets the active index buffer.255///256/// Subsequent calls to [`TrackedRenderPass::draw_indexed`] will use the buffer referenced by257/// `buffer_slice` as the source index buffer.258pub fn set_index_buffer(259&mut self,260buffer_slice: BufferSlice<'a>,261offset: u64,262index_format: IndexFormat,263) {264if self265.state266.is_index_buffer_set(buffer_slice.id(), offset, index_format)267{268#[cfg(feature = "detailed_trace")]269trace!(270"set index buffer (already set): {:?} ({})",271buffer_slice.id(),272offset273);274return;275}276#[cfg(feature = "detailed_trace")]277trace!("set index buffer: {:?} ({})", buffer_slice.id(), offset);278self.pass.set_index_buffer(*buffer_slice, index_format);279self.state280.set_index_buffer(buffer_slice.id(), offset, index_format);281}282283/// Draws primitives from the active vertex buffer(s).284///285/// The active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].286pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {287#[cfg(feature = "detailed_trace")]288trace!("draw: {:?} {:?}", vertices, instances);289self.pass.draw(vertices, instances);290}291292/// Draws indexed primitives using the active index buffer and the active vertex buffer(s).293///294/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the295/// active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].296pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {297#[cfg(feature = "detailed_trace")]298trace!(299"draw indexed: {:?} {} {:?}",300indices,301base_vertex,302instances303);304self.pass.draw_indexed(indices, base_vertex, instances);305}306307/// Draws primitives from the active vertex buffer(s) based on the contents of the308/// `indirect_buffer`.309///310/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].311///312/// The structure expected in `indirect_buffer` is the following:313///314/// ```315/// #[repr(C)]316/// struct DrawIndirect {317/// vertex_count: u32, // The number of vertices to draw.318/// instance_count: u32, // The number of instances to draw.319/// first_vertex: u32, // The Index of the first vertex to draw.320/// first_instance: u32, // The instance ID of the first instance to draw.321/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.322/// }323/// ```324pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {325#[cfg(feature = "detailed_trace")]326trace!("draw indirect: {:?} {}", indirect_buffer, indirect_offset);327self.pass.draw_indirect(indirect_buffer, indirect_offset);328}329330/// Draws indexed primitives using the active index buffer and the active vertex buffers,331/// based on the contents of the `indirect_buffer`.332///333/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the334/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].335///336/// The structure expected in `indirect_buffer` is the following:337///338/// ```339/// #[repr(C)]340/// struct DrawIndexedIndirect {341/// vertex_count: u32, // The number of vertices to draw.342/// instance_count: u32, // The number of instances to draw.343/// first_index: u32, // The base index within the index buffer.344/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.345/// first_instance: u32, // The instance ID of the first instance to draw.346/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.347/// }348/// ```349pub fn draw_indexed_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: u64) {350#[cfg(feature = "detailed_trace")]351trace!(352"draw indexed indirect: {:?} {}",353indirect_buffer,354indirect_offset355);356self.pass357.draw_indexed_indirect(indirect_buffer, indirect_offset);358}359360/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the361/// `indirect_buffer`.`count` draw calls are issued.362///363/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].364///365/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:366///367/// ```368/// #[repr(C)]369/// struct DrawIndirect {370/// vertex_count: u32, // The number of vertices to draw.371/// instance_count: u32, // The number of instances to draw.372/// first_vertex: u32, // The Index of the first vertex to draw.373/// first_instance: u32, // The instance ID of the first instance to draw.374/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.375/// }376/// ```377pub fn multi_draw_indirect(378&mut self,379indirect_buffer: &'a Buffer,380indirect_offset: u64,381count: u32,382) {383#[cfg(feature = "detailed_trace")]384trace!(385"multi draw indirect: {:?} {}, {}x",386indirect_buffer,387indirect_offset,388count389);390self.pass391.multi_draw_indirect(indirect_buffer, indirect_offset, count);392}393394/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of395/// the `indirect_buffer`.396/// The count buffer is read to determine how many draws to issue.397///398/// The indirect buffer must be long enough to account for `max_count` draws, however only399/// `count` elements will be read, where `count` is the value read from `count_buffer` capped400/// at `max_count`.401///402/// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].403///404/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:405///406/// ```407/// #[repr(C)]408/// struct DrawIndirect {409/// vertex_count: u32, // The number of vertices to draw.410/// instance_count: u32, // The number of instances to draw.411/// first_vertex: u32, // The Index of the first vertex to draw.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/// ```416pub fn multi_draw_indirect_count(417&mut self,418indirect_buffer: &'a Buffer,419indirect_offset: u64,420count_buffer: &'a Buffer,421count_offset: u64,422max_count: u32,423) {424#[cfg(feature = "detailed_trace")]425trace!(426"multi draw indirect count: {:?} {}, ({:?} {})x, max {}x",427indirect_buffer,428indirect_offset,429count_buffer,430count_offset,431max_count432);433self.pass.multi_draw_indirect_count(434indirect_buffer,435indirect_offset,436count_buffer,437count_offset,438max_count,439);440}441442/// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,443/// based on the contents of the `indirect_buffer`. `count` draw calls are issued.444///445/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the446/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].447///448/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:449///450/// ```451/// #[repr(C)]452/// struct DrawIndexedIndirect {453/// vertex_count: u32, // The number of vertices to draw.454/// instance_count: u32, // The number of instances to draw.455/// first_index: u32, // The base index within the index buffer.456/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.457/// first_instance: u32, // The instance ID of the first instance to draw.458/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.459/// }460/// ```461pub fn multi_draw_indexed_indirect(462&mut self,463indirect_buffer: &'a Buffer,464indirect_offset: u64,465count: u32,466) {467#[cfg(feature = "detailed_trace")]468trace!(469"multi draw indexed indirect: {:?} {}, {}x",470indirect_buffer,471indirect_offset,472count473);474self.pass475.multi_draw_indexed_indirect(indirect_buffer, indirect_offset, count);476}477478/// Dispatches multiple draw calls from the active index buffer and the active vertex buffers,479/// based on the contents of the `indirect_buffer`.480/// The count buffer is read to determine how many draws to issue.481///482/// The indirect buffer must be long enough to account for `max_count` draws, however only483/// `count` elements will be read, where `count` is the value read from `count_buffer` capped484/// at `max_count`.485///486/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the487/// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`].488///489/// `indirect_buffer` should contain `count` tightly packed elements of the following structure:490///491/// ```492/// #[repr(C)]493/// struct DrawIndexedIndirect {494/// vertex_count: u32, // The number of vertices to draw.495/// instance_count: u32, // The number of instances to draw.496/// first_index: u32, // The base index within the index buffer.497/// vertex_offset: i32, // The value added to the vertex index before indexing into the vertex buffer.498/// first_instance: u32, // The instance ID of the first instance to draw.499/// // has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`] is enabled.500/// }501/// ```502pub fn multi_draw_indexed_indirect_count(503&mut self,504indirect_buffer: &'a Buffer,505indirect_offset: u64,506count_buffer: &'a Buffer,507count_offset: u64,508max_count: u32,509) {510#[cfg(feature = "detailed_trace")]511trace!(512"multi draw indexed indirect count: {:?} {}, ({:?} {})x, max {}x",513indirect_buffer,514indirect_offset,515count_buffer,516count_offset,517max_count518);519self.pass.multi_draw_indexed_indirect_count(520indirect_buffer,521indirect_offset,522count_buffer,523count_offset,524max_count,525);526}527528/// Sets the stencil reference.529///530/// Subsequent stencil tests will test against this value.531pub fn set_stencil_reference(&mut self, reference: u32) {532#[cfg(feature = "detailed_trace")]533trace!("set stencil reference: {}", reference);534self.pass.set_stencil_reference(reference);535}536537/// Sets the scissor region.538///539/// Subsequent draw calls will discard any fragments that fall outside this region.540pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {541#[cfg(feature = "detailed_trace")]542trace!("set_scissor_rect: {} {} {} {}", x, y, width, height);543self.pass.set_scissor_rect(x, y, width, height);544}545546/// Set push constant data.547///548/// `Features::PUSH_CONSTANTS` must be enabled on the device in order to call these functions.549pub fn set_push_constants(&mut self, stages: ShaderStages, offset: u32, data: &[u8]) {550#[cfg(feature = "detailed_trace")]551trace!(552"set push constants: {:?} offset: {} data.len: {}",553stages,554offset,555data.len()556);557self.pass.set_push_constants(stages, offset, data);558}559560/// Set the rendering viewport.561///562/// Subsequent draw calls will be projected into that viewport.563pub fn set_viewport(564&mut self,565x: f32,566y: f32,567width: f32,568height: f32,569min_depth: f32,570max_depth: f32,571) {572#[cfg(feature = "detailed_trace")]573trace!(574"set viewport: {} {} {} {} {} {}",575x,576y,577width,578height,579min_depth,580max_depth581);582self.pass583.set_viewport(x, y, width, height, min_depth, max_depth);584}585586/// Set the rendering viewport to the given camera [`Viewport`].587///588/// Subsequent draw calls will be projected into that viewport.589pub fn set_camera_viewport(&mut self, viewport: &Viewport) {590self.set_viewport(591viewport.physical_position.x as f32,592viewport.physical_position.y as f32,593viewport.physical_size.x as f32,594viewport.physical_size.y as f32,595viewport.depth.start,596viewport.depth.end,597);598}599600/// Insert a single debug marker.601///602/// This is a GPU debugging feature. This has no effect on the rendering itself.603pub fn insert_debug_marker(&mut self, label: &str) {604#[cfg(feature = "detailed_trace")]605trace!("insert debug marker: {}", label);606self.pass.insert_debug_marker(label);607}608609/// Start a new debug group.610///611/// Push a new debug group over the internal stack. Subsequent render commands and debug612/// markers are grouped into this new group, until [`pop_debug_group`] is called.613///614/// ```615/// # fn example(mut pass: bevy_render::render_phase::TrackedRenderPass<'static>) {616/// pass.push_debug_group("Render the car");617/// // [setup pipeline etc...]618/// pass.draw(0..64, 0..1);619/// pass.pop_debug_group();620/// # }621/// ```622///623/// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.624///625/// This is a GPU debugging feature. This has no effect on the rendering itself.626///627/// [`push_debug_group`]: TrackedRenderPass::push_debug_group628/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group629pub fn push_debug_group(&mut self, label: &str) {630#[cfg(feature = "detailed_trace")]631trace!("push_debug_group marker: {}", label);632self.pass.push_debug_group(label);633}634635/// End the current debug group.636///637/// Subsequent render commands and debug markers are not grouped anymore in638/// this group, but in the previous one (if any) or the default top-level one639/// if the debug group was the last one on the stack.640///641/// Note that [`push_debug_group`] and [`pop_debug_group`] must always be called in pairs.642///643/// This is a GPU debugging feature. This has no effect on the rendering itself.644///645/// [`push_debug_group`]: TrackedRenderPass::push_debug_group646/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group647pub fn pop_debug_group(&mut self) {648#[cfg(feature = "detailed_trace")]649trace!("pop_debug_group");650self.pass.pop_debug_group();651}652653/// Sets the blend color as used by some of the blending modes.654///655/// Subsequent blending tests will test against this value.656pub fn set_blend_constant(&mut self, color: LinearRgba) {657#[cfg(feature = "detailed_trace")]658trace!("set blend constant: {:?}", color);659self.pass.set_blend_constant(wgpu::Color::from(color));660}661}662663impl WriteTimestamp for TrackedRenderPass<'_> {664fn write_timestamp(&mut self, query_set: &QuerySet, index: u32) {665self.pass.write_timestamp(query_set, index);666}667}668669impl WritePipelineStatistics for TrackedRenderPass<'_> {670fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, index: u32) {671self.pass.begin_pipeline_statistics_query(query_set, index);672}673674fn end_pipeline_statistics_query(&mut self) {675self.pass.end_pipeline_statistics_query();676}677}678679impl Pass for TrackedRenderPass<'_> {680const KIND: PassKind = PassKind::Render;681}682683684