atagen

monolith

public
Star0
Watch0
82c94edb8c503ce7c87c5fbd1a00410265cbad05
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/render-vk/tests/golden.rs

824bf02b6e55atagen2026-10-16 09:15 UTC1
//! Renders small scenes offscreen and checks pixels. Runs on whatever Vulkan
824bf02b6e55atagen2026-10-16 09:15 UTC2
//! driver is available. `nix flake check` forces lavapipe.
824bf02b6e55atagen2026-10-16 09:15 UTC3
824bf02b6e55atagen2026-10-16 09:15 UTC4
use monolith_render_vk::{VulkanImage, VulkanRenderer, VulkanTexture};
824bf02b6e55atagen2026-10-16 09:15 UTC5
use smithay::backend::allocator::Fourcc;
824bf02b6e55atagen2026-10-16 09:15 UTC6
use smithay::backend::renderer::{
5a760f013a02atagen2026-10-16 09:55 UTC7
    Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer,
824bf02b6e55atagen2026-10-16 09:15 UTC8
};
5a760f013a02atagen2026-10-16 09:55 UTC9
use smithay::utils::{Physical, Rectangle, Transform};
824bf02b6e55atagen2026-10-16 09:15 UTC10
824bf02b6e55atagen2026-10-16 09:15 UTC11
const W: i32 = 64;
824bf02b6e55atagen2026-10-16 09:15 UTC12
const H: i32 = 64;
824bf02b6e55atagen2026-10-16 09:15 UTC13
824bf02b6e55atagen2026-10-16 09:15 UTC14
type Pixel = [u8; 4];
824bf02b6e55atagen2026-10-16 09:15 UTC15
824bf02b6e55atagen2026-10-16 09:15 UTC16
const RED: Pixel = [255, 0, 0, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC17
const GREEN: Pixel = [0, 255, 0, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC18
const WHITE: Pixel = [255, 255, 255, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC19
const BLACK: Pixel = [0, 0, 0, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC20
824bf02b6e55atagen2026-10-16 09:15 UTC21
struct Scene {
824bf02b6e55atagen2026-10-16 09:15 UTC22
    renderer: VulkanRenderer,
824bf02b6e55atagen2026-10-16 09:15 UTC23
    image: VulkanImage,
824bf02b6e55atagen2026-10-16 09:15 UTC24
}
824bf02b6e55atagen2026-10-16 09:15 UTC25
824bf02b6e55atagen2026-10-16 09:15 UTC26
impl Scene {
824bf02b6e55atagen2026-10-16 09:15 UTC27
    fn new() -> Self {
824bf02b6e55atagen2026-10-16 09:15 UTC28
        let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device");
824bf02b6e55atagen2026-10-16 09:15 UTC29
        let image = renderer
824bf02b6e55atagen2026-10-16 09:15 UTC30
            .create_buffer(Fourcc::Argb8888, (W, H).into())
824bf02b6e55atagen2026-10-16 09:15 UTC31
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC32
        Self { renderer, image }
824bf02b6e55atagen2026-10-16 09:15 UTC33
    }
824bf02b6e55atagen2026-10-16 09:15 UTC34
824bf02b6e55atagen2026-10-16 09:15 UTC35
    fn draw(
824bf02b6e55atagen2026-10-16 09:15 UTC36
        &mut self,
824bf02b6e55atagen2026-10-16 09:15 UTC37
        transform: Transform,
824bf02b6e55atagen2026-10-16 09:15 UTC38
        f: impl FnOnce(&mut monolith_render_vk::VulkanFrame<'_, '_>),
824bf02b6e55atagen2026-10-16 09:15 UTC39
    ) {
824bf02b6e55atagen2026-10-16 09:15 UTC40
        let mut target = self.renderer.bind(&mut self.image).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC41
        let mut frame = self
824bf02b6e55atagen2026-10-16 09:15 UTC42
            .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC43
            .render(&mut target, (W, H).into(), transform)
824bf02b6e55atagen2026-10-16 09:15 UTC44
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC45
        f(&mut frame);
824bf02b6e55atagen2026-10-16 09:15 UTC46
        frame.finish().unwrap().wait().unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC47
    }
824bf02b6e55atagen2026-10-16 09:15 UTC48
824bf02b6e55atagen2026-10-16 09:15 UTC49
    /// Pixels as RGBA, whatever the image's byte order.
824bf02b6e55atagen2026-10-16 09:15 UTC50
    fn pixels(&mut self) -> Vec<Pixel> {
824bf02b6e55atagen2026-10-16 09:15 UTC51
        let target = self.renderer.bind(&mut self.image).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC52
        let mapping = self
824bf02b6e55atagen2026-10-16 09:15 UTC53
            .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC54
            .copy_framebuffer(
824bf02b6e55atagen2026-10-16 09:15 UTC55
                &target,
824bf02b6e55atagen2026-10-16 09:15 UTC56
                Rectangle::from_size((W, H).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC57
                Fourcc::Abgr8888,
824bf02b6e55atagen2026-10-16 09:15 UTC58
            )
824bf02b6e55atagen2026-10-16 09:15 UTC59
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC60
        self.renderer
824bf02b6e55atagen2026-10-16 09:15 UTC61
            .map_texture(&mapping)
824bf02b6e55atagen2026-10-16 09:15 UTC62
            .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC63
            .as_chunks::<4>()
824bf02b6e55atagen2026-10-16 09:15 UTC64
            .0
824bf02b6e55atagen2026-10-16 09:15 UTC65
            .to_vec()
824bf02b6e55atagen2026-10-16 09:15 UTC66
    }
824bf02b6e55atagen2026-10-16 09:15 UTC67
824bf02b6e55atagen2026-10-16 09:15 UTC68
    fn texture(&mut self, size: (i32, i32), rgba: &[Pixel]) -> VulkanTexture {
824bf02b6e55atagen2026-10-16 09:15 UTC69
        let bytes: Vec<u8> = rgba.iter().flatten().copied().collect();
824bf02b6e55atagen2026-10-16 09:15 UTC70
        self.renderer
824bf02b6e55atagen2026-10-16 09:15 UTC71
            .import_memory(&bytes, Fourcc::Abgr8888, size.into(), false)
824bf02b6e55atagen2026-10-16 09:15 UTC72
            .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC73
    }
824bf02b6e55atagen2026-10-16 09:15 UTC74
}
824bf02b6e55atagen2026-10-16 09:15 UTC75
824bf02b6e55atagen2026-10-16 09:15 UTC76
fn at(pixels: &[Pixel], x: i32, y: i32) -> Pixel {
824bf02b6e55atagen2026-10-16 09:15 UTC77
    pixels[(y * W + x) as usize]
824bf02b6e55atagen2026-10-16 09:15 UTC78
}
824bf02b6e55atagen2026-10-16 09:15 UTC79
824bf02b6e55atagen2026-10-16 09:15 UTC80
fn full() -> Rectangle<i32, Physical> {
824bf02b6e55atagen2026-10-16 09:15 UTC81
    Rectangle::from_size((W, H).into())
824bf02b6e55atagen2026-10-16 09:15 UTC82
}
824bf02b6e55atagen2026-10-16 09:15 UTC83
824bf02b6e55atagen2026-10-16 09:15 UTC84
fn rgba(p: Pixel) -> Color32F {
824bf02b6e55atagen2026-10-16 09:15 UTC85
    p.map(|c| f32::from(c) / 255.0).into()
824bf02b6e55atagen2026-10-16 09:15 UTC86
}
824bf02b6e55atagen2026-10-16 09:15 UTC87
824bf02b6e55atagen2026-10-16 09:15 UTC88
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC89
fn partial_clear_keeps_previous_contents() {
824bf02b6e55atagen2026-10-16 09:15 UTC90
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC91
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC92
        f.clear(rgba(RED), &[full()]).unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC93
    });
824bf02b6e55atagen2026-10-16 09:15 UTC94
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC95
        f.clear(
824bf02b6e55atagen2026-10-16 09:15 UTC96
            rgba(GREEN),
824bf02b6e55atagen2026-10-16 09:15 UTC97
            &[Rectangle::new((0, 0).into(), (32, 64).into())],
824bf02b6e55atagen2026-10-16 09:15 UTC98
        )
824bf02b6e55atagen2026-10-16 09:15 UTC99
        .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC100
    });
824bf02b6e55atagen2026-10-16 09:15 UTC101
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC102
    assert_eq!(at(&px, 0, 0), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC103
    assert_eq!(at(&px, 63, 0), RED);
824bf02b6e55atagen2026-10-16 09:15 UTC104
}
824bf02b6e55atagen2026-10-16 09:15 UTC105
824bf02b6e55atagen2026-10-16 09:15 UTC106
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC107
fn alpha_blends_premultiplied() {
824bf02b6e55atagen2026-10-16 09:15 UTC108
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC109
    let tex = s.texture((1, 1), &[WHITE]);
824bf02b6e55atagen2026-10-16 09:15 UTC110
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC111
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC112
        f.render_texture_from_to(
824bf02b6e55atagen2026-10-16 09:15 UTC113
            &tex,
824bf02b6e55atagen2026-10-16 09:15 UTC114
            Rectangle::from_size((1.0, 1.0).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC115
            full(),
824bf02b6e55atagen2026-10-16 09:15 UTC116
            &[full()],
824bf02b6e55atagen2026-10-16 09:15 UTC117
            &[],
824bf02b6e55atagen2026-10-16 09:15 UTC118
            Transform::Normal,
824bf02b6e55atagen2026-10-16 09:15 UTC119
            0.5,
824bf02b6e55atagen2026-10-16 09:15 UTC120
        )
824bf02b6e55atagen2026-10-16 09:15 UTC121
        .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC122
    });
824bf02b6e55atagen2026-10-16 09:15 UTC123
    let p = at(&s.pixels(), 32, 32);
824bf02b6e55atagen2026-10-16 09:15 UTC124
    assert!(
824bf02b6e55atagen2026-10-16 09:15 UTC125
        (127..=129).contains(&p[0]) && p[0] == p[1] && p[1] == p[2],
824bf02b6e55atagen2026-10-16 09:15 UTC126
        "{p:?}"
824bf02b6e55atagen2026-10-16 09:15 UTC127
    );
824bf02b6e55atagen2026-10-16 09:15 UTC128
}
824bf02b6e55atagen2026-10-16 09:15 UTC129
824bf02b6e55atagen2026-10-16 09:15 UTC130
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC131
fn output_transform_moves_content() {
824bf02b6e55atagen2026-10-16 09:15 UTC132
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC133
    s.draw(Transform::_180, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC134
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC135
        let dst = Rectangle::new((0, 0).into(), (16, 16).into());
824bf02b6e55atagen2026-10-16 09:15 UTC136
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
824bf02b6e55atagen2026-10-16 09:15 UTC137
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC138
    });
824bf02b6e55atagen2026-10-16 09:15 UTC139
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC140
    assert_eq!(at(&px, 0, 0), BLACK);
824bf02b6e55atagen2026-10-16 09:15 UTC141
    assert_eq!(at(&px, 63, 63), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC142
}
824bf02b6e55atagen2026-10-16 09:15 UTC143
824bf02b6e55atagen2026-10-16 09:15 UTC144
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC145
fn solid_fill_in_the_corner_keeps_its_colour() {
824bf02b6e55atagen2026-10-16 09:15 UTC146
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC147
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC148
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC149
        let dst = Rectangle::new((48, 48).into(), (16, 16).into());
824bf02b6e55atagen2026-10-16 09:15 UTC150
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(RED))
824bf02b6e55atagen2026-10-16 09:15 UTC151
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC152
    });
824bf02b6e55atagen2026-10-16 09:15 UTC153
    assert_eq!(at(&s.pixels(), 56, 56), RED);
824bf02b6e55atagen2026-10-16 09:15 UTC154
}