Skip to main content

cvkg_compositor/
layer.rs

1//! # Layer Tree & Material Definitions
2//!
3//! Defines the retained-mode layer orchestration structures.
4//! The compositor organizes UI elements into a `LayerTree`, where each `Layer`
5//! has an explicit `Material` property that dictates which GPU pass it belongs to
6//! in the Backdrop Capture Architecture.
7
8use cvkg_core::Rect;
9use std::collections::HashMap;
10
11/// Unique identifier for a layer in the tree.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
13pub struct LayerId(pub u64);
14
15/// Material type that determines which GPU pass a layer's draw calls are routed to
16/// in the Backdrop Capture Architecture.
17///
18/// The blend mode variants correspond to the 16 SVG 1.1 blend modes from
19/// the CSS Compositing and Blending Level 1 specification. When a blend mode
20/// is set, the draw call's fragment shader uses the corresponding blend function
21/// instead of standard alpha compositing.
22///
23/// `Isolated` triggers off-screen buffer rendering: the layer and all its
24/// children are rendered to a separate texture, then composited back into the
25/// main scene. This matches the SVG `isolation` property.
26#[derive(Debug, Clone, PartialEq, Default)]
27pub enum Material {
28    /// Opaque or standard UI. Rendered in the initial Scene Capture pass
29    /// with standard alpha compositing (src-over).
30    #[default]
31    Opaque,
32    /// Glassmorphism elements. Rendered in the Material Composite pass,
33    /// sampling from the Kawase Blur pyramid.
34    /// The `blur_radius` controls the blur intensity.
35    /// The `depth_index` (0=foreground, higher=more background) controls
36    /// depth-aware tinting: background windows get stronger tint.
37    Glass {
38        blur_radius: f32,
39        /// Z-order depth for depth-aware tinting. 0 = key/foreground window.
40        /// Higher values = more background. Default: 0.
41        depth_index: u32,
42    },
43    /// Overlay UI (crisp text, focus rings, edge lighting).
44    /// Rendered in the final Foreground pass, on top of glass.
45    Overlay,
46
47    // ── SVG Blend Modes (CSS Compositing Level 1) ──────────────────────────
48    /// Multiplied blend: multiplies source and destination colors.
49    /// Formula: result = src * dst
50    Multiply,
51    /// Screen blend: inverse of multiply.
52    /// Formula: result = 1 - (1 - src) * (1 - dst)
53    Screen,
54    /// Overlay blend: combines multiply and screen based on destination.
55    /// Formula: if dst < 0.5 then 2*src*dst else 1-2*(1-src)*(1-dst)
56    BlendOverlay,
57    /// Darken blend: keeps the darker of source and destination per channel.
58    /// Formula: result = min(src, dst)
59    Darken,
60    /// Lighten blend: keeps the lighter of source and destination per channel.
61    /// Formula: result = max(src, dst)
62    Lighten,
63    /// Color-dodge blend: brightens destination to reflect source.
64    /// Formula: result = dst / (1 - src)
65    ColorDodge,
66    /// Color-burn blend: darkens destination to reflect source.
67    /// Formula: result = 1 - (1 - dst) / src
68    ColorBurn,
69    /// Hard-light blend: like overlay, but based on source instead of dest.
70    /// Formula: if src < 0.5 then 2*src*dst else 1-2*(1-src)*(1-dst)
71    HardLight,
72    /// Soft-light blend: subtle highlights/shadows.
73    /// Formula: result = (1-2*src)*dst^2 + 2*src*dst (simplified Pegtop)
74    SoftLight,
75    /// Difference blend: subtracts colors and takes absolute value.
76    /// Formula: result = |src - dst|
77    Difference,
78    /// Exclusion blend: similar to difference but lower contrast.
79    /// Formula: result = src + dst - 2*src*dst
80    Exclusion,
81    /// Hue blend: applies source hue to destination saturation/luminosity.
82    Hue,
83    /// Saturation blend: applies source saturation to destination hue/luminosity.
84    Saturation,
85    /// Color blend: applies source hue/saturation to destination luminosity.
86    Color,
87    /// Luminosity blend: applies source luminosity to destination hue/saturation.
88    Luminosity,
89
90    /// Isolated rendering: layer and children are rendered to an off-screen
91    /// buffer, then composited back into the main scene. This matches the
92    /// SVG `isolation` property and is required for correct blend mode
93    /// behavior when child elements should not blend with the background.
94    Isolated,
95
96    /// Renders the layer and its children into an offscreen buffer, then applies
97    /// a custom post-processing WGSL shader when blending back into the scene.
98    ShaderEffect {
99        /// Name of the registered shader effect (e.g. "HeatShimmer")
100        effect_name: String,
101        /// Dynamic parameters for the shader, serialized as a JSON string
102        params_json: String,
103    },
104}
105
106/// A draw command within a layer.
107/// This is a simplified representation that the compositor produces
108/// and the renderer consumes.
109#[derive(Debug, Clone)]
110pub struct DrawCommand {
111    /// Texture binding index (None for solid color).
112    pub texture_id: Option<u32>,
113    /// Scissor rectangle for clipping.
114    pub scissor_rect: Option<Rect>,
115    /// Range in the shared index buffer.
116    pub index_start: u32,
117    pub index_count: u32,
118    /// Instance ID for instanced rendering transform data.
119    pub instance_id: u32,
120}
121
122/// A node in the retained-mode layer tree.
123/// Each layer represents a compositable unit with its own material,
124/// transform, and draw list.
125#[derive(Debug, Clone)]
126pub struct Layer {
127    /// Unique identifier.
128    pub id: LayerId,
129    /// Screen-space bounding rectangle.
130    pub bounds: Rect,
131    /// 4x4 transformation matrix (column-major).
132    pub transform: [f32; 16],
133    /// Material determining which GPU pass this layer belongs to.
134    pub material: Material,
135    /// Draw commands for this layer.
136    pub draw_list: Vec<DrawCommand>,
137    /// Child layer IDs in painter's order (back to front).
138    pub children: Vec<LayerId>,
139    /// Visibility flag.
140    pub visible: bool,
141    /// Opacity multiplier. Defaults to 1.0.
142    pub opacity: f32,
143}
144
145impl Default for Layer {
146    fn default() -> Self {
147        Self {
148            id: LayerId::default(),
149            bounds: Rect::zero(),
150            transform: [
151                1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
152            ],
153            material: Material::Opaque,
154            draw_list: Vec::new(),
155            children: Vec::new(),
156            visible: true,
157            opacity: 1.0,
158        }
159    }
160}
161
162/// The retained-mode layer tree.
163/// Maintained across frames by the `CompositorEngine`.
164pub struct LayerTree {
165    /// All layers indexed by ID.
166    layers: HashMap<LayerId, Layer>,
167    /// Root layer IDs in painter's order (back to front).
168    roots: Vec<LayerId>,
169    /// Next available layer ID counter.
170    next_id: u64,
171    /// Generation counter for damage tracking.
172    generation: u64,
173    /// Per-layer generation stamps for change detection.
174    layer_generations: HashMap<LayerId, u64>,
175}
176
177impl Default for LayerTree {
178    fn default() -> Self {
179        Self::new()
180    }
181}
182
183impl LayerTree {
184    /// Creates a new empty layer tree.
185    pub fn new() -> Self {
186        Self {
187            layers: HashMap::new(),
188            roots: Vec::new(),
189            next_id: 1,
190            generation: 0,
191            layer_generations: HashMap::new(),
192        }
193    }
194
195    /// Allocates and returns a new layer ID.
196    pub fn allocate_id(&mut self) -> LayerId {
197        let id = LayerId(self.next_id);
198        self.next_id += 1;
199        id
200    }
201
202    /// Inserts a new layer into the tree.
203    pub fn insert_layer(&mut self, layer: Layer) {
204        let id = layer.id;
205        self.layer_generations.insert(id, self.generation);
206        self.layers.insert(id, layer);
207    }
208
209    /// Removes a layer from the tree.
210    pub fn remove_layer(&mut self, id: LayerId) -> Option<Layer> {
211        self.layer_generations.remove(&id);
212        self.layers.remove(&id)
213    }
214
215    /// Returns a reference to a layer by ID.
216    pub fn get_layer(&self, id: LayerId) -> Option<&Layer> {
217        self.layers.get(&id)
218    }
219
220    /// Returns a mutable reference to a layer by ID.
221    pub fn get_layer_mut(&mut self, id: LayerId) -> Option<&mut Layer> {
222        self.layers.get_mut(&id)
223    }
224
225    /// Returns the root layer IDs in painter's order.
226    pub fn roots(&self) -> &[LayerId] {
227        &self.roots
228    }
229
230    /// Sets the root layer IDs.
231    pub fn set_roots(&mut self, roots: Vec<LayerId>) {
232        self.roots = roots;
233    }
234
235    /// Marks a layer as dirty (modified since last frame).
236    pub fn mark_dirty(&mut self, id: LayerId) {
237        self.layer_generations.insert(id, self.generation);
238    }
239
240    /// Returns true if the layer has been modified since the given generation.
241    pub fn is_dirty(&self, id: LayerId, since_generation: u64) -> bool {
242        self.layer_generations
243            .get(&id)
244            .is_some_and(|&g| g > since_generation)
245    }
246
247    /// Advances the global generation counter.
248    /// Call once per frame after processing damage.
249    pub fn advance_generation(&mut self) {
250        self.generation += 1;
251    }
252
253    /// Returns the current global generation.
254    pub fn generation(&self) -> u64 {
255        self.generation
256    }
257
258    /// Iterates over all layers in the tree.
259    pub fn iter_layers(&self) -> impl Iterator<Item = &Layer> {
260        self.layers.values()
261    }
262
263    /// Returns the number of layers in the tree.
264    pub fn len(&self) -> usize {
265        self.layers.len()
266    }
267
268    /// Returns true if the tree has no layers.
269    pub fn is_empty(&self) -> bool {
270        self.layers.is_empty()
271    }
272
273    /// Clears all layers from the tree.
274    pub fn clear(&mut self) {
275        self.layers.clear();
276        self.roots.clear();
277        self.layer_generations.clear();
278        self.generation += 1;
279    }
280}