1use crate::*;
3use anyhow::{Result, ensure};
4use std::sync::Arc;
5
6pub const MAX_LAYER_GROUPS: usize = 4;
8pub const MAX_LAYER_SIDE: u32 = 1024;
10pub const MAX_LAYER_COMMANDS: usize = 65_536;
12
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
15pub enum LayerBlurQuality {
16 #[default]
18 Adaptive,
19 FullResolution,
21}
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
25#[repr(u8)]
26pub enum LayerBlendMode {
27 #[default]
29 Normal = 0,
30 Multiply = 1,
32 Screen = 2,
34 Overlay = 3,
36 ColorDodge = 4,
38 SoftLight = 5,
40 LinearBurn = 6,
42}
43
44#[derive(Clone, Copy, Debug)]
47pub struct LayerColorFilter {
48 pub blur_quality: LayerBlurQuality,
50 pub blur_radius: Pixels,
52 pub blend_mode: LayerBlendMode,
54 pub saturation: f32,
56 pub brightness: f32,
58 pub contrast: f32,
60 pub invert: f32,
62}
63impl Default for LayerColorFilter {
64 fn default() -> Self {
65 Self {
66 blur_quality: LayerBlurQuality::Adaptive,
67 blur_radius: px(0.),
68 blend_mode: LayerBlendMode::Normal,
69 saturation: 1.,
70 brightness: 1.,
71 contrast: 1.,
72 invert: 0.,
73 }
74 }
75}
76
77#[derive(Debug)]
79pub struct LayerCapture {
80 pub blur_quality: LayerBlurQuality,
82 pub blur_radius: ScaledPixels,
84 pub blend_mode: LayerBlendMode,
86 pub bounds: Bounds<ScaledPixels>,
88 pub clip: Option<Bounds<ScaledPixels>>,
90 pub opacity: f32,
92 pub saturation: f32,
94 pub brightness: f32,
96 pub contrast: f32,
98 pub invert: f32,
100 pub program: Arc<SceneShaderProgram>,
102}
103#[derive(Clone, Debug)]
105pub struct LayerBoundary {
106 pub order: DrawOrder,
108 pub bounds: Bounds<ScaledPixels>,
110 pub content_mask: ContentMask<ScaledPixels>,
112 pub capture: Arc<LayerCapture>,
114 pub begin: bool,
116}
117#[derive(Clone, Debug)]
119pub struct LayerPlan {
120 pub begin_index: usize,
122 pub end_index: usize,
124 pub origin: [u32; 2],
126 pub size: [u32; 2],
128 pub parameters: [[[f32; 4]; 6]; 4],
130 pub blur_radius: f32,
132 pub blur_size: [u32; 2],
134 pub revision: Arc<[u64]>,
136 pub reusable: bool,
138}
139#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
141pub struct LayerCacheStats {
142 pub captures: u64,
144 pub hits: u64,
146}
147impl LayerPlan {
148 pub(crate) fn new(
149 capture: &LayerCapture,
150 viewport: Size<DevicePixels>,
151 begin_index: usize,
152 ) -> Result<Self> {
153 ensure!(
154 capture.program.output_encoding() == ShaderOutputEncoding::EncodedSrgb
155 && !capture.program.uses_glyph_mask()
156 && !capture.program.uses_path_mask(),
157 "layer capture requires an SDR composition program"
158 );
159 let b = capture.bounds;
160 ensure!(
161 [
162 b.origin.x.0,
163 b.origin.y.0,
164 b.size.width.0,
165 b.size.height.0,
166 capture.opacity,
167 capture.saturation,
168 capture.brightness,
169 capture.contrast,
170 capture.invert,
171 capture.blur_radius.0
172 ]
173 .iter()
174 .all(|v| v.is_finite()),
175 "non-finite layer geometry"
176 );
177 ensure!(
178 viewport.width.0 > 0
179 && viewport.height.0 > 0
180 && b.size.width.0 > 0.
181 && b.size.height.0 > 0.,
182 "empty layer capture"
183 );
184 ensure!(
185 (0. ..=1.).contains(&capture.opacity),
186 "invalid group opacity"
187 );
188 ensure!(
189 capture.saturation.is_finite() && (0. ..=2.).contains(&capture.saturation),
190 "invalid group saturation"
191 );
192 ensure!(
193 (0. ..=2.).contains(&capture.brightness),
194 "invalid group brightness"
195 );
196 ensure!(
197 (0. ..=2.).contains(&capture.contrast),
198 "invalid group contrast"
199 );
200 ensure!(
201 (0. ..=1.).contains(&capture.invert),
202 "invalid group inversion"
203 );
204 let clamp_x = |x: f32| x.max(0.).min(viewport.width.0 as f32) as u32;
205 let clamp_y = |y: f32| y.max(0.).min(viewport.height.0 as f32) as u32;
206 let origin = [clamp_x(b.origin.x.0.floor()), clamp_y(b.origin.y.0.floor())];
207 let end = [
208 clamp_x((b.origin.x.0 + b.size.width.0).ceil()),
209 clamp_y((b.origin.y.0 + b.size.height.0).ceil()),
210 ];
211 let size = [
212 end[0].saturating_sub(origin[0]),
213 end[1].saturating_sub(origin[1]),
214 ];
215 ensure!(
216 size.iter().all(|v| *v > 0 && *v <= MAX_LAYER_SIDE),
217 "layer capture must fit within 1024x1024 device pixels"
218 );
219 ensure!(
220 (0. ..=64.).contains(&capture.blur_radius.0),
221 "layer blur radius must be in [0,64] device pixels"
222 );
223 let factor = match capture.blur_quality {
224 LayerBlurQuality::FullResolution => 1,
225 LayerBlurQuality::Adaptive if capture.blur_radius.0 >= 8. => 4,
226 LayerBlurQuality::Adaptive if capture.blur_radius.0 >= 4. => 2,
227 LayerBlurQuality::Adaptive => 1,
228 };
229 let blur_size = size.map(|v| v.div_ceil(factor));
230 let output_clip = capture.clip.unwrap_or(b);
231 ensure!(
232 [
233 output_clip.origin.x.0,
234 output_clip.origin.y.0,
235 output_clip.size.width.0,
236 output_clip.size.height.0
237 ]
238 .iter()
239 .all(|v| v.is_finite())
240 && output_clip.size.width.0 >= 0.
241 && output_clip.size.height.0 >= 0.,
242 "layer output clip must be finite and nonnegative"
243 );
244 let clip = output_clip.intersect(&Bounds::new(
245 Point::default(),
246 viewport.map(|p| ScaledPixels(p.0 as f32)),
247 ));
248 ensure!(
249 [
250 clip.origin.x.0,
251 clip.origin.y.0,
252 clip.size.width.0,
253 clip.size.height.0
254 ]
255 .iter()
256 .all(|v| v.is_finite())
257 && clip.intersect(&b) == clip,
258 "layer output clip must fit within its source capture"
259 );
260 let mut parameters = [[[0.; 4]; 6]; 4];
261 for pass in 0..2 {
262 parameters[pass][0] = [
263 viewport.width.0 as f32,
264 viewport.height.0 as f32,
265 3. + pass as f32,
266 0.,
267 ];
268 parameters[pass][1] = [
269 origin[0] as f32,
270 origin[1] as f32,
271 size[0] as f32,
272 size[1] as f32,
273 ];
274 parameters[pass][2] = [0., 0., 1., 1.];
275 parameters[pass][3] = [
276 origin[0] as f32,
277 origin[1] as f32,
278 end[0] as f32,
279 end[1] as f32,
280 ];
281 if pass == 1 {
282 parameters[pass][3] =
283 [clip.left().0, clip.top().0, clip.right().0, clip.bottom().0];
284 }
285 parameters[pass][4][2] = capture.opacity;
286 parameters[pass][4][3] = capture.saturation;
287 parameters[pass][5] = [
288 capture.brightness,
289 capture.contrast,
290 capture.invert,
291 capture.blend_mode as u8 as f32,
292 ];
293 }
294 for pass in 0..2 {
295 parameters[pass + 2][0] = [blur_size[0] as f32, blur_size[1] as f32, 5., 0.];
296 parameters[pass + 2][1] = [0., 0., blur_size[0] as f32, blur_size[1] as f32];
297 parameters[pass + 2][2] = [0., 0., 1., 1.];
298 parameters[pass + 2][4] = [
299 if pass == 0 { 1. } else { 0. },
300 if pass == 1 { 1. } else { 0. },
301 capture.blur_radius.0 * blur_size[pass] as f32 / size[pass] as f32,
302 0.,
303 ];
304 }
305 Ok(Self {
306 blur_radius: capture.blur_radius.0,
307 blur_size,
308 begin_index,
309 end_index: 0,
310 origin,
311 size,
312 parameters,
313 revision: Arc::from([]),
314 reusable: true,
315 })
316 }
317 pub fn needs_blur(&self, previous: Option<&Self>) -> bool {
320 self.blur_radius > 0.
321 && previous.is_none_or(|old| {
322 !self.matches(old)
323 || old.blur_radius != self.blur_radius
324 || old.blur_size != self.blur_size
325 })
326 }
327 pub fn matches(&self, previous: &Self) -> bool {
330 self.reusable
331 && previous.reusable
332 && self.origin == previous.origin
333 && self.size == previous.size
334 && self.revision == previous.revision
335 }
336}