atagen

monolith

public
Star0
Watch0
c0caa658decffac52092381ab1dc44ec373c3bd8
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/frame.rsRust321 lines9.3 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 uvs = texture_uvs(src, dst.size, texture.size(), src_transform);
183
        let (fb, uv) = to_framebuffer(dst, uvs, self.area, self.transform);
184
        let (clip, radii, ring) = self.clip_push();
185
        let push = Push {
186
            dst: self.ndc(fb),
187
            uv,
188
            color: [1.0, 1.0, 1.0, alpha],
189
            effect,
190
            clip,
191
            radii,
192
            ring,
193
        };
194
        self.ops.push(Op::Draw {
195
            kind,
196
            opaque: kind == Kind::Texture
197
                && alpha >= 1.0
198
                && self.clip.is_none()
199
                && texture.0.opaque(),
200
            texture: Some(texture.clone()),
201
            push,
202
            scissors,
203
        });
204
        Ok(())
205
    }
206
207
    fn ndc(&self, r: Rectangle<i32, Physical>) -> [f32; 4] {
208
        let fb = self.fb_size();
209
        let (w, h) = (fb.w as f32, fb.h as f32);
210
        [
211
            r.loc.x as f32 / w * 2.0 - 1.0,
212
            r.loc.y as f32 / h * 2.0 - 1.0,
213
            r.size.w as f32 / w * 2.0,
214
            r.size.h as f32 / h * 2.0,
215
        ]
216
    }
217
}
218
219
impl Frame for VulkanFrame<'_, '_> {
220
    type Error = Error;
221
    type TextureId = VulkanTexture;
222
223
    fn context_id(&self) -> ContextId<VulkanTexture> {
224
        self.renderer.context_id_ref().clone()
225
    }
226
227
    fn clear(&mut self, color: Color32F, at: &[Rectangle<i32, Physical>]) -> Result<(), Error> {
228
        let rects: Vec<_> = at.iter().filter_map(|r| self.fb_rect(*r)).collect();
229
        if !rects.is_empty() {
230
            self.ops.push(Op::Clear {
231
                color: color.components(),
232
                rects,
233
            });
234
        }
235
        Ok(())
236
    }
237
238
    fn draw_solid(
239
        &mut self,
240
        dst: Rectangle<i32, Physical>,
241
        damage: &[Rectangle<i32, Physical>],
242
        color: Color32F,
243
    ) -> Result<(), Error> {
244
        let scissors = self.scissors(dst, damage);
245
        if scissors.is_empty() {
246
            return Ok(());
247
        }
248
        let (fb, _) = to_framebuffer(dst, [[0.0; 2]; 4], self.area, self.transform);
249
        let (clip, radii, ring) = self.clip_push();
250
        let push = Push {
251
            dst: self.ndc(fb),
252
            uv: [[0.0; 2]; 4],
253
            color: color.components(),
254
            effect: [0.0; 4],
255
            clip,
256
            radii,
257
            ring,
258
        };
259
        self.ops.push(Op::Draw {
260
            kind: Kind::Solid,
261
            opaque: color.a() >= 1.0 && self.clip.is_none(),
262
            texture: None,
263
            push,
264
            scissors,
265
        });
266
        Ok(())
267
    }
268
269
    fn render_texture_from_to(
270
        &mut self,
271
        texture: &VulkanTexture,
272
        src: Rectangle<f64, Buffer>,
273
        dst: Rectangle<i32, Physical>,
274
        damage: &[Rectangle<i32, Physical>],
275
        _opaque_regions: &[Rectangle<i32, Physical>],
276
        src_transform: Transform,
277
        alpha: f32,
278
    ) -> Result<(), Error> {
279
        self.draw_texture(
280
            texture,
281
            src,
282
            dst,
283
            damage,
284
            src_transform,
285
            alpha,
286
            Kind::Texture,
287
            [0.0; 4],
288
        )
289
    }
290
291
    fn transformation(&self) -> Transform {
292
        self.transform
293
    }
294
295
    fn output_size(&self) -> Size<i32, Physical> {
296
        self.output
297
    }
298
299
    fn wait(&mut self, sync: &SyncPoint) -> Result<(), Error> {
300
        sync.wait().map_err(|_| Error::Interrupted)
301
    }
302
303
    fn finish(mut self) -> Result<SyncPoint, Error> {
304
        let ops = std::mem::take(&mut self.ops);
305
        self.renderer.submit(self.target, ops)
306
    }
307
}
308
309
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
310
pub enum Transition {
311
    Move = 0,
312
    Spawn = 1,
313
    Close = 2,
314
}
315
316
/// A window transition for the effect shader, with `progress` in [0, 1].
317
#[derive(Clone, Copy, Debug, PartialEq)]
318
pub struct Effect {
319
    pub kind: Transition,
320
    pub progress: f32,
321
}