atagen

monolith

public
Star0
Watch0
e27a31fac80d6c681920e4212a42b8cc3105dc0b
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/frame.rsRust326 lines9.4 KB
1
use crate::Error;
2
use crate::image::{VulkanTarget, VulkanTexture};
3
use crate::pipeline::{Kind, Push};
4
use crate::renderer::VulkanRenderer;
5
use crate::transform::{rect_to_framebuffer, texture_uvs, to_framebuffer};
6
use ash::vk;
7
use smithay::backend::renderer::sync::SyncPoint;
8
use smithay::backend::renderer::{Color32F, ContextId, Frame, Texture};
9
use smithay::utils::{Buffer, Physical, Rectangle, Size, Transform};
10
use std::sync::Arc;
11
12
pub(crate) enum Op {
13
    Clear {
14
        color: [f32; 4],
15
        rects: Vec<vk::Rect2D>,
16
    },
17
    Draw {
18
        kind: Kind,
19
        opaque: bool,
20
        texture: Option<VulkanTexture>,
21
        push: Push,
22
        scissors: Vec<vk::Rect2D>,
23
    },
24
}
25
26
/// A frame in progress. Draws are recorded and submitted together by [`Frame::finish`].
27
pub struct VulkanFrame<'frame, 'buffer> {
28
    renderer: &'frame mut VulkanRenderer,
29
    target: &'frame mut VulkanTarget<'buffer>,
30
    output: Size<i32, Physical>,
31
    /// The output rotated into the space draws are given in, which differs
32
    /// from `output` for 90 and 270 degree transforms.
33
    area: Size<i32, Physical>,
34
    transform: Transform,
35
    ops: Vec<Op>,
36
    clip: Option<Clip>,
37
}
38
39
/// A rounded rect in output space that later draws are cut to, until
40
/// [`VulkanFrame::set_clip`] changes it. A nonzero `ring` keeps only a band
41
/// that wide inside the edge.
42
#[derive(Clone, Copy, Debug, PartialEq)]
43
pub struct Clip {
44
    pub rect: Rectangle<i32, Physical>,
45
    /// Top left, top right, bottom right and bottom left, in pixels.
46
    pub radii: [f32; 4],
47
    pub ring: f32,
48
}
49
50
impl std::fmt::Debug for VulkanFrame<'_, '_> {
51
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52
        f.debug_struct("VulkanFrame")
53
            .field("output", &self.output)
54
            .field("ops", &self.ops.len())
55
            .finish()
56
    }
57
}
58
59
impl<'frame, 'buffer> VulkanFrame<'frame, 'buffer> {
60
    pub(crate) fn new(
61
        renderer: &'frame mut VulkanRenderer,
62
        target: &'frame mut VulkanTarget<'buffer>,
63
        output: Size<i32, Physical>,
64
        transform: Transform,
65
    ) -> Self {
66
        Self {
67
            renderer,
68
            target,
69
            output,
70
            area: transform.transform_size(output),
71
            transform,
72
            ops: Vec::new(),
73
            clip: None,
74
        }
75
    }
76
77
    pub fn set_clip(&mut self, clip: Option<Clip>) {
78
        self.clip = clip;
79
    }
80
81
    /// The clip's push constants in framebuffer space, where an output
82
    /// transform moves each radius to another corner.
83
    fn clip_push(&self) -> ([f32; 4], [f32; 4], [f32; 4]) {
84
        let Some(c) = self.clip else {
85
            return ([0.0; 4], [0.0; 4], [0.0; 4]);
86
        };
87
        let [tl, tr, br, bl] = c.radii;
88
        let corners = [[tl, 0.0], [tr, 0.0], [bl, 0.0], [br, 0.0]];
89
        let (fb, r) = to_framebuffer(c.rect, corners, self.area, self.transform);
90
        let rect = [
91
            fb.loc.x as f32,
92
            fb.loc.y as f32,
93
            fb.size.w as f32,
94
            fb.size.h as f32,
95
        ];
96
        let most = fb.size.w.min(fb.size.h) as f32 / 2.0;
97
        let radii = [r[0][0], r[1][0], r[3][0], r[2][0]].map(|r| r.min(most));
98
        (rect, radii, [c.ring, 0.0, 0.0, 0.0])
99
    }
100
101
    fn fb_size(&self) -> Size<i32, Physical> {
102
        (self.target.width() as i32, self.target.height() as i32).into()
103
    }
104
105
    fn fb_rect(&self, r: Rectangle<i32, Physical>) -> Option<vk::Rect2D> {
106
        let r = rect_to_framebuffer(r, self.area, self.transform)
107
            .intersection(Rectangle::from_size(self.fb_size()))?;
108
        Some(vk::Rect2D {
109
            offset: vk::Offset2D {
110
                x: r.loc.x,
111
                y: r.loc.y,
112
            },
113
            extent: vk::Extent2D {
114
                width: r.size.w as u32,
115
                height: r.size.h as u32,
116
            },
117
        })
118
    }
119
120
    fn scissors(
121
        &self,
122
        dst: Rectangle<i32, Physical>,
123
        damage: &[Rectangle<i32, Physical>],
124
    ) -> Vec<vk::Rect2D> {
125
        damage
126
            .iter()
127
            .filter_map(|d| d.intersection(Rectangle::from_size(dst.size)))
128
            .filter_map(|mut d| {
129
                d.loc += dst.loc;
130
                self.fb_rect(d)
131
            })
132
            .collect()
133
    }
134
135
    /// Draws a texture through the transition hook in `shaders/effect.frag`.
136
    #[expect(clippy::too_many_arguments)]
137
    pub fn render_effect(
138
        &mut self,
139
        texture: &VulkanTexture,
140
        src: Rectangle<f64, Buffer>,
141
        dst: Rectangle<i32, Physical>,
142
        damage: &[Rectangle<i32, Physical>],
143
        src_transform: Transform,
144
        alpha: f32,
145
        effect: Effect,
146
    ) -> Result<(), Error> {
147
        let params = [effect.progress, effect.kind as u32 as f32, 0.0, 0.0];
148
        self.draw_texture(
149
            texture,
150
            src,
151
            dst,
152
            damage,
153
            src_transform,
154
            alpha,
155
            Kind::Effect,
156
            params,
157
        )
158
    }
159
160
    #[expect(clippy::too_many_arguments)]
161
    fn draw_texture(
162
        &mut self,
163
        texture: &VulkanTexture,
164
        src: Rectangle<f64, Buffer>,
165
        dst: Rectangle<i32, Physical>,
166
        damage: &[Rectangle<i32, Physical>],
167
        src_transform: Transform,
168
        alpha: f32,
169
        kind: Kind,
170
        effect: [f32; 4],
171
    ) -> Result<(), Error> {
172
        if !Arc::ptr_eq(&texture.0.shared, self.renderer.shared()) {
173
            return Err(Error::ForeignTexture);
174
        }
175
        if dst.size.w <= 0 || dst.size.h <= 0 || Arc::ptr_eq(&texture.0, &self.target.0) {
176
            return Ok(());
177
        }
178
        let scissors = self.scissors(dst, damage);
179
        if scissors.is_empty() {
180
            return Ok(());
181
        }
182
        let mut uvs = texture_uvs(src, dst.size, texture.size(), src_transform);
183
        if texture.0.y_inverted {
184
            for uv in &mut uvs {
185
                uv[1] = 1.0 - uv[1];
186
            }
187
        }
188
        let (fb, uv) = to_framebuffer(dst, uvs, self.area, self.transform);
189
        let (clip, radii, ring) = self.clip_push();
190
        let push = Push {
191
            dst: self.ndc(fb),
192
            uv,
193
            color: [1.0, 1.0, 1.0, alpha],
194
            effect,
195
            clip,
196
            radii,
197
            ring,
198
        };
199
        self.ops.push(Op::Draw {
200
            kind,
201
            opaque: kind == Kind::Texture
202
                && alpha >= 1.0
203
                && self.clip.is_none()
204
                && texture.0.opaque(),
205
            texture: Some(texture.clone()),
206
            push,
207
            scissors,
208
        });
209
        Ok(())
210
    }
211
212
    fn ndc(&self, r: Rectangle<i32, Physical>) -> [f32; 4] {
213
        let fb = self.fb_size();
214
        let (w, h) = (fb.w as f32, fb.h as f32);
215
        [
216
            r.loc.x as f32 / w * 2.0 - 1.0,
217
            r.loc.y as f32 / h * 2.0 - 1.0,
218
            r.size.w as f32 / w * 2.0,
219
            r.size.h as f32 / h * 2.0,
220
        ]
221
    }
222
}
223
224
impl Frame for VulkanFrame<'_, '_> {
225
    type Error = Error;
226
    type TextureId = VulkanTexture;
227
228
    fn context_id(&self) -> ContextId<VulkanTexture> {
229
        self.renderer.context_id_ref().clone()
230
    }
231
232
    fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Error> {
233
        let rects: Vec<_> = at.iter().filter_map(|r| self.fb_rect(*r)).collect();
234
        if !rects.is_empty() {
235
            self.ops.push(Op::Clear {
236
                color: color.components(),
237
                rects,
238
            });
239
        }
240
        Ok(())
241
    }
242
243
    fn draw_solid(
244
        &mut self,
245
        dst: Rectangle<i32, Physical>,
246
        damage: &[Rectangle<i32, Physical>],
247
        color: Color32F,
248
    ) -> Result<(), Error> {
249
        let scissors = self.scissors(dst, damage);
250
        if scissors.is_empty() {
251
            return Ok(());
252
        }
253
        let (fb, _) = to_framebuffer(dst, [[0.0; 2]; 4], self.area, self.transform);
254
        let (clip, radii, ring) = self.clip_push();
255
        let push = Push {
256
            dst: self.ndc(fb),
257
            uv: [[0.0; 2]; 4],
258
            color: color.components(),
259
            effect: [0.0; 4],
260
            clip,
261
            radii,
262
            ring,
263
        };
264
        self.ops.push(Op::Draw {
265
            kind: Kind::Solid,
266
            opaque: color.a() >= 1.0 && self.clip.is_none(),
267
            texture: None,
268
            push,
269
            scissors,
270
        });
271
        Ok(())
272
    }
273
274
    fn render_texture_from_to(
275
        &mut self,
276
        texture: &VulkanTexture,
277
        src: Rectangle<f64, Buffer>,
278
        dst: Rectangle<i32, Physical>,
279
        damage: &[Rectangle<i32, Physical>],
280
        _opaque_regions: &[Rectangle<i32, Physical>],
281
        src_transform: Transform,
282
        alpha: f32,
283
    ) -> Result<(), Error> {
284
        self.draw_texture(
285
            texture,
286
            src,
287
            dst,
288
            damage,
289
            src_transform,
290
            alpha,
291
            Kind::Texture,
292
            [0.0; 4],
293
        )
294
    }
295
296
    fn transformation(&self) -> Transform {
297
        self.transform
298
    }
299
300
    fn output_size(&self) -> Size<i32, Physical> {
301
        self.output
302
    }
303
304
    fn wait(&mut self, sync: &SyncPoint) -> Result<(), Error> {
305
        sync.wait().map_err(|_| crate::error::wait_error())
306
    }
307
308
    fn finish(mut self) -> Result<SyncPoint, Error> {
309
        let ops = std::mem::take(&mut self.ops);
310
        self.renderer.submit(self.target, ops)
311
    }
312
}
313
314
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
315
pub enum Transition {
316
    Move = 0,
317
    Spawn = 1,
318
    Close = 2,
319
}
320
321
/// A window transition for the effect shader, with `progress` in [0, 1].
322
#[derive(Clone, Copy, Debug, PartialEq)]
323
pub struct Effect {
324
    pub kind: Transition,
325
    pub progress: f32,
326
}