From 50f8134ca0c5b0047e3a7e8a94e81e4ae538e766 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 2 May 2020 10:27:22 -0700 Subject: [PATCH] rewrite batcher to be fully generic --- crates/bevy_asset/src/lib.rs | 6 + crates/bevy_pbr/src/lib.rs | 5 - crates/bevy_render/Cargo.toml | 1 + crates/bevy_render/src/batch/asset_batcher.rs | 269 ------------------ .../bevy_render/src/batch/asset_batcher2.rs | 144 ---------- crates/bevy_render/src/batch/batch.rs | 58 ++-- crates/bevy_render/src/batch/batcher.rs | 262 +++++++++++++++++ crates/bevy_render/src/batch/batcher_set.rs | 0 crates/bevy_render/src/batch/mod.rs | 10 +- .../assigned_batches_draw_target.rs | 120 -------- .../src/draw_target/draw_targets/mod.rs | 2 - crates/bevy_render/src/lib.rs | 3 - crates/bevy_render/src/mesh.rs | 30 +- crates/bevy_render/src/shader/uniform.rs | 22 -- src/prelude.rs | 1 - 15 files changed, 304 insertions(+), 629 deletions(-) delete mode 100644 crates/bevy_render/src/batch/asset_batcher.rs delete mode 100644 crates/bevy_render/src/batch/asset_batcher2.rs create mode 100644 crates/bevy_render/src/batch/batcher.rs create mode 100644 crates/bevy_render/src/batch/batcher_set.rs delete mode 100644 crates/bevy_render/src/draw_target/draw_targets/assigned_batches_draw_target.rs diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 985c9af567..12f1325439 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -81,6 +81,12 @@ pub struct HandleUntyped { pub type_id: TypeId, } +impl HandleUntyped { + pub fn is_handle(untyped: &HandleUntyped) -> bool { + TypeId::of::() == untyped.type_id + } +} + impl From> for HandleUntyped where T: 'static, diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index b9088703c9..e3e6e214a0 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -20,15 +20,10 @@ pub struct PbrPlugin; // NOTE: this isn't PBR yet. consider this name "aspirational" :) impl AppPlugin for PbrPlugin { fn build(&self, app: &mut AppBuilder) { - // asset_batchers.batch_types2::(); app.add_resource(AssetStorage::::new()) .add_system_to_stage( stage::POST_UPDATE, shader::asset_handle_shader_def_system::.system(), - ) - .add_system_to_stage( - stage::POST_UPDATE, - shader::asset_handle_batcher_system::(), ); let resources = app.resources(); let mut render_graph = resources.get_mut::().unwrap(); diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index bacf66b31c..674b07b76d 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -27,6 +27,7 @@ uuid = { version = "0.8", features = ["v4", "serde"] } glam = "0.8.6" zerocopy = "0.3" bitflags = "1.0" +smallvec = "1.4.0" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788 once_cell = "1.3.1" downcast-rs = "1.1.1" diff --git a/crates/bevy_render/src/batch/asset_batcher.rs b/crates/bevy_render/src/batch/asset_batcher.rs deleted file mode 100644 index 1ab15ebed0..0000000000 --- a/crates/bevy_render/src/batch/asset_batcher.rs +++ /dev/null @@ -1,269 +0,0 @@ -use super::{AssetSetBatcher2, AssetSetBatcherKey2, Batch, BatchKey2}; -use bevy_asset::{Handle, HandleId}; -use legion::prelude::Entity; -use std::{any::TypeId, collections::HashMap}; - -pub trait AssetBatcher { - fn set_entity_handle(&mut self, entity: Entity, handle_type: TypeId, handle_id: HandleId); - fn get_batch2(&self, key: &BatchKey2) -> Option<&Batch>; - // TODO: add pipeline handle here - fn get_batches2(&self) -> std::collections::hash_map::Iter<'_, BatchKey2, Batch>; - fn get_batches<'a>(&'a self) -> Box + 'a>; - fn get_batches_mut<'a>(&'a mut self) -> Box + 'a>; -} - -#[derive(Default)] -pub struct AssetBatchers { - asset_batchers: Vec>, - asset_batcher_indices2: HashMap, - handle_batchers: HashMap>, -} - -impl AssetBatchers { - pub fn set_entity_handle(&mut self, entity: Entity, handle: Handle) - where - T: 'static, - { - let handle_type = TypeId::of::(); - if let Some(batcher_indices) = self.handle_batchers.get(&handle_type) { - for index in batcher_indices.iter() { - self.asset_batchers[*index].set_entity_handle(entity, handle_type, handle.id); - } - } - } - - pub fn batch_types2(&mut self) - where - T1: 'static, - T2: 'static, - { - let key = AssetSetBatcherKey2 { - handle1_type: TypeId::of::(), - handle2_type: TypeId::of::(), - }; - - self.asset_batchers - .push(Box::new(AssetSetBatcher2::new(key.clone()))); - - let index = self.asset_batchers.len() - 1; - - let handle1_batchers = self - .handle_batchers - .entry(key.handle1_type.clone()) - .or_insert_with(|| Vec::new()); - handle1_batchers.push(index); - - let handle2_batchers = self - .handle_batchers - .entry(key.handle2_type.clone()) - .or_insert_with(|| Vec::new()); - handle2_batchers.push(index); - - self.asset_batcher_indices2.insert(key, index); - } - - pub fn get_batches2( - &self, - ) -> Option> - where - T1: 'static, - T2: 'static, - { - let key = AssetSetBatcherKey2 { - handle1_type: TypeId::of::(), - handle2_type: TypeId::of::(), - }; - - if let Some(index) = self.asset_batcher_indices2.get(&key) { - Some(self.asset_batchers[*index].get_batches2()) - } else { - None - } - } - - pub fn get_batch2(&self, handle1: Handle, handle2: Handle) -> Option<&Batch> - where - T1: 'static, - T2: 'static, - { - let key = AssetSetBatcherKey2 { - handle1_type: TypeId::of::(), - handle2_type: TypeId::of::(), - }; - - let batch_key = BatchKey2 { - handle1: handle1.id, - handle2: handle2.id, - }; - - if let Some(index) = self.asset_batcher_indices2.get(&key) { - self.asset_batchers[*index].get_batch2(&batch_key) - } else { - None - } - } - - pub fn get_batches(&self) -> impl Iterator { - self.asset_batchers - .iter() - .map(|a| a.get_batches()) - .flatten() - } - - pub fn get_batches_mut(&mut self) -> impl Iterator { - self.asset_batchers - .iter_mut() - .map(|a| a.get_batches_mut()) - .flatten() - } - - pub fn get_handle_batches(&self) -> Option> - where - T: 'static, - { - let handle_type = TypeId::of::(); - if let Some(batcher_indices) = self.handle_batchers.get(&handle_type) { - Some( - // NOTE: it would be great to use batcher_indices.iter().map(|i| self.asset_batchers[*i].get_batches()) here - // but unfortunately the lifetimes don't work out for some reason - self.asset_batchers - .iter() - .enumerate() - .filter(move |(index, _a)| batcher_indices.contains(index)) - .map(|(_index, a)| a.get_batches()) - .flatten(), - ) - } else { - None - } - } - - pub fn get_handle_batches_mut(&mut self) -> Option> - where - T: 'static, - { - let handle_type = TypeId::of::(); - if let Some(batcher_indices) = self.handle_batchers.get(&handle_type) { - Some( - self.asset_batchers - .iter_mut() - .enumerate() - .filter(move |(index, _a)| batcher_indices.contains(index)) - .map(|(_index, a)| a.get_batches_mut()) - .flatten(), - ) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use legion::prelude::*; - struct A; - struct B; - struct C; - - #[test] - fn test_batching() { - let mut asset_batchers = AssetBatchers::default(); - asset_batchers.batch_types2::(); - - let mut world = World::new(); - let a1: Handle = Handle::new(1); - let b1: Handle = Handle::new(1); - let c1: Handle = Handle::new(1); - - let a2: Handle = Handle::new(2); - let b2: Handle = Handle::new(2); - - let entities = world.insert((), (0..3).map(|_| ())); - asset_batchers.set_entity_handle(entities[0], a1); - // batch is empty when Handle is missing - assert_eq!(asset_batchers.get_batch2(a1, b1), None); - asset_batchers.set_entity_handle(entities[0], b1); - // entity[0] is added to batch when it has both Handle and Handle - let mut expected_batch = Batch { - handles: vec![a1.into(), b1.into()], - ..Default::default() - }; - expected_batch.add_entity(entities[0]); - let actual_batch = asset_batchers.get_batch2(a1, b1).unwrap(); - copy_ignored_fields(actual_batch, &mut expected_batch); - assert_eq!(actual_batch, &expected_batch); - asset_batchers.set_entity_handle(entities[0], c1); - - asset_batchers.set_entity_handle(entities[1], a1); - asset_batchers.set_entity_handle(entities[1], b1); - - // all entities with Handle and Handle are returned - let mut expected_batch = Batch { - handles: vec![a1.into(), b1.into()], - ..Default::default() - }; - expected_batch.add_entity(entities[0]); - expected_batch.add_entity(entities[1]); - let actual_batch = asset_batchers.get_batch2(a1, b1).unwrap(); - copy_ignored_fields(actual_batch, &mut expected_batch); - assert_eq!(actual_batch, &expected_batch); - - // uncreated batches are empty - assert_eq!(asset_batchers.get_batch2(a1, c1), None); - - // batch iteration works - asset_batchers.set_entity_handle(entities[2], a2); - asset_batchers.set_entity_handle(entities[2], b2); - - let mut batches = asset_batchers - .get_batches2::() - .unwrap() - .collect::>(); - - batches.sort_by(|a, b| a.0.cmp(b.0)); - let mut expected_batch1 = Batch { - handles: vec![a1.into(), b1.into()], - ..Default::default() - }; - expected_batch1.add_entity(entities[0]); - expected_batch1.add_entity(entities[1]); - let mut expected_batch2 = Batch { - handles: vec![a2.into(), b2.into()], - ..Default::default() - }; - expected_batch2.add_entity(entities[2]); - let mut expected_batches = vec![ - ( - BatchKey2 { - handle1: a1.id, - handle2: b1.id, - }, - expected_batch1, - ), - ( - BatchKey2 { - handle1: a2.id, - handle2: b2.id, - }, - expected_batch2, - ), - ]; - expected_batches.sort_by(|a, b| a.0.cmp(&b.0)); - // copy ignored fields - batches.iter().zip(expected_batches.iter_mut()).for_each( - |((_, ref actual), (_, ref mut expected))| copy_ignored_fields(actual, expected), - ); - assert_eq!( - batches, - expected_batches - .iter() - .map(|(a, b)| (a, b)) - .collect::>() - ); - } - - fn copy_ignored_fields(source: &Batch, destination: &mut Batch) { - destination.render_resource_assignments.id = source.render_resource_assignments.id; - } -} diff --git a/crates/bevy_render/src/batch/asset_batcher2.rs b/crates/bevy_render/src/batch/asset_batcher2.rs deleted file mode 100644 index d6960cba29..0000000000 --- a/crates/bevy_render/src/batch/asset_batcher2.rs +++ /dev/null @@ -1,144 +0,0 @@ -use super::{AssetBatcher, Batch}; -use bevy_asset::{HandleId, HandleUntyped}; -use legion::prelude::Entity; -use std::{any::TypeId, collections::HashMap, hash::Hash}; - -// TODO: if/when const generics land, revisit this design in favor of generic array lengths - -// TODO: add sorting by primary / secondary handle to reduce rebinds of data - -#[derive(Hash, Eq, PartialEq, Debug, Ord, PartialOrd)] -pub struct BatchKey2 { - pub handle1: HandleId, - pub handle2: HandleId, -} - -#[derive(Hash, Eq, PartialEq, Clone, Debug)] -pub struct AssetSetBatcherKey2 { - pub handle1_type: TypeId, - pub handle2_type: TypeId, -} - -struct EntitySetState2 { - handle1: Option, - handle2: Option, -} - -impl EntitySetState2 { - fn is_full(&self) -> bool { - self.handle1.is_some() && self.handle2.is_some() - } -} - -pub struct AssetSetBatcher2 { - key: AssetSetBatcherKey2, - set_batches: HashMap, - entity_set_states: HashMap, -} - -impl AssetSetBatcher2 { - pub fn new(key: AssetSetBatcherKey2) -> Self { - AssetSetBatcher2 { - key, - set_batches: HashMap::new(), - entity_set_states: HashMap::new(), - } - } - - pub fn add_entity_to_set(&mut self, entity: Entity) { - // these unwraps are safe because this function is only called from set_entity_handle on a "full" state - let state = self.entity_set_states.get(&entity).unwrap(); - let key = BatchKey2 { - handle1: state.handle1.unwrap(), - handle2: state.handle2.unwrap(), - }; - - match self.set_batches.get_mut(&key) { - Some(batch) => { - batch.add_entity(entity); - } - None => { - let mut batch = Batch::default(); - - batch.handles.push(HandleUntyped { - id: key.handle1, - type_id: self.key.handle1_type, - }); - batch.handles.push(HandleUntyped { - id: key.handle2, - type_id: self.key.handle2_type, - }); - - batch.add_entity(entity); - self.set_batches.insert(key, batch); - } - } - } - - pub fn set_entity_handle1(&mut self, entity: Entity, handle_id: HandleId) { - match self.entity_set_states.get_mut(&entity) { - None => { - // TODO: when generalizing to set size 1, ensure you treat set as "full" here - self.entity_set_states.insert( - entity, - EntitySetState2 { - handle1: Some(handle_id), - handle2: None, - }, - ); - } - Some(state) => { - state.handle1 = Some(handle_id); - if state.is_full() { - self.add_entity_to_set(entity); - } - } - } - } - - pub fn set_entity_handle2(&mut self, entity: Entity, handle_id: HandleId) { - match self.entity_set_states.get_mut(&entity) { - None => { - // TODO: when generalizing to set size 1, ensure you treat set as "full" here - self.entity_set_states.insert( - entity, - EntitySetState2 { - handle1: None, - handle2: Some(handle_id), - }, - ); - } - Some(state) => { - state.handle2 = Some(handle_id); - if state.is_full() { - self.add_entity_to_set(entity); - } - } - } - } -} - -impl AssetBatcher for AssetSetBatcher2 { - fn set_entity_handle(&mut self, entity: Entity, handle_type: TypeId, handle_id: HandleId) { - if handle_type == self.key.handle1_type { - self.set_entity_handle1(entity, handle_id); - } else if handle_type == self.key.handle2_type { - self.set_entity_handle2(entity, handle_id); - } - } - fn get_batch2(&self, key: &BatchKey2) -> Option<&Batch> { - self.set_batches.get(key) - } - - fn get_batches2(&self) -> std::collections::hash_map::Iter<'_, BatchKey2, Batch> { - self.set_batches.iter() - } - - fn get_batches<'a>(&'a self) -> Box + 'a> { - Box::new(self.set_batches.values()) - } - - fn get_batches_mut<'a>(&'a mut self) -> Box + 'a> { - Box::new(self.set_batches.values_mut()) - } -} diff --git a/crates/bevy_render/src/batch/batch.rs b/crates/bevy_render/src/batch/batch.rs index 4681a15f2f..80f1862092 100644 --- a/crates/bevy_render/src/batch/batch.rs +++ b/crates/bevy_render/src/batch/batch.rs @@ -1,38 +1,36 @@ -use crate::render_resource::RenderResourceAssignments; -use bevy_asset::{Handle, HandleUntyped}; -use legion::prelude::Entity; -use std::collections::{HashMap, HashSet}; +use super::{BatchKey, Key}; -#[derive(PartialEq, Eq, Debug, Default)] -pub struct Batch { - pub handles: Vec, - pub entities: HashSet, - pub instanced_entity_indices: HashMap, - pub current_instanced_entity_index: usize, - pub render_resource_assignments: RenderResourceAssignments, +#[derive(Debug, Eq, PartialEq)] +pub struct Batch +where + TKey: Key, +{ + pub batch_key: BatchKey, + pub values: Vec, + pub data: TData, } -impl Batch { - pub fn add_entity(&mut self, entity: Entity) { - self.entities.insert(entity); - } - - pub fn add_instanced_entity(&mut self, entity: Entity) { - if let None = self.instanced_entity_indices.get(&entity) { - self.instanced_entity_indices - .insert(entity, self.current_instanced_entity_index); - self.current_instanced_entity_index += 1; +impl Batch +where + TKey: Key, +{ + pub fn new(batch_key: BatchKey, data: TData) -> Self { + Batch { + data, + values: Vec::new(), + batch_key, } } - pub fn get_handle(&self) -> Option> - where - T: 'static, - { - self.handles - .iter() - .map(|h| Handle::from_untyped(*h)) - .find(|h| h.is_some()) - .map(|h| h.unwrap()) + pub fn add(&mut self, value: TValue) { + self.values.push(value); + } + + pub fn iter(&self) -> impl Iterator { + self.values.iter() + } + + pub fn get_key(&self, index: usize) -> Option<&TKey> { + self.batch_key.0.get(index) } } diff --git a/crates/bevy_render/src/batch/batcher.rs b/crates/bevy_render/src/batch/batcher.rs new file mode 100644 index 0000000000..d435982829 --- /dev/null +++ b/crates/bevy_render/src/batch/batcher.rs @@ -0,0 +1,262 @@ +use super::Batch; +use smallvec::{smallvec, SmallVec}; +use std::{borrow::Cow, collections::HashMap, hash::Hash}; + +// TODO: add sorting by primary / secondary handle to reduce rebinds of data + +// TValue: entityid +// TKey: handleuntyped + +pub trait Key: Clone + Eq + Hash + 'static {} +impl Key for T {} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct BatchKey(pub Cow<'static, SmallVec<[TKey; 2]>>); + +impl BatchKey { + pub fn key1(key: TKey) -> Self { + BatchKey(Cow::Owned(smallvec![key])) + } + + pub fn key2(key1: TKey, key2: TKey) -> Self { + BatchKey(Cow::Owned(smallvec![key1, key2])) + } + + pub fn key3(key1: TKey, key2: TKey, key3: TKey) -> Self { + BatchKey(Cow::Owned(smallvec![key1, key2, key3])) + } +} + +pub struct BatcherKeyState { + batch_key: Option>, + keys: SmallVec<[Option; 2]>, +} + +impl BatcherKeyState { + pub fn new(size: usize) -> Self { + BatcherKeyState { + keys: smallvec![None; size], + batch_key: None, + } + } + + pub fn set(&mut self, index: usize, key: TKey) { + self.keys[index] = Some(key); + } + + pub fn finish(&mut self) -> Option> { + let finished = self.keys.iter().filter(|x| x.is_some()).count() == self.keys.len(); + if finished { + let batch_key = BatchKey(Cow::Owned( + self.keys + .drain(..) + .map(|k| k.unwrap()) + .collect::>(), + )); + self.batch_key = Some(batch_key); + self.batch_key.clone() + } else { + None + } + } +} + +/// An unordered batcher intended to support an arbitrary number of keys of the same type (but with some distinguishing factor) +/// NOTE: this may or may not be useful for anything. when paired with a higher-level "BatcherSet" it would allow updating batches +// per-key (ex: material, mesh) with no global knowledge of the number of batch types (ex: (Mesh), (Material, Mesh)) that key belongs +// to. The downside is that it is completely unordered, so it probably isn't useful for front->back or back->front rendering. But +// _maybe_ for gpu instancing? +pub struct Batcher +where + TKey: Key, +{ + pub batches: HashMap, Batch>, + pub is_index: Vec bool>, + pub key_states: HashMap>, + pub key_count: usize, +} + +impl Batcher +where + TKey: Key, + TValue: Clone + Eq + Hash, + TData: Default, +{ + pub fn new(is_index: Vec bool>) -> Self { + Batcher { + batches: HashMap::default(), + key_states: HashMap::default(), + key_count: is_index.len(), + is_index, + } + } + + pub fn get_batch(&self, batch_key: &BatchKey) -> Option<&Batch> { + self.batches.get(batch_key) + } + + pub fn get_batch_mut( + &mut self, + batch_key: &BatchKey, + ) -> Option<&mut Batch> { + self.batches.get_mut(batch_key) + } + + pub fn add(&mut self, key: TKey, value: TValue) -> bool { + let batch_key = { + let key_count = self.key_count; + let key_state = self + .key_states + .entry(value.clone()) + .or_insert_with(|| BatcherKeyState::new(key_count)); + + // if all key states are set, the value is already in the batch + if key_state.batch_key.is_some() { + // TODO: if weights are ever added, make sure to get the batch and set the weight here + return true; + } + + let key_index = self + .is_index + .iter() + .enumerate() + .find(|(_i, is_index)| is_index(&key)) + .map(|(i, _)| i); + if let Some(key_index) = key_index { + key_state.set(key_index, key.clone()); + key_state.finish() + } else { + return false; + } + }; + + if let Some(batch_key) = batch_key { + let batch = self + .batches + .entry(batch_key.clone()) + .or_insert_with(|| Batch::new(batch_key, TData::default())); + + batch.add(value); + } + + return true; + } + + pub fn iter(&self) -> impl Iterator> { + self.batches.values() + } + + pub fn iter_mut(&mut self) -> impl Iterator> { + self.batches.values_mut() + } +} + +#[cfg(test)] +mod tests { + use super::{Batch, BatchKey, Batcher}; + use bevy_asset::{Handle, HandleUntyped}; + + #[derive(Debug, Eq, PartialEq)] + struct A; + #[derive(Debug, Eq, PartialEq)] + struct B; + #[derive(Debug, Eq, PartialEq)] + struct C; + #[derive(Debug, Eq, PartialEq, Default)] + struct Data; + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + struct Entity(usize); + #[test] + fn test_batcher_2() { + let mut batcher: Batcher = Batcher::new(vec![ + HandleUntyped::is_handle::, + HandleUntyped::is_handle::, + ]); + + let e1 = Entity(1); + let e2 = Entity(2); + let e3 = Entity(3); + + let a1: HandleUntyped = Handle::::new(1).into(); + let b1: HandleUntyped = Handle::::new(1).into(); + let c1: HandleUntyped = Handle::::new(1).into(); + + let a2: HandleUntyped = Handle::::new(2).into(); + let b2: HandleUntyped = Handle::::new(2).into(); + + let a1_b1 = BatchKey::key2(a1, b1); + let a2_b2 = BatchKey::key2(a2, b2); + + assert_eq!( + batcher.get_batch(&a1_b1), + None, + "a1_b1 batch should not exist yet" + ); + batcher.add(a1, e1); + assert_eq!( + batcher.get_batch(&a1_b1), + None, + "a1_b1 batch should not exist yet" + ); + batcher.add(b1, e1); + + let a1_b1_batch = Batch { + batch_key: a1_b1.clone(), + values: vec![e1], + data: Data, + }; + + assert_eq!( + batcher.get_batch(&a1_b1), + Some(&a1_b1_batch), + "a1_b1 batch should exist" + ); + + assert_eq!( + batcher.get_batch(&a2_b2), + None, + "a2_b2 batch should not exist yet" + ); + batcher.add(a2, e2); + assert_eq!( + batcher.get_batch(&a2_b2), + None, + "a2_b2 batch should not exist yet" + ); + batcher.add(b2, e2); + + let expected_batch = Batch { + batch_key: a2_b2.clone(), + values: vec![e2], + data: Data, + }; + + assert_eq!( + batcher.get_batch(&a2_b2), + Some(&expected_batch), + "a2_b2 batch should have e2" + ); + + batcher.add(a2, e3); + batcher.add(b2, e3); + batcher.add(c1, e3); // this should be ignored + let a2_b2_batch = Batch { + batch_key: a2_b2.clone(), + values: vec![e2, e3], + data: Data, + }; + + assert_eq!( + batcher.get_batch(&a2_b2), + Some(&a2_b2_batch), + "a2_b2 batch should have e2 and e3" + ); + + assert_eq!( + batcher + .iter() + .collect::>>(), + vec![&a1_b1_batch, &a2_b2_batch] + ) + } +} diff --git a/crates/bevy_render/src/batch/batcher_set.rs b/crates/bevy_render/src/batch/batcher_set.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/bevy_render/src/batch/mod.rs b/crates/bevy_render/src/batch/mod.rs index 90982a894c..c7bbb36c8f 100644 --- a/crates/bevy_render/src/batch/mod.rs +++ b/crates/bevy_render/src/batch/mod.rs @@ -1,7 +1,9 @@ -mod asset_batcher; -mod asset_batcher2; +// mod asset_batcher; +// mod asset_batcher2; mod batch; +mod batcher; -pub use asset_batcher::*; -pub use asset_batcher2::*; +// pub use asset_batcher::*; +// pub use asset_batcher2::*; pub use batch::*; +pub use batcher::*; \ No newline at end of file diff --git a/crates/bevy_render/src/draw_target/draw_targets/assigned_batches_draw_target.rs b/crates/bevy_render/src/draw_target/draw_targets/assigned_batches_draw_target.rs deleted file mode 100644 index 5c03368778..0000000000 --- a/crates/bevy_render/src/draw_target/draw_targets/assigned_batches_draw_target.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::{ - batch::AssetBatchers, - draw_target::DrawTarget, - pass::RenderPass, - pipeline::PipelineDescriptor, - render_resource::{resource_name, RenderResourceAssignments}, - renderer::RenderContext, - Renderable, -}; -use bevy_asset::Handle; -use legion::prelude::*; - -#[derive(Default)] -pub struct AssignedBatchesDrawTarget; - -impl DrawTarget for AssignedBatchesDrawTarget { - fn draw( - &self, - world: &World, - resources: &Resources, - render_pass: &mut dyn RenderPass, - pipeline_handle: Handle, - pipeline_descriptor: &PipelineDescriptor, - ) { - log::trace!("drawing batches for pipeline {:?}", pipeline_handle); - let asset_batches = resources.get::().unwrap(); - let global_render_resource_assignments = - resources.get::().unwrap(); - render_pass.set_render_resources(pipeline_descriptor, &global_render_resource_assignments); - for batch in asset_batches.get_batches() { - let indices = render_pass - .set_render_resources(pipeline_descriptor, &batch.render_resource_assignments); - log::trace!("drawing batch {:?}", batch.render_resource_assignments.id); - log::trace!("{:#?}", batch); - for batched_entity in batch.entities.iter() { - let renderable = world.get_component::(*batched_entity).unwrap(); - if !renderable.is_visible { - continue; - } - - log::trace!("start drawing batched entity: {:?}", batched_entity); - log::trace!("{:#?}", renderable.render_resource_assignments); - let entity_indices = render_pass.set_render_resources( - pipeline_descriptor, - &renderable.render_resource_assignments, - ); - let mut draw_indices = &indices; - if entity_indices.is_some() { - if indices.is_some() { - // panic!("entities overriding their batch's vertex buffer is not currently supported"); - log::trace!("using batch vertex indices"); - draw_indices = &entity_indices; - } else { - log::trace!("using entity vertex indices"); - draw_indices = &entity_indices; - } - } - - if draw_indices.is_none() { - continue; - } - - render_pass.draw_indexed(draw_indices.as_ref().unwrap().clone(), 0, 0..1); - log::trace!("finish drawing batched entity: {:?}", batched_entity); - } - } - } - - fn setup( - &mut self, - world: &World, - resources: &Resources, - render_context: &mut dyn RenderContext, - pipeline_handle: Handle, - pipeline_descriptor: &PipelineDescriptor, - ) { - let asset_batches = resources.get::().unwrap(); - - let global_render_resource_assignments = - resources.get_mut::().unwrap(); - - log::trace!( - "setting up batch bind groups for pipeline: {:?} {:?}", - pipeline_handle, - pipeline_descriptor.name, - ); - log::trace!("setting up global bind groups"); - render_context.setup_bind_groups(pipeline_descriptor, &global_render_resource_assignments); - - for batch in asset_batches.get_batches() { - log::trace!( - "setting up batch bind groups: {:?}", - batch.render_resource_assignments.id - ); - log::trace!("{:#?}", batch); - render_context - .setup_bind_groups(pipeline_descriptor, &batch.render_resource_assignments); - for batched_entity in batch.entities.iter() { - let renderable = world.get_component::(*batched_entity).unwrap(); - if !renderable.is_visible || renderable.is_instanced { - continue; - } - - log::trace!( - "setting up entity bind group {:?} for batch {:?}", - batched_entity, - batch.render_resource_assignments.id - ); - render_context.setup_bind_groups( - pipeline_descriptor, - &renderable.render_resource_assignments, - ); - } - } - } - - fn get_name(&self) -> String { - resource_name::draw_target::ASSIGNED_BATCHES.to_string() - } -} diff --git a/crates/bevy_render/src/draw_target/draw_targets/mod.rs b/crates/bevy_render/src/draw_target/draw_targets/mod.rs index 561eefd609..d29d08b1dd 100644 --- a/crates/bevy_render/src/draw_target/draw_targets/mod.rs +++ b/crates/bevy_render/src/draw_target/draw_targets/mod.rs @@ -1,9 +1,7 @@ -mod assigned_batches_draw_target; mod assigned_meshes_draw_target; mod meshes_draw_target; mod ui_draw_target; -pub use assigned_batches_draw_target::*; pub use assigned_meshes_draw_target::*; pub use meshes_draw_target::*; pub use ui_draw_target::*; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7f30522365..84bade5cbe 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -38,7 +38,6 @@ use self::{ texture::Texture, }; -use batch::AssetBatchers; use bevy_app::{stage, AppBuilder, AppPlugin}; use bevy_asset::AssetStorage; use mesh::mesh_resource_provider_system; @@ -67,12 +66,10 @@ impl AppPlugin for RenderPlugin { .add_resource(RenderResourceAssignments::default()) .add_resource(VertexBufferDescriptors::default()) .add_resource(EntityRenderResourceAssignments::default()) - .add_resource(AssetBatchers::default()) // core systems .add_system(entity_render_resource_assignments_system()) .init_system_to_stage(stage::POST_UPDATE, camera::camera_update_system) .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_specializer_system()) - .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system()) // render resource provider systems .init_system_to_stage(RENDER_RESOURCE_STAGE, mesh_resource_provider_system); } diff --git a/crates/bevy_render/src/mesh.rs b/crates/bevy_render/src/mesh.rs index 1a25fa4930..27d8cca76a 100644 --- a/crates/bevy_render/src/mesh.rs +++ b/crates/bevy_render/src/mesh.rs @@ -1,5 +1,4 @@ use crate::{ - batch::AssetBatchers, pipeline::{ state_descriptors::{IndexFormat, PrimitiveTopology}, VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat, @@ -311,19 +310,6 @@ pub mod shape { } } -pub fn mesh_batcher_system() -> Box { - SystemBuilder::new("mesh_batcher") - .write_resource::() - .with_query( - <(Read>, Read)>::query().filter(changed::>()), - ) - .build(|_, world, asset_batchers, query| { - for (entity, (mesh_handle, _renderable)) in query.iter_entities(world) { - asset_batchers.set_entity_handle(entity, *mesh_handle); - } - }) -} - pub fn mesh_specializer_system() -> Box { SystemBuilder::new("mesh_specializer") .read_resource::>() @@ -396,24 +382,10 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box() .read_resource::>() - .write_resource::() .with_query(<(Read>, Write)>::query()) .build( - move |_, world, (render_resource_context, meshes, asset_batchers), query| { + move |_, world, (render_resource_context, meshes,/* asset_batchers*/), query| { let render_resources = &*render_resource_context.context; - if let Some(batches) = asset_batchers.get_handle_batches_mut::() { - for batch in batches { - let handle = batch.get_handle::().unwrap(); - setup_mesh_resource( - render_resources, - &mut batch.render_resource_assignments, - &vertex_buffer_descriptor, - handle, - &meshes, - ); - } - } - // TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target for (handle, mut renderable) in query.iter_mut(world) { setup_mesh_resource( diff --git a/crates/bevy_render/src/shader/uniform.rs b/crates/bevy_render/src/shader/uniform.rs index 3d14eaad82..75c443038f 100644 --- a/crates/bevy_render/src/shader/uniform.rs +++ b/crates/bevy_render/src/shader/uniform.rs @@ -1,5 +1,4 @@ use crate::{ - batch::AssetBatchers, color::ColorSource, pipeline::{BindType, VertexBufferDescriptor}, texture::Texture, @@ -56,27 +55,6 @@ pub fn asset_handle_shader_def_system( } } -pub fn asset_handle_batcher_system() -> Box -where - T: AsUniforms + Send + Sync + 'static, -{ - SystemBuilder::new(format!( - "asset_handle_batcher::{}", - std::any::type_name::() - )) - .write_resource::() - .with_query(<(Read>, Read)>::query()) - .build(|_, world, asset_batchers, query| { - for (entity, (uniform_handle, renderable)) in query.iter_entities(world) { - if !renderable.is_visible || renderable.is_instanced { - continue; - } - - asset_batchers.set_entity_handle(entity, *uniform_handle); - } - }) -} - pub trait ShaderDefSuffixProvider { fn get_shader_def(&self) -> Option<&'static str>; } diff --git a/src/prelude.rs b/src/prelude.rs index 661f47a0cf..004a2ea2ca 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,7 +13,6 @@ pub use crate::diagnostic::DiagnosticsPlugin; pub use crate::pbr::{entity::*, light::Light, material::StandardMaterial}; #[cfg(feature = "render")] pub use crate::render::{ - batch::AssetBatchers, draw_target, entity::*, mesh::{shape, Mesh},