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