Skip to main content

gpui/
backdrop.rs

1//! Explicit bounded sampling of the already painted SDR framebuffer.
2use crate::*;
3use anyhow::{Result, ensure};
4use std::sync::Arc;
5
6/// Maximum backdrop boundaries encoded in a frame.
7pub const MAX_BACKDROPS: usize = 4;
8/// Maximum full-resolution capture side, including blur halo.
9pub const MAX_BACKDROP_SIDE: u32 = 1024;
10/// Downsampling factor used by the separable filter.
11pub const BACKDROP_DOWNSAMPLE: u32 = 4;
12
13/// Retained scene description for an explicit framebuffer pass barrier.
14#[derive(Clone, Debug)]
15pub struct BackdropDraw {
16    /// Scene ordering barrier.
17    pub order: DrawOrder,
18    /// Sampling footprint, including the blur halo.
19    pub bounds: Bounds<ScaledPixels>,
20    /// Full viewport used to order the pass barrier.
21    pub content_mask: ContentMask<ScaledPixels>,
22    /// Rectangle receiving the filtered pixels.
23    pub surface_bounds: Bounds<ScaledPixels>,
24    /// Ancestor content clip.
25    pub clip: ContentMask<ScaledPixels>,
26    /// Clamped surface corner radii in device pixels.
27    pub corner_radii: Corners<ScaledPixels>,
28    /// Gaussian support radius in device pixels.
29    pub radius: f32,
30    /// Filter strength, including element opacity.
31    pub opacity: f32,
32    /// Immutable shared filter program.
33    pub program: Arc<SceneShaderProgram>,
34}
35/// Checked allocation/dispatch plan shared by every backend.
36#[derive(Clone, Debug)]
37pub struct BackdropPlan {
38    /// Full-resolution source crop origin.
39    pub origin: [u32; 2],
40    /// Full-resolution source crop extent.
41    pub size: [u32; 2],
42    /// Quarter-resolution ping-pong extent.
43    pub reduced: [u32; 2],
44    /// Horizontal/downsample, vertical, and main-target composition uniforms.
45    pub parameters: [[[f32; 4]; 6]; 3],
46}
47impl BackdropDraw {
48    /// Validate before recording GPU work. The working set is at most 18 MiB
49    /// across four boundaries (excluding driver and in-flight retention).
50    pub fn plan(&self, viewport: Size<DevicePixels>) -> Result<BackdropPlan> {
51        ensure!(
52            viewport.width.0 > 0 && viewport.height.0 > 0,
53            "invalid backdrop viewport"
54        );
55        let b = self.surface_bounds;
56        let clip = self.clip.bounds;
57        let values = [
58            b.origin.x.0,
59            b.origin.y.0,
60            b.size.width.0,
61            b.size.height.0,
62            clip.origin.x.0,
63            clip.origin.y.0,
64            clip.size.width.0,
65            clip.size.height.0,
66            self.radius,
67            self.opacity,
68            self.corner_radii.top_left.0,
69            self.corner_radii.top_right.0,
70            self.corner_radii.bottom_right.0,
71            self.corner_radii.bottom_left.0,
72        ];
73        ensure!(
74            values.iter().all(|v| v.is_finite()),
75            "non-finite backdrop geometry"
76        );
77        ensure!(
78            self.radius > 0. && self.radius <= 128.,
79            "backdrop radius must be in (0,128] device pixels"
80        );
81        ensure!(
82            (0. ..=1.).contains(&self.opacity),
83            "invalid backdrop opacity"
84        );
85        ensure!(
86            b.size.width.0 > 0. && b.size.height.0 > 0.,
87            "empty backdrop surface"
88        );
89        ensure!(
90            values[10..].iter().all(|v| *v >= 0.),
91            "invalid backdrop corner radii"
92        );
93        // Include reconstruction/bilinear support beyond the Gaussian taps.
94        let halo = self.radius + BACKDROP_DOWNSAMPLE as f32;
95        let x = (b.origin.x.0 - halo)
96            .floor()
97            .max(0.)
98            .min(viewport.width.0 as f32) as u32;
99        let y = (b.origin.y.0 - halo)
100            .floor()
101            .max(0.)
102            .min(viewport.height.0 as f32) as u32;
103        let right = (b.origin.x.0 + b.size.width.0 + halo)
104            .ceil()
105            .max(0.)
106            .min(viewport.width.0 as f32) as u32;
107        let bottom = (b.origin.y.0 + b.size.height.0 + halo)
108            .ceil()
109            .max(0.)
110            .min(viewport.height.0 as f32) as u32;
111        let size = [right.saturating_sub(x), bottom.saturating_sub(y)];
112        ensure!(
113            size.iter().all(|v| *v > 0 && *v <= MAX_BACKDROP_SIDE),
114            "backdrop capture including halo must fit within 1024x1024 device pixels"
115        );
116        let reduced = [
117            size[0].div_ceil(BACKDROP_DOWNSAMPLE),
118            size[1].div_ceil(BACKDROP_DOWNSAMPLE),
119        ];
120        let mut parameters = [[[0.; 4]; 6]; 3];
121        for pass in 0..2 {
122            parameters[pass][0] = [reduced[0] as f32, reduced[1] as f32, pass as f32, 0.];
123            parameters[pass][1] = [0., 0., reduced[0] as f32, reduced[1] as f32];
124            parameters[pass][2] = [0., 0., 1., 1.];
125        }
126        parameters[0][4][0] = self.radius / 4.;
127        parameters[1][4][1] = self.radius / 4. * reduced[1] as f32 / size[1] as f32;
128        parameters[2][0] = [viewport.width.0 as f32, viewport.height.0 as f32, 2., 0.];
129        parameters[2][1] = [b.origin.x.0, b.origin.y.0, b.size.width.0, b.size.height.0];
130        parameters[2][2] = [
131            (b.origin.x.0 - x as f32) / size[0] as f32,
132            (b.origin.y.0 - y as f32) / size[1] as f32,
133            b.size.width.0 / size[0] as f32,
134            b.size.height.0 / size[1] as f32,
135        ];
136        parameters[2][3] = [
137            clip.origin.x.0,
138            clip.origin.y.0,
139            clip.origin.x.0 + clip.size.width.0,
140            clip.origin.y.0 + clip.size.height.0,
141        ];
142        parameters[2][4][2] = self.opacity;
143        parameters[2][5] = [
144            self.corner_radii.top_left.0,
145            self.corner_radii.top_right.0,
146            self.corner_radii.bottom_right.0,
147            self.corner_radii.bottom_left.0,
148        ];
149        Ok(BackdropPlan {
150            origin: [x, y],
151            size,
152            reduced,
153            parameters,
154        })
155    }
156}