atagen

monolith

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