atagen

monolith

public
Star0
Watch0
7de49c2aa70b317e35783f54619e08eff5326c7f
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/tests/golden.rsRust258 lines7.1 KB
1
//! Renders small scenes offscreen and checks pixels. Runs on whatever Vulkan
2
//! driver is available. `nix flake check` forces lavapipe.
3
4
use monolith_render_vk::{VulkanImage, VulkanRenderer, VulkanTexture};
5
use smithay::backend::allocator::Fourcc;
6
use smithay::backend::renderer::{
7
    Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer, TextureFilter,
8
};
9
use smithay::utils::{Physical, Rectangle, Size, Transform};
10
11
const W: i32 = 64;
12
const H: i32 = 64;
13
14
type Pixel = [u8; 4];
15
16
const RED: Pixel = [255, 0, 0, 255];
17
const GREEN: Pixel = [0, 255, 0, 255];
18
const BLUE: Pixel = [0, 0, 255, 255];
19
const WHITE: Pixel = [255, 255, 255, 255];
20
const BLACK: Pixel = [0, 0, 0, 255];
21
22
struct Scene {
23
    renderer: VulkanRenderer,
24
    image: VulkanImage,
25
}
26
27
impl Scene {
28
    fn new() -> Self {
29
        let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device");
30
        let image = renderer
31
            .create_buffer(Fourcc::Argb8888, (W, H).into())
32
            .unwrap();
33
        Self { renderer, image }
34
    }
35
36
    fn draw(
37
        &mut self,
38
        transform: Transform,
39
        f: impl FnOnce(&mut monolith_render_vk::VulkanFrame<'_, '_>),
40
    ) {
41
        let mut target = self.renderer.bind(&mut self.image).unwrap();
42
        let mut frame = self
43
            .renderer
44
            .render(&mut target, (W, H).into(), transform)
45
            .unwrap();
46
        f(&mut frame);
47
        frame.finish().unwrap().wait().unwrap();
48
    }
49
50
    /// Pixels as RGBA, whatever the image's byte order.
51
    fn pixels(&mut self) -> Vec<Pixel> {
52
        let target = self.renderer.bind(&mut self.image).unwrap();
53
        let mapping = self
54
            .renderer
55
            .copy_framebuffer(
56
                &target,
57
                Rectangle::from_size((W, H).into()),
58
                Fourcc::Abgr8888,
59
            )
60
            .unwrap();
61
        self.renderer
62
            .map_texture(&mapping)
63
            .unwrap()
64
            .as_chunks::<4>()
65
            .0
66
            .to_vec()
67
    }
68
69
    fn texture(&mut self, size: (i32, i32), rgba: &[Pixel]) -> VulkanTexture {
70
        let bytes: Vec<u8> = rgba.iter().flatten().copied().collect();
71
        self.renderer
72
            .import_memory(&bytes, Fourcc::Abgr8888, size.into(), false)
73
            .unwrap()
74
    }
75
}
76
77
fn at(pixels: &[Pixel], x: i32, y: i32) -> Pixel {
78
    pixels[(y * W + x) as usize]
79
}
80
81
fn full() -> Rectangle<i32, Physical> {
82
    Rectangle::from_size((W, H).into())
83
}
84
85
fn rgba(p: Pixel) -> Color32F {
86
    p.map(|c| f32::from(c) / 255.0).into()
87
}
88
89
#[test]
90
fn clears_to_a_colour() {
91
    let mut s = Scene::new();
92
    s.draw(Transform::Normal, |f| {
93
        f.clear(rgba(RED), &[full()]).unwrap()
94
    });
95
    let px = s.pixels();
96
    assert!(px.iter().all(|&p| p == RED));
97
}
98
99
#[test]
100
fn solid_fill_respects_its_rectangle() {
101
    let mut s = Scene::new();
102
    s.draw(Transform::Normal, |f| {
103
        f.clear(rgba(BLACK), &[full()]).unwrap();
104
        let dst = Rectangle::new((16, 16).into(), (32, 32).into());
105
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
106
            .unwrap();
107
    });
108
    let px = s.pixels();
109
    assert_eq!(at(&px, 16, 16), GREEN);
110
    assert_eq!(at(&px, 47, 47), GREEN);
111
    assert_eq!(at(&px, 15, 16), BLACK);
112
    assert_eq!(at(&px, 48, 47), BLACK);
113
}
114
115
#[test]
116
fn damage_limits_drawing() {
117
    let mut s = Scene::new();
118
    s.draw(Transform::Normal, |f| {
119
        f.clear(rgba(BLACK), &[full()]).unwrap();
120
        let damage = Rectangle::new((0, 0).into(), (8, 8).into());
121
        f.draw_solid(full(), &[damage], rgba(BLUE)).unwrap();
122
    });
123
    let px = s.pixels();
124
    assert_eq!(at(&px, 7, 7), BLUE);
125
    assert_eq!(at(&px, 8, 8), BLACK);
126
}
127
128
#[test]
129
fn partial_clear_keeps_previous_contents() {
130
    let mut s = Scene::new();
131
    s.draw(Transform::Normal, |f| {
132
        f.clear(rgba(RED), &[full()]).unwrap()
133
    });
134
    s.draw(Transform::Normal, |f| {
135
        f.clear(
136
            rgba(GREEN),
137
            &[Rectangle::new((0, 0).into(), (32, 64).into())],
138
        )
139
        .unwrap()
140
    });
141
    let px = s.pixels();
142
    assert_eq!(at(&px, 0, 0), GREEN);
143
    assert_eq!(at(&px, 63, 0), RED);
144
}
145
146
#[test]
147
fn textures_sample_with_nearest_filtering() {
148
    let mut s = Scene::new();
149
    s.renderer.upscale_filter(TextureFilter::Nearest).unwrap();
150
    let tex = s.texture((2, 2), &[RED, GREEN, BLUE, WHITE]);
151
    s.draw(Transform::Normal, |f| {
152
        f.render_texture_from_to(
153
            &tex,
154
            Rectangle::from_size((2.0, 2.0).into()),
155
            full(),
156
            &[full()],
157
            &[],
158
            Transform::Normal,
159
            1.0,
160
        )
161
        .unwrap();
162
    });
163
    let px = s.pixels();
164
    assert_eq!(at(&px, 8, 8), RED);
165
    assert_eq!(at(&px, 56, 8), GREEN);
166
    assert_eq!(at(&px, 8, 56), BLUE);
167
    assert_eq!(at(&px, 56, 56), WHITE);
168
}
169
170
#[test]
171
fn alpha_blends_premultiplied() {
172
    let mut s = Scene::new();
173
    let tex = s.texture((1, 1), &[WHITE]);
174
    s.draw(Transform::Normal, |f| {
175
        f.clear(rgba(BLACK), &[full()]).unwrap();
176
        f.render_texture_from_to(
177
            &tex,
178
            Rectangle::from_size((1.0, 1.0).into()),
179
            full(),
180
            &[full()],
181
            &[],
182
            Transform::Normal,
183
            0.5,
184
        )
185
        .unwrap();
186
    });
187
    let p = at(&s.pixels(), 32, 32);
188
    assert!(
189
        (127..=129).contains(&p[0]) && p[0] == p[1] && p[1] == p[2],
190
        "{p:?}"
191
    );
192
}
193
194
#[test]
195
fn output_transform_moves_content() {
196
    let mut s = Scene::new();
197
    s.draw(Transform::_180, |f| {
198
        f.clear(rgba(BLACK), &[full()]).unwrap();
199
        let dst = Rectangle::new((0, 0).into(), (16, 16).into());
200
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
201
            .unwrap();
202
    });
203
    let px = s.pixels();
204
    assert_eq!(at(&px, 0, 0), BLACK);
205
    assert_eq!(at(&px, 63, 63), GREEN);
206
}
207
208
#[test]
209
fn offscreen_images_can_be_sampled() {
210
    let mut s = Scene::new();
211
    let mut inner = s
212
        .renderer
213
        .create_buffer(Fourcc::Argb8888, Size::from((4, 4)))
214
        .unwrap();
215
    {
216
        let mut t = s.renderer.bind(&mut inner).unwrap();
217
        let mut f = s
218
            .renderer
219
            .render(&mut t, (4, 4).into(), Transform::Normal)
220
            .unwrap();
221
        f.clear(rgba(BLUE), &[Rectangle::from_size((4, 4).into())])
222
            .unwrap();
223
        f.finish().unwrap().wait().unwrap();
224
    }
225
    let tex = inner.texture();
226
    s.draw(Transform::Normal, |f| {
227
        f.render_texture_from_to(
228
            &tex,
229
            Rectangle::from_size((4.0, 4.0).into()),
230
            full(),
231
            &[full()],
232
            &[],
233
            Transform::Normal,
234
            1.0,
235
        )
236
        .unwrap();
237
    });
238
    assert_eq!(at(&s.pixels(), 32, 32), BLUE);
239
}
240
241
#[test]
242
fn reports_the_device() {
243
    let r = VulkanRenderer::new().unwrap();
244
    eprintln!("rendering on {}", r.device_name());
245
    assert!(!r.device_name().is_empty());
246
}
247
248
#[test]
249
fn solid_fill_in_the_corner_keeps_its_colour() {
250
    let mut s = Scene::new();
251
    s.draw(Transform::Normal, |f| {
252
        f.clear(rgba(BLACK), &[full()]).unwrap();
253
        let dst = Rectangle::new((48, 48).into(), (16, 16).into());
254
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(RED))
255
            .unwrap();
256
    });
257
    assert_eq!(at(&s.pixels(), 56, 56), RED);
258
}