atagen

monolith

public
Star0
Watch0
92d5331c9dd92a20a90d9957f26f6036da59ce8b
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/tests/golden.rsRust154 lines4.2 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,
8
};
9
use smithay::utils::{Physical, Rectangle, 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 WHITE: Pixel = [255, 255, 255, 255];
19
const BLACK: Pixel = [0, 0, 0, 255];
20
21
struct Scene {
22
    renderer: VulkanRenderer,
23
    image: VulkanImage,
24
}
25
26
impl Scene {
27
    fn new() -> Self {
28
        let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device");
29
        let image = renderer
30
            .create_buffer(Fourcc::Argb8888, (W, H).into())
31
            .unwrap();
32
        Self { renderer, image }
33
    }
34
35
    fn draw(
36
        &mut self,
37
        transform: Transform,
38
        f: impl FnOnce(&mut monolith_render_vk::VulkanFrame<'_, '_>),
39
    ) {
40
        let mut target = self.renderer.bind(&mut self.image).unwrap();
41
        let mut frame = self
42
            .renderer
43
            .render(&mut target, (W, H).into(), transform)
44
            .unwrap();
45
        f(&mut frame);
46
        frame.finish().unwrap().wait().unwrap();
47
    }
48
49
    /// Pixels as RGBA, whatever the image's byte order.
50
    fn pixels(&mut self) -> Vec<Pixel> {
51
        let target = self.renderer.bind(&mut self.image).unwrap();
52
        let mapping = self
53
            .renderer
54
            .copy_framebuffer(
55
                &target,
56
                Rectangle::from_size((W, H).into()),
57
                Fourcc::Abgr8888,
58
            )
59
            .unwrap();
60
        self.renderer
61
            .map_texture(&mapping)
62
            .unwrap()
63
            .as_chunks::<4>()
64
            .0
65
            .to_vec()
66
    }
67
68
    fn texture(&mut self, size: (i32, i32), rgba: &[Pixel]) -> VulkanTexture {
69
        let bytes: Vec<u8> = rgba.iter().flatten().copied().collect();
70
        self.renderer
71
            .import_memory(&bytes, Fourcc::Abgr8888, size.into(), false)
72
            .unwrap()
73
    }
74
}
75
76
fn at(pixels: &[Pixel], x: i32, y: i32) -> Pixel {
77
    pixels[(y * W + x) as usize]
78
}
79
80
fn full() -> Rectangle<i32, Physical> {
81
    Rectangle::from_size((W, H).into())
82
}
83
84
fn rgba(p: Pixel) -> Color32F {
85
    p.map(|c| f32::from(c) / 255.0).into()
86
}
87
88
#[test]
89
fn partial_clear_keeps_previous_contents() {
90
    let mut s = Scene::new();
91
    s.draw(Transform::Normal, |f| {
92
        f.clear(rgba(RED), &[full()]).unwrap()
93
    });
94
    s.draw(Transform::Normal, |f| {
95
        f.clear(
96
            rgba(GREEN),
97
            &[Rectangle::new((0, 0).into(), (32, 64).into())],
98
        )
99
        .unwrap()
100
    });
101
    let px = s.pixels();
102
    assert_eq!(at(&px, 0, 0), GREEN);
103
    assert_eq!(at(&px, 63, 0), RED);
104
}
105
106
#[test]
107
fn alpha_blends_premultiplied() {
108
    let mut s = Scene::new();
109
    let tex = s.texture((1, 1), &[WHITE]);
110
    s.draw(Transform::Normal, |f| {
111
        f.clear(rgba(BLACK), &[full()]).unwrap();
112
        f.render_texture_from_to(
113
            &tex,
114
            Rectangle::from_size((1.0, 1.0).into()),
115
            full(),
116
            &[full()],
117
            &[],
118
            Transform::Normal,
119
            0.5,
120
        )
121
        .unwrap();
122
    });
123
    let p = at(&s.pixels(), 32, 32);
124
    assert!(
125
        (127..=129).contains(&p[0]) && p[0] == p[1] && p[1] == p[2],
126
        "{p:?}"
127
    );
128
}
129
130
#[test]
131
fn output_transform_moves_content() {
132
    let mut s = Scene::new();
133
    s.draw(Transform::_180, |f| {
134
        f.clear(rgba(BLACK), &[full()]).unwrap();
135
        let dst = Rectangle::new((0, 0).into(), (16, 16).into());
136
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
137
            .unwrap();
138
    });
139
    let px = s.pixels();
140
    assert_eq!(at(&px, 0, 0), BLACK);
141
    assert_eq!(at(&px, 63, 63), GREEN);
142
}
143
144
#[test]
145
fn solid_fill_in_the_corner_keeps_its_colour() {
146
    let mut s = Scene::new();
147
    s.draw(Transform::Normal, |f| {
148
        f.clear(rgba(BLACK), &[full()]).unwrap();
149
        let dst = Rectangle::new((48, 48).into(), (16, 16).into());
150
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(RED))
151
            .unwrap();
152
    });
153
    assert_eq!(at(&s.pixels(), 56, 56), RED);
154
}