use crate::layer::{DrawCommand, Layer, LayerId, LayerTree, Material};
use log::warn;
#[derive(Debug, Clone)]
pub struct RoutedDrawCommand {
pub command: DrawCommand,
pub material: Material,
pub source_layer: LayerId,
pub z_index: u32,
}
#[derive(Debug, Default)]
pub struct CommandBuckets {
pub scene_commands: Vec<RoutedDrawCommand>,
pub glass_commands: Vec<RoutedDrawCommand>,
pub overlay_commands: Vec<RoutedDrawCommand>,
}
impl CommandBuckets {
pub fn total_count(&self) -> usize {
self.scene_commands.len() + self.glass_commands.len() + self.overlay_commands.len()
}
pub fn is_empty(&self) -> bool {
self.scene_commands.is_empty()
&& self.glass_commands.is_empty()
&& self.overlay_commands.is_empty()
}
pub fn clear(&mut self) {
self.scene_commands.clear();
self.glass_commands.clear();
self.overlay_commands.clear();
}
}
#[derive(Debug, Default)]
pub struct DamageInfo {
pub dirty_layers: Vec<LayerId>,
pub frame_generation: u64,
pub full_rebuild_needed: bool,
}
pub struct CompositorEngine {
layer_tree: LayerTree,
flatten_buffer: Vec<RoutedDrawCommand>,
last_flatten_generation: u64,
current_damage: DamageInfo,
z_counter: u32,
}
impl Default for CompositorEngine {
fn default() -> Self {
Self::new()
}
}
impl CompositorEngine {
pub fn new() -> Self {
Self {
layer_tree: LayerTree::new(),
flatten_buffer: Vec::new(),
last_flatten_generation: 0,
current_damage: DamageInfo::default(),
z_counter: 0,
}
}
pub fn layer_tree(&self) -> &LayerTree {
&self.layer_tree
}
pub fn layer_tree_mut(&mut self) -> &mut LayerTree {
&mut self.layer_tree
}
pub fn create_layer(&mut self, layer: Layer) -> LayerId {
let id = layer.id;
self.layer_tree.insert_layer(layer);
self.current_damage.dirty_layers.push(id);
self.current_damage.full_rebuild_needed = true;
id
}
pub fn remove_layer(&mut self, id: LayerId) -> Option<Layer> {
self.current_damage.dirty_layers.push(id);
self.current_damage.full_rebuild_needed = true;
self.layer_tree.remove_layer(id)
}
pub fn mark_dirty(&mut self, id: LayerId) {
if self.layer_tree.get_layer(id).is_some() {
self.layer_tree.mark_dirty(id);
self.current_damage.dirty_layers.push(id);
}
}
pub fn damage_info(&self) -> &DamageInfo {
&self.current_damage
}
pub fn flatten_and_route(&mut self) -> CommandBuckets {
let mut buckets = CommandBuckets::default();
if self.layer_tree.is_empty() {
return buckets;
}
self.flatten_buffer.clear();
self.z_counter = 0;
let roots = self.layer_tree.roots().to_vec();
Self::flatten_tree(
&mut self.layer_tree,
&roots,
&mut self.flatten_buffer,
&mut self.z_counter,
);
for cmd in &self.flatten_buffer {
match cmd.material {
Material::Opaque
| Material::Multiply
| Material::Screen
| Material::BlendOverlay
| Material::Darken
| Material::Lighten
| Material::ColorDodge
| Material::ColorBurn
| Material::HardLight
| Material::SoftLight
| Material::Difference
| Material::Exclusion
| Material::Hue
| Material::Saturation
| Material::Color
| Material::Luminosity => {
buckets.scene_commands.push(cmd.clone());
}
Material::Isolated => {
buckets.scene_commands.push(cmd.clone());
}
Material::Glass { .. } => {
buckets.glass_commands.push(cmd.clone());
}
Material::Overlay => {
buckets.overlay_commands.push(cmd.clone());
}
}
}
self.last_flatten_generation = self.layer_tree.generation();
self.current_damage.frame_generation = self.last_flatten_generation;
self.current_damage.dirty_layers.clear();
self.current_damage.full_rebuild_needed = false;
buckets
}
fn flatten_tree(
layer_tree: &mut LayerTree,
layer_ids: &[LayerId],
buffer: &mut Vec<RoutedDrawCommand>,
z_counter: &mut u32,
) {
for layer_id in layer_ids {
Self::flatten_layer(layer_tree, *layer_id, buffer, z_counter);
}
}
fn flatten_layer(
layer_tree: &mut LayerTree,
layer_id: LayerId,
buffer: &mut Vec<RoutedDrawCommand>,
z_counter: &mut u32,
) {
let layer = match layer_tree.get_layer(layer_id) {
Some(l) => l,
None => {
warn!(
"CompositorEngine: referenced layer {:?} not found in tree",
layer_id
);
return;
}
};
if !layer.visible {
return;
}
let material = layer.material;
let draw_list: Vec<_> = layer.draw_list.to_vec();
let children: Vec<_> = layer.children.iter().rev().cloned().collect();
for cmd in &draw_list {
buffer.push(RoutedDrawCommand {
command: cmd.clone(),
material,
source_layer: layer_id,
z_index: *z_counter,
});
*z_counter += 1;
}
for child_id in &children {
Self::flatten_layer(layer_tree, *child_id, buffer, z_counter);
}
}
pub fn needs_reflatten(&self) -> bool {
if self.current_damage.full_rebuild_needed {
return true;
}
if !self.current_damage.dirty_layers.is_empty() {
return true;
}
self.layer_tree.generation() > self.last_flatten_generation
}
pub fn end_frame(&mut self) {
self.layer_tree.advance_generation();
}
pub fn clear(&mut self) {
self.layer_tree.clear();
self.flatten_buffer.clear();
self.last_flatten_generation = 0;
self.current_damage = DamageInfo::default();
self.z_counter = 0;
}
}