1use anyrender::{Filter, PaintScene};
2use kurbo::{Affine, Shape};
3use peniko::Mix;
4use std::sync::atomic::{AtomicU32, Ordering};
5use std::{cell::Cell, sync::Arc};
6
7const LAYER_LIMIT: u32 = 1024;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum LayerSite {
17 ClipPath,
19 Effect,
21 Overflow,
23 OutsetShadow,
25 BackgroundImage,
27 InsetShadow,
29 Mask,
31 Border,
33}
34
35impl LayerSite {
36 pub const ALL: [LayerSite; 8] = [
38 LayerSite::ClipPath,
39 LayerSite::Effect,
40 LayerSite::Overflow,
41 LayerSite::OutsetShadow,
42 LayerSite::BackgroundImage,
43 LayerSite::InsetShadow,
44 LayerSite::Mask,
45 LayerSite::Border,
46 ];
47
48 pub fn name(self) -> &'static str {
50 match self {
51 LayerSite::ClipPath => "clip-path",
52 LayerSite::Effect => "effect",
53 LayerSite::Overflow => "overflow",
54 LayerSite::OutsetShadow => "outset-shadow",
55 LayerSite::BackgroundImage => "bg-image",
56 LayerSite::InsetShadow => "inset-shadow",
57 LayerSite::Mask => "mask",
58 LayerSite::Border => "border",
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
75pub struct SceneLayerCounts {
76 pub wanted: u32,
78 pub used: u32,
80 pub max_depth: u32,
82 pub by_site: [u32; 8],
87}
88
89static LATEST_WANTED: AtomicU32 = AtomicU32::new(0);
98static LATEST_USED: AtomicU32 = AtomicU32::new(0);
99static LATEST_MAX_DEPTH: AtomicU32 = AtomicU32::new(0);
100#[allow(clippy::declare_interior_mutable_const)]
101const ZERO: AtomicU32 = AtomicU32::new(0);
102static LATEST_BY_SITE: [AtomicU32; 8] = [ZERO; 8];
103
104pub fn latest_scene_layers() -> SceneLayerCounts {
111 let mut by_site = [0u32; 8];
112 for (slot, counter) in by_site.iter_mut().zip(LATEST_BY_SITE.iter()) {
113 *slot = counter.load(Ordering::Relaxed);
114 }
115 SceneLayerCounts {
116 wanted: LATEST_WANTED.load(Ordering::Relaxed),
117 used: LATEST_USED.load(Ordering::Relaxed),
118 max_depth: LATEST_MAX_DEPTH.load(Ordering::Relaxed),
119 by_site,
120 }
121}
122
123#[derive(Default)]
124pub(crate) struct LayerManager {
125 layers_used: Cell<u32>,
126 layer_depth: Cell<u32>,
127 layers_wanted: Cell<u32>,
128 layer_depth_used: Cell<u32>,
129 by_site: [Cell<u32>; 8],
130}
131
132impl LayerManager {
133 pub(crate) fn publish(&self) {
135 LATEST_WANTED.store(self.layers_wanted.get(), Ordering::Relaxed);
136 LATEST_USED.store(self.layers_used.get(), Ordering::Relaxed);
137 LATEST_MAX_DEPTH.store(self.layer_depth_used.get(), Ordering::Relaxed);
138 for (counter, cell) in LATEST_BY_SITE.iter().zip(self.by_site.iter()) {
139 counter.store(cell.get(), Ordering::Relaxed);
140 }
141 }
142
143 pub(crate) fn note_unmanaged(&self, site: LayerSite) {
149 self.by_site[site as usize].update(|x| x + 1);
150 }
151
152 #[allow(clippy::too_many_arguments)]
153 pub(crate) fn maybe_with_layer<S: PaintScene, F: FnOnce(&mut S)>(
154 &self,
155 scene: &mut S,
156 site: LayerSite,
157 condition: bool,
158 opacity: f32,
159 transform: Affine,
160 shape: &impl Shape,
161 filter: Option<Arc<Filter>>,
162 backdrop_filter: Option<Arc<Filter>>,
163 paint_layer: F,
164 ) {
165 let layer_used = self.maybe_push_layer(
166 scene,
167 site,
168 condition,
169 opacity,
170 transform,
171 shape,
172 filter,
173 backdrop_filter,
174 );
175 paint_layer(scene);
176 self.maybe_pop_layer(scene, layer_used);
177 }
178
179 #[allow(clippy::too_many_arguments)]
180 pub(crate) fn maybe_push_layer(
181 &self,
182 scene: &mut impl PaintScene,
183 site: LayerSite,
184 condition: bool,
185 opacity: f32,
186 transform: Affine,
187 shape: &impl Shape,
188 filter: Option<Arc<Filter>>,
189 backdrop_filter: Option<Arc<Filter>>,
190 ) -> bool {
191 if !condition {
192 return false;
193 }
194 self.layers_wanted.update(|x| x + 1);
195 self.by_site[site as usize].update(|x| x + 1);
196
197 let layers_available = self.layers_used.get() <= LAYER_LIMIT;
199 if !layers_available {
200 return false;
201 }
202
203 if opacity == 1.0 && filter.is_none() && backdrop_filter.is_none() {
205 scene.push_clip_layer(transform, shape);
206 } else {
207 scene.push_layer(
208 Mix::Normal,
209 opacity,
210 transform,
211 shape,
212 filter,
213 backdrop_filter,
214 );
215 };
216
217 self.layers_used.update(|x| x + 1);
222 self.layer_depth.update(|x| x + 1);
223 self.layer_depth_used
224 .update(|x| x.max(self.layer_depth.get()));
225
226 true
227 }
228
229 pub(crate) fn maybe_pop_layer(&self, scene: &mut impl PaintScene, condition: bool) {
230 if condition {
231 scene.pop_layer();
232 self.layer_depth.update(|x| x - 1);
233 }
234 }
235}