atagen

monolith

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