Path: blob/main/crates/bevy_render/src/batching/no_gpu_preprocessing.rs
6596 views
//! Batching functionality when GPU preprocessing isn't in use.12use bevy_derive::{Deref, DerefMut};3use bevy_ecs::entity::Entity;4use bevy_ecs::resource::Resource;5use bevy_ecs::system::{Res, ResMut, StaticSystemParam};6use smallvec::{smallvec, SmallVec};7use tracing::error;8use wgpu::BindingResource;910use crate::{11render_phase::{12BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSets,13CachedRenderPipelinePhaseItem, PhaseItemExtraIndex, SortedPhaseItem,14ViewBinnedRenderPhases, ViewSortedRenderPhases,15},16render_resource::{GpuArrayBuffer, GpuArrayBufferable},17renderer::{RenderDevice, RenderQueue},18};1920use super::{GetBatchData, GetFullBatchData};2122/// The GPU buffers holding the data needed to render batches.23///24/// For example, in the 3D PBR pipeline this holds `MeshUniform`s, which are the25/// `BD` type parameter in that mode.26#[derive(Resource, Deref, DerefMut)]27pub struct BatchedInstanceBuffer<BD>(pub GpuArrayBuffer<BD>)28where29BD: GpuArrayBufferable + Sync + Send + 'static;3031impl<BD> BatchedInstanceBuffer<BD>32where33BD: GpuArrayBufferable + Sync + Send + 'static,34{35/// Creates a new buffer.36pub fn new(render_device: &RenderDevice) -> Self {37BatchedInstanceBuffer(GpuArrayBuffer::new(render_device))38}3940/// Returns the binding of the buffer that contains the per-instance data.41///42/// If we're in the GPU instance buffer building mode, this buffer needs to43/// be filled in via a compute shader.44pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {45self.binding()46}47}4849/// A system that clears out the [`BatchedInstanceBuffer`] for the frame.50///51/// This needs to run before the CPU batched instance buffers are used.52pub fn clear_batched_cpu_instance_buffers<GBD>(53cpu_batched_instance_buffer: Option<ResMut<BatchedInstanceBuffer<GBD::BufferData>>>,54) where55GBD: GetBatchData,56{57if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer {58cpu_batched_instance_buffer.clear();59}60}6162/// Batch the items in a sorted render phase, when GPU instance buffer building63/// isn't in use. This means comparing metadata needed to draw each phase item64/// and trying to combine the draws into a batch.65pub fn batch_and_prepare_sorted_render_phase<I, GBD>(66batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,67mut phases: ResMut<ViewSortedRenderPhases<I>>,68param: StaticSystemParam<GBD::Param>,69) where70I: CachedRenderPipelinePhaseItem + SortedPhaseItem,71GBD: GetBatchData,72{73let system_param_item = param.into_inner();7475// We only process CPU-built batch data in this function.76let batched_instance_buffer = batched_instance_buffer.into_inner();7778for phase in phases.values_mut() {79super::batch_and_prepare_sorted_render_phase::<I, GBD>(phase, |item| {80let (buffer_data, compare_data) =81GBD::get_batch_data(&system_param_item, (item.entity(), item.main_entity()))?;82let buffer_index = batched_instance_buffer.push(buffer_data);8384let index = buffer_index.index;85let (batch_range, extra_index) = item.batch_range_and_extra_index_mut();86*batch_range = index..index + 1;87*extra_index = PhaseItemExtraIndex::maybe_dynamic_offset(buffer_index.dynamic_offset);8889compare_data90});91}92}9394/// Creates batches for a render phase that uses bins, when GPU batch data95/// building isn't in use.96pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(97gpu_array_buffer: ResMut<BatchedInstanceBuffer<GFBD::BufferData>>,98mut phases: ResMut<ViewBinnedRenderPhases<BPI>>,99param: StaticSystemParam<GFBD::Param>,100) where101BPI: BinnedPhaseItem,102GFBD: GetFullBatchData,103{104let gpu_array_buffer = gpu_array_buffer.into_inner();105let system_param_item = param.into_inner();106107for phase in phases.values_mut() {108// Prepare batchables.109110for bin in phase.batchable_meshes.values_mut() {111let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![];112for main_entity in bin.entities().keys() {113let Some(buffer_data) =114GFBD::get_binned_batch_data(&system_param_item, *main_entity)115else {116continue;117};118let instance = gpu_array_buffer.push(buffer_data);119120// If the dynamic offset has changed, flush the batch.121//122// This is the only time we ever have more than one batch per123// bin. Note that dynamic offsets are only used on platforms124// with no storage buffers.125if !batch_set.last().is_some_and(|batch| {126batch.instance_range.end == instance.index127&& batch.extra_index128== PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset)129}) {130batch_set.push(BinnedRenderPhaseBatch {131representative_entity: (Entity::PLACEHOLDER, *main_entity),132instance_range: instance.index..instance.index,133extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(134instance.dynamic_offset,135),136});137}138139if let Some(batch) = batch_set.last_mut() {140batch.instance_range.end = instance.index + 1;141}142}143144match phase.batch_sets {145BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut batch_sets) => {146batch_sets.push(batch_set);147}148BinnedRenderPhaseBatchSets::Direct(_)149| BinnedRenderPhaseBatchSets::MultidrawIndirect { .. } => {150error!(151"Dynamic uniform batch sets should be used when GPU preprocessing is off"152);153}154}155}156157// Prepare unbatchables.158for unbatchables in phase.unbatchable_meshes.values_mut() {159for main_entity in unbatchables.entities.keys() {160let Some(buffer_data) =161GFBD::get_binned_batch_data(&system_param_item, *main_entity)162else {163continue;164};165let instance = gpu_array_buffer.push(buffer_data);166unbatchables.buffer_indices.add(instance.into());167}168}169}170}171172/// Writes the instance buffer data to the GPU.173pub fn write_batched_instance_buffer<GBD>(174render_device: Res<RenderDevice>,175render_queue: Res<RenderQueue>,176mut cpu_batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,177) where178GBD: GetBatchData,179{180cpu_batched_instance_buffer.write_buffer(&render_device, &render_queue);181}182183184