mod draw;
mod draw_state;
pub use draw::*;
pub use draw_state::*;
use bevy_ecs::prelude::{Component, Query};
#[derive(Component)]
pub struct RenderPhase<I: PhaseItem> {
pub items: Vec<I>,
}
impl<I: PhaseItem> Default for RenderPhase<I> {
fn default() -> Self {
Self { items: Vec::new() }
}
}
impl<I: PhaseItem> RenderPhase<I> {
#[inline]
pub fn add(&mut self, item: I) {
self.items.push(item);
}
pub fn sort(&mut self) {
I::sort(&mut self.items);
}
}
impl<I: BatchedPhaseItem> RenderPhase<I> {
pub fn batch(&mut self) {
let mut items = std::mem::take(&mut self.items).into_iter();
self.items.reserve(items.len());
if let Some(mut current_batch) = items.next() {
for next_item in items {
if matches!(
current_batch.add_to_batch(&next_item),
BatchResult::IncompatibleItems
) {
self.items.push(current_batch);
current_batch = next_item;
}
}
self.items.push(current_batch);
}
}
}
pub fn sort_phase_system<I: PhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
for mut phase in &mut render_phases {
phase.sort();
}
}
pub fn batch_phase_system<I: BatchedPhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
for mut phase in &mut render_phases {
phase.batch();
}
}
#[cfg(test)]
mod tests {
use std::ops::Range;
use bevy_ecs::entity::Entity;
use super::*;
#[test]
fn batching() {
#[derive(Debug, PartialEq)]
struct TestPhaseItem {
entity: Entity,
batch_range: Option<Range<u32>>,
}
impl PhaseItem for TestPhaseItem {
type SortKey = ();
fn sort_key(&self) -> Self::SortKey {}
fn draw_function(&self) -> DrawFunctionId {
unimplemented!();
}
}
impl EntityPhaseItem for TestPhaseItem {
fn entity(&self) -> bevy_ecs::entity::Entity {
self.entity
}
}
impl BatchedPhaseItem for TestPhaseItem {
fn batch_range(&self) -> &Option<std::ops::Range<u32>> {
&self.batch_range
}
fn batch_range_mut(&mut self) -> &mut Option<std::ops::Range<u32>> {
&mut self.batch_range
}
}
let mut render_phase = RenderPhase::<TestPhaseItem>::default();
let items = [
TestPhaseItem {
entity: Entity::from_raw(0),
batch_range: Some(0..5),
},
TestPhaseItem {
entity: Entity::from_raw(0),
batch_range: Some(5..10),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(0..5),
},
TestPhaseItem {
entity: Entity::from_raw(0),
batch_range: Some(10..15),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(5..10),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: None,
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(10..15),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(20..25),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(25..30),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(30..35),
},
];
for item in items {
render_phase.add(item);
}
render_phase.batch();
let items_batched = [
TestPhaseItem {
entity: Entity::from_raw(0),
batch_range: Some(0..10),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(0..5),
},
TestPhaseItem {
entity: Entity::from_raw(0),
batch_range: Some(10..15),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(5..10),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: None,
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(10..15),
},
TestPhaseItem {
entity: Entity::from_raw(1),
batch_range: Some(20..35),
},
];
assert_eq!(&*render_phase.items, items_batched);
}
}