diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 55201d66e8733..91e95f481a536 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -16,7 +16,7 @@ use bevy_core_pipeline::{ }; use bevy_ecs::{ prelude::*, - query::ROQueryItem, + query::{QueryItem, ROQueryItem}, system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Affine3, Mat4, Vec2, Vec4}; @@ -649,7 +649,7 @@ impl GetBatchData for MeshPipeline { type BufferData = MeshUniform; fn get_batch_data( - (material_bind_group_id, mesh_handle, mesh_transforms): ::Item<'_>, + (material_bind_group_id, mesh_handle, mesh_transforms): QueryItem, ) -> (Self::CompareData, Self::BufferData) { ( (material_bind_group_id.cloned(), mesh_handle.id()), diff --git a/crates/bevy_render/src/batching/mod.rs b/crates/bevy_render/src/batching/mod.rs index 74c2c3fc42f74..87fe8b2a02410 100644 --- a/crates/bevy_render/src/batching/mod.rs +++ b/crates/bevy_render/src/batching/mod.rs @@ -1,7 +1,7 @@ use bevy_ecs::{ component::Component, prelude::Res, - query::{Has, ReadOnlyWorldQuery, WorldQuery}, + query::{Has, QueryItem, ReadOnlyWorldQuery}, system::{Query, ResMut}, }; use nonmax::NonMaxU32; @@ -29,25 +29,26 @@ pub struct NoAutomaticBatching; /// result of the `prepare_and_batch_meshes` system, e.g. due to having to split /// data across separate uniform bindings within the same buffer due to the /// maximum uniform buffer binding size. +#[derive(PartialEq)] struct BatchMeta { /// The pipeline id encompasses all pipeline configuration including vertex /// buffers and layouts, shaders and their specializations, bind group /// layouts, etc. - pub pipeline_id: CachedRenderPipelineId, + pipeline_id: CachedRenderPipelineId, /// The draw function id defines the RenderCommands that are called to /// set the pipeline and bindings, and make the draw command - pub draw_function_id: DrawFunctionId, - pub dynamic_offset: Option, - pub user_data: T, + draw_function_id: DrawFunctionId, + dynamic_offset: Option, + user_data: T, } - -impl PartialEq for BatchMeta { - #[inline] - fn eq(&self, other: &BatchMeta) -> bool { - self.pipeline_id == other.pipeline_id - && self.draw_function_id == other.draw_function_id - && self.dynamic_offset == other.dynamic_offset - && self.user_data == other.user_data +impl BatchMeta { + fn new(item: &impl CachedRenderPipelinePhaseItem, user_data: T) -> Self { + BatchMeta { + pipeline_id: item.cached_pipeline(), + draw_function_id: item.draw_function(), + dynamic_offset: item.dynamic_offset(), + user_data, + } } } @@ -55,9 +56,7 @@ pub trait GetBatchData { type Query: ReadOnlyWorldQuery; type CompareData: PartialEq; type BufferData: GpuArrayBufferable + Sync + Send + 'static; - fn get_batch_data( - batch_data: ::Item<'_>, - ) -> (Self::CompareData, Self::BufferData); + fn get_batch_data(batch_data: QueryItem) -> (Self::CompareData, Self::BufferData); } /// Batch the items in a render phase. This means comparing metadata needed to draw each phase item @@ -69,58 +68,31 @@ pub fn batch_and_prepare_render_phase Option> { - let Ok((no_batching, batch_data)) = query.get(item.entity()) else { - return None; - }; - - let (user_data, buffer_data) = F::get_batch_data(batch_data); + let mut process_item = |item: &mut I| { + let (no_auto_batching, batch_query_item) = query.get(item.entity()).ok()?; + let (user_data, buffer_data) = F::get_batch_data(batch_query_item); let buffer_index = gpu_array_buffer.push(buffer_data); - *item.batch_range_mut() = buffer_index.index.get()..buffer_index.index.get() + 1; + let index = buffer_index.index.get(); + *item.batch_range_mut() = index..index + 1; *item.dynamic_offset_mut() = buffer_index.dynamic_offset; - if no_batching { - None - } else { - Some(BatchMeta { - pipeline_id: item.cached_pipeline(), - draw_function_id: item.draw_function(), - dynamic_offset: buffer_index.dynamic_offset, - user_data, - }) - } + (!no_auto_batching).then(|| BatchMeta::new(item, user_data)) }; for mut phase in &mut views { - let mut items = phase.items.iter_mut().peekable(); - let mut batch_start_item = None; - let mut next_batch = items.peek_mut().and_then(|i| process_item(i)); - while let Some(item) = items.next() { - // Get the current batch meta and update the next batch meta - let Some(batch_meta) = std::mem::replace( - &mut next_batch, - items.peek_mut().and_then(|i| process_item(i)), - ) else { - // If the current phase item doesn't match the query or has NoAutomaticBatching, - // we don't modify it any further - continue; - }; - - let batch_end_item = item.batch_range().end; - - // If we are beginning a new batch, record the start item - if batch_start_item.is_none() { - batch_start_item = Some(item); - } - - if Some(&batch_meta) != next_batch.as_ref() { - // The next item doesn't match the current batch (or doesn't exist). - // Update the first phase item to render the full batch. - let batch_start_item = batch_start_item.take().unwrap(); - batch_start_item.batch_range_mut().end = batch_end_item; + let items = phase.items.iter_mut().map(|i| { + let batch_data = process_item(i); + (i.batch_range_mut(), batch_data) + }); + items.reduce(|(start_range, prev_batch_meta), (range, batch_meta)| { + if batch_meta.is_some() && prev_batch_meta == batch_meta { + start_range.end = range.end; + (start_range, prev_batch_meta) + } else { + (range, batch_meta) } - } + }); } } diff --git a/crates/bevy_render/src/render_phase/mod.rs b/crates/bevy_render/src/render_phase/mod.rs index dec5ddf77621d..bf04d2d13f438 100644 --- a/crates/bevy_render/src/render_phase/mod.rs +++ b/crates/bevy_render/src/render_phase/mod.rs @@ -39,7 +39,7 @@ use bevy_ecs::{ prelude::*, system::{lifetimeless::SRes, SystemParamItem}, }; -use std::ops::Range; +use std::{ops::Range, slice::SliceIndex}; /// A collection of all rendering instructions, that will be executed by the GPU, for a /// single render phase for a single view. @@ -87,22 +87,7 @@ impl RenderPhase { world: &'w World, view: Entity, ) { - let draw_functions = world.resource::>(); - let mut draw_functions = draw_functions.write(); - draw_functions.prepare(world); - - let mut index = 0; - while index < self.items.len() { - let item = &self.items[index]; - let batch_range = item.batch_range(); - if batch_range.is_empty() { - index += 1; - } else { - let draw_function = draw_functions.get_mut(item.draw_function()).unwrap(); - draw_function.draw(world, render_pass, view, item); - index += batch_range.len(); - } - } + self.render_range(render_pass, world, view, ..); } /// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions. @@ -111,17 +96,17 @@ impl RenderPhase { render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - range: Range, + range: impl SliceIndex<[I], Output = [I]>, ) { - let draw_functions = world.resource::>(); - let mut draw_functions = draw_functions.write(); - draw_functions.prepare(world); - let items = self .items .get(range) .expect("`Range` provided to `render_range()` is out of bounds"); + let draw_functions = world.resource::>(); + let mut draw_functions = draw_functions.write(); + draw_functions.prepare(world); + let mut index = 0; while index < items.len() { let item = &items[index]; diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 6228c69971e09..8ed10896b5980 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -4,7 +4,7 @@ use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_core_pipeline::core_2d::Transparent2d; use bevy_ecs::{ prelude::*, - query::{ROQueryItem, WorldQuery}, + query::{QueryItem, ROQueryItem}, system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Affine3, Vec2, Vec4}; @@ -334,9 +334,7 @@ impl GetBatchData for Mesh2dPipeline { type BufferData = Mesh2dUniform; fn get_batch_data( - (material_bind_group_id, mesh_handle, mesh_transforms): ::Item< - '_, - >, + (material_bind_group_id, mesh_handle, mesh_transforms): QueryItem, ) -> (Self::CompareData, Self::BufferData) { ( (material_bind_group_id.cloned(), mesh_handle.0.id()),