optimize batch_and_prepare_render_phase (#11323)
# Objective - since #9685 ,bevy introduce automatic batching of draw commands, - `batch_and_prepare_render_phase` take the responsibility for batching `phaseItem`, - `GetBatchData` trait is used for indentify each phaseitem how to batch. it defines a associated type `Data `used for Query to fetch data from world. - however,the impl of `GetBatchData ` in bevy always set ` type Data=Entity` then we acually get following code `let entity:Entity =query.get(item.entity())` that cause unnecessary overhead . ## Solution - remove associated type `Data ` and `Filter` from `GetBatchData `, - change the type of the `query_item ` parameter in get_batch_data from` Self::Data` to `Entity`. - `batch_and_prepare_render_phase ` no longer takes a query using `F::Data, F::Filter` - `get_batch_data `now returns `Option<(Self::BufferData, Option<Self::CompareData>)>` --- ## Performance based in main merged with #11290 Window 11 ,Intel 13400kf, NV 4070Ti  frame time from 3.34ms to 3 ms, ~ 10%  `batch_and_prepare_render_phase` from 800us ~ 400 us ## Migration Guide trait `GetBatchData` no longer hold associated type `Data `and `Filter` `get_batch_data` `query_item `type from `Self::Data` to `Entity` and return `Option<(Self::BufferData, Option<Self::CompareData>)>` `batch_and_prepare_render_phase` should not have a query
This commit is contained in:
		
							parent
							
								
									11e43860d8
								
							
						
					
					
						commit
						04aedf12fa
					
				@ -12,7 +12,7 @@ use bevy_core_pipeline::{
 | 
			
		||||
use bevy_derive::{Deref, DerefMut};
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
    prelude::*,
 | 
			
		||||
    query::{QueryItem, ROQueryItem},
 | 
			
		||||
    query::ROQueryItem,
 | 
			
		||||
    system::{lifetimeless::*, SystemParamItem, SystemState},
 | 
			
		||||
};
 | 
			
		||||
use bevy_math::{Affine3, Rect, UVec2, Vec4};
 | 
			
		||||
@ -476,9 +476,6 @@ impl MeshPipeline {
 | 
			
		||||
 | 
			
		||||
impl GetBatchData for MeshPipeline {
 | 
			
		||||
    type Param = (SRes<RenderMeshInstances>, SRes<RenderLightmaps>);
 | 
			
		||||
    type Data = Entity;
 | 
			
		||||
    type Filter = With<Mesh3d>;
 | 
			
		||||
 | 
			
		||||
    // The material bind group ID, the mesh ID, and the lightmap ID,
 | 
			
		||||
    // respectively.
 | 
			
		||||
    type CompareData = (MaterialBindGroupId, AssetId<Mesh>, Option<AssetId<Image>>);
 | 
			
		||||
@ -487,14 +484,12 @@ impl GetBatchData for MeshPipeline {
 | 
			
		||||
 | 
			
		||||
    fn get_batch_data(
 | 
			
		||||
        (mesh_instances, lightmaps): &SystemParamItem<Self::Param>,
 | 
			
		||||
        entity: &QueryItem<Self::Data>,
 | 
			
		||||
    ) -> (Self::BufferData, Option<Self::CompareData>) {
 | 
			
		||||
        let mesh_instance = mesh_instances
 | 
			
		||||
            .get(entity)
 | 
			
		||||
            .expect("Failed to find render mesh instance");
 | 
			
		||||
        let maybe_lightmap = lightmaps.render_lightmaps.get(entity);
 | 
			
		||||
        entity: Entity,
 | 
			
		||||
    ) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
 | 
			
		||||
        let mesh_instance = mesh_instances.get(&entity)?;
 | 
			
		||||
        let maybe_lightmap = lightmaps.render_lightmaps.get(&entity);
 | 
			
		||||
 | 
			
		||||
        (
 | 
			
		||||
        Some((
 | 
			
		||||
            MeshUniform::new(
 | 
			
		||||
                &mesh_instance.transforms,
 | 
			
		||||
                maybe_lightmap.map(|lightmap| lightmap.uv_rect),
 | 
			
		||||
@ -504,7 +499,7 @@ impl GetBatchData for MeshPipeline {
 | 
			
		||||
                mesh_instance.mesh_asset_id,
 | 
			
		||||
                maybe_lightmap.map(|lightmap| lightmap.image),
 | 
			
		||||
            )),
 | 
			
		||||
        )
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
    component::Component,
 | 
			
		||||
    entity::Entity,
 | 
			
		||||
    prelude::Res,
 | 
			
		||||
    query::{QueryFilter, QueryItem, ReadOnlyQueryData},
 | 
			
		||||
    system::{Query, ResMut, StaticSystemParam, SystemParam, SystemParamItem},
 | 
			
		||||
};
 | 
			
		||||
use bevy_utils::nonmax::NonMaxU32;
 | 
			
		||||
@ -57,8 +57,6 @@ impl<T: PartialEq> BatchMeta<T> {
 | 
			
		||||
/// items.
 | 
			
		||||
pub trait GetBatchData {
 | 
			
		||||
    type Param: SystemParam + 'static;
 | 
			
		||||
    type Data: ReadOnlyQueryData;
 | 
			
		||||
    type Filter: QueryFilter;
 | 
			
		||||
    /// Data used for comparison between phase items. If the pipeline id, draw
 | 
			
		||||
    /// function id, per-instance data buffer dynamic offset and this data
 | 
			
		||||
    /// matches, the draws can be batched.
 | 
			
		||||
@ -72,8 +70,8 @@ pub trait GetBatchData {
 | 
			
		||||
    /// for the `CompareData`.
 | 
			
		||||
    fn get_batch_data(
 | 
			
		||||
        param: &SystemParamItem<Self::Param>,
 | 
			
		||||
        query_item: &QueryItem<Self::Data>,
 | 
			
		||||
    ) -> (Self::BufferData, Option<Self::CompareData>);
 | 
			
		||||
        query_item: Entity,
 | 
			
		||||
    ) -> Option<(Self::BufferData, Option<Self::CompareData>)>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Batch the items in a render phase. This means comparing metadata needed to draw each phase item
 | 
			
		||||
@ -81,16 +79,13 @@ pub trait GetBatchData {
 | 
			
		||||
pub fn batch_and_prepare_render_phase<I: CachedRenderPipelinePhaseItem, F: GetBatchData>(
 | 
			
		||||
    gpu_array_buffer: ResMut<GpuArrayBuffer<F::BufferData>>,
 | 
			
		||||
    mut views: Query<&mut RenderPhase<I>>,
 | 
			
		||||
    query: Query<F::Data, F::Filter>,
 | 
			
		||||
    param: StaticSystemParam<F::Param>,
 | 
			
		||||
) {
 | 
			
		||||
    let gpu_array_buffer = gpu_array_buffer.into_inner();
 | 
			
		||||
    let system_param_item = param.into_inner();
 | 
			
		||||
 | 
			
		||||
    let mut process_item = |item: &mut I| {
 | 
			
		||||
        let batch_query_item = query.get(item.entity()).ok()?;
 | 
			
		||||
 | 
			
		||||
        let (buffer_data, compare_data) = F::get_batch_data(&system_param_item, &batch_query_item);
 | 
			
		||||
        let (buffer_data, compare_data) = F::get_batch_data(&system_param_item, item.entity())?;
 | 
			
		||||
        let buffer_index = gpu_array_buffer.push(buffer_data);
 | 
			
		||||
 | 
			
		||||
        let index = buffer_index.index.get();
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ use bevy_core_pipeline::core_2d::Transparent2d;
 | 
			
		||||
use bevy_derive::{Deref, DerefMut};
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
    prelude::*,
 | 
			
		||||
    query::{QueryItem, ROQueryItem},
 | 
			
		||||
    query::ROQueryItem,
 | 
			
		||||
    system::{lifetimeless::*, SystemParamItem, SystemState},
 | 
			
		||||
};
 | 
			
		||||
use bevy_math::{Affine3, Vec4};
 | 
			
		||||
@ -339,25 +339,21 @@ impl Mesh2dPipeline {
 | 
			
		||||
 | 
			
		||||
impl GetBatchData for Mesh2dPipeline {
 | 
			
		||||
    type Param = SRes<RenderMesh2dInstances>;
 | 
			
		||||
    type Data = Entity;
 | 
			
		||||
    type Filter = With<Mesh2d>;
 | 
			
		||||
    type CompareData = (Material2dBindGroupId, AssetId<Mesh>);
 | 
			
		||||
    type BufferData = Mesh2dUniform;
 | 
			
		||||
 | 
			
		||||
    fn get_batch_data(
 | 
			
		||||
        mesh_instances: &SystemParamItem<Self::Param>,
 | 
			
		||||
        entity: &QueryItem<Self::Data>,
 | 
			
		||||
    ) -> (Self::BufferData, Option<Self::CompareData>) {
 | 
			
		||||
        let mesh_instance = mesh_instances
 | 
			
		||||
            .get(entity)
 | 
			
		||||
            .expect("Failed to find render mesh2d instance");
 | 
			
		||||
        (
 | 
			
		||||
        entity: Entity,
 | 
			
		||||
    ) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
 | 
			
		||||
        let mesh_instance = mesh_instances.get(&entity)?;
 | 
			
		||||
        Some((
 | 
			
		||||
            (&mesh_instance.transforms).into(),
 | 
			
		||||
            mesh_instance.automatic_batching.then_some((
 | 
			
		||||
                mesh_instance.material_bind_group_id,
 | 
			
		||||
                mesh_instance.mesh_asset_id,
 | 
			
		||||
            )),
 | 
			
		||||
        )
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user