atagen

monolith

public
Star0
Watch0
d4a64ce87164783b0e497f58e15da3c6efa3c95b
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::{
824bf02b6e55atagen2026-10-16 09:15 UTC7
    Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer, TextureFilter,
824bf02b6e55atagen2026-10-16 09:15 UTC8
};
824bf02b6e55atagen2026-10-16 09:15 UTC9
use smithay::utils::{Physical, Rectangle, Size, 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 BLUE: Pixel = [0, 0, 255, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC19
const WHITE: Pixel = [255, 255, 255, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC20
const BLACK: Pixel = [0, 0, 0, 255];
824bf02b6e55atagen2026-10-16 09:15 UTC21
824bf02b6e55atagen2026-10-16 09:15 UTC22
struct Scene {
824bf02b6e55atagen2026-10-16 09:15 UTC23
    renderer: VulkanRenderer,
824bf02b6e55atagen2026-10-16 09:15 UTC24
    image: VulkanImage,
824bf02b6e55atagen2026-10-16 09:15 UTC25
}
824bf02b6e55atagen2026-10-16 09:15 UTC26
824bf02b6e55atagen2026-10-16 09:15 UTC27
impl Scene {
824bf02b6e55atagen2026-10-16 09:15 UTC28
    fn new() -> Self {
824bf02b6e55atagen2026-10-16 09:15 UTC29
        let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device");
824bf02b6e55atagen2026-10-16 09:15 UTC30
        let image = renderer
824bf02b6e55atagen2026-10-16 09:15 UTC31
            .create_buffer(Fourcc::Argb8888, (W, H).into())
824bf02b6e55atagen2026-10-16 09:15 UTC32
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC33
        Self { renderer, image }
824bf02b6e55atagen2026-10-16 09:15 UTC34
    }
824bf02b6e55atagen2026-10-16 09:15 UTC35
824bf02b6e55atagen2026-10-16 09:15 UTC36
    fn draw(
824bf02b6e55atagen2026-10-16 09:15 UTC37
        &mut self,
824bf02b6e55atagen2026-10-16 09:15 UTC38
        transform: Transform,
824bf02b6e55atagen2026-10-16 09:15 UTC39
        f: impl FnOnce(&mut monolith_render_vk::VulkanFrame<'_, '_>),
824bf02b6e55atagen2026-10-16 09:15 UTC40
    ) {
824bf02b6e55atagen2026-10-16 09:15 UTC41
        let mut target = self.renderer.bind(&mut self.image).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC42
        let mut frame = self
824bf02b6e55atagen2026-10-16 09:15 UTC43
            .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC44
            .render(&mut target, (W, H).into(), transform)
824bf02b6e55atagen2026-10-16 09:15 UTC45
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC46
        f(&mut frame);
824bf02b6e55atagen2026-10-16 09:15 UTC47
        frame.finish().unwrap().wait().unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC48
    }
824bf02b6e55atagen2026-10-16 09:15 UTC49
824bf02b6e55atagen2026-10-16 09:15 UTC50
    /// Pixels as RGBA, whatever the image's byte order.
824bf02b6e55atagen2026-10-16 09:15 UTC51
    fn pixels(&mut self) -> Vec<Pixel> {
824bf02b6e55atagen2026-10-16 09:15 UTC52
        let target = self.renderer.bind(&mut self.image).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC53
        let mapping = self
824bf02b6e55atagen2026-10-16 09:15 UTC54
            .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC55
            .copy_framebuffer(
824bf02b6e55atagen2026-10-16 09:15 UTC56
                &target,
824bf02b6e55atagen2026-10-16 09:15 UTC57
                Rectangle::from_size((W, H).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC58
                Fourcc::Abgr8888,
824bf02b6e55atagen2026-10-16 09:15 UTC59
            )
824bf02b6e55atagen2026-10-16 09:15 UTC60
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC61
        self.renderer
824bf02b6e55atagen2026-10-16 09:15 UTC62
            .map_texture(&mapping)
824bf02b6e55atagen2026-10-16 09:15 UTC63
            .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC64
            .as_chunks::<4>()
824bf02b6e55atagen2026-10-16 09:15 UTC65
            .0
824bf02b6e55atagen2026-10-16 09:15 UTC66
            .to_vec()
824bf02b6e55atagen2026-10-16 09:15 UTC67
    }
824bf02b6e55atagen2026-10-16 09:15 UTC68
824bf02b6e55atagen2026-10-16 09:15 UTC69
    fn texture(&mut self, size: (i32, i32), rgba: &[Pixel]) -> VulkanTexture {
824bf02b6e55atagen2026-10-16 09:15 UTC70
        let bytes: Vec<u8> = rgba.iter().flatten().copied().collect();
824bf02b6e55atagen2026-10-16 09:15 UTC71
        self.renderer
824bf02b6e55atagen2026-10-16 09:15 UTC72
            .import_memory(&bytes, Fourcc::Abgr8888, size.into(), false)
824bf02b6e55atagen2026-10-16 09:15 UTC73
            .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC74
    }
824bf02b6e55atagen2026-10-16 09:15 UTC75
}
824bf02b6e55atagen2026-10-16 09:15 UTC76
824bf02b6e55atagen2026-10-16 09:15 UTC77
fn at(pixels: &[Pixel], x: i32, y: i32) -> Pixel {
824bf02b6e55atagen2026-10-16 09:15 UTC78
    pixels[(y * W + x) as usize]
824bf02b6e55atagen2026-10-16 09:15 UTC79
}
824bf02b6e55atagen2026-10-16 09:15 UTC80
824bf02b6e55atagen2026-10-16 09:15 UTC81
fn full() -> Rectangle<i32, Physical> {
824bf02b6e55atagen2026-10-16 09:15 UTC82
    Rectangle::from_size((W, H).into())
824bf02b6e55atagen2026-10-16 09:15 UTC83
}
824bf02b6e55atagen2026-10-16 09:15 UTC84
824bf02b6e55atagen2026-10-16 09:15 UTC85
fn rgba(p: Pixel) -> Color32F {
824bf02b6e55atagen2026-10-16 09:15 UTC86
    p.map(|c| f32::from(c) / 255.0).into()
824bf02b6e55atagen2026-10-16 09:15 UTC87
}
824bf02b6e55atagen2026-10-16 09:15 UTC88
824bf02b6e55atagen2026-10-16 09:15 UTC89
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC90
fn clears_to_a_colour() {
824bf02b6e55atagen2026-10-16 09:15 UTC91
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC92
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC93
        f.clear(rgba(RED), &[full()]).unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC94
    });
824bf02b6e55atagen2026-10-16 09:15 UTC95
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC96
    assert!(px.iter().all(|&p| p == RED));
824bf02b6e55atagen2026-10-16 09:15 UTC97
}
824bf02b6e55atagen2026-10-16 09:15 UTC98
824bf02b6e55atagen2026-10-16 09:15 UTC99
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC100
fn solid_fill_respects_its_rectangle() {
824bf02b6e55atagen2026-10-16 09:15 UTC101
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC102
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC103
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC104
        let dst = Rectangle::new((16, 16).into(), (32, 32).into());
824bf02b6e55atagen2026-10-16 09:15 UTC105
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
824bf02b6e55atagen2026-10-16 09:15 UTC106
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC107
    });
824bf02b6e55atagen2026-10-16 09:15 UTC108
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC109
    assert_eq!(at(&px, 16, 16), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC110
    assert_eq!(at(&px, 47, 47), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC111
    assert_eq!(at(&px, 15, 16), BLACK);
824bf02b6e55atagen2026-10-16 09:15 UTC112
    assert_eq!(at(&px, 48, 47), BLACK);
824bf02b6e55atagen2026-10-16 09:15 UTC113
}
824bf02b6e55atagen2026-10-16 09:15 UTC114
824bf02b6e55atagen2026-10-16 09:15 UTC115
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC116
fn damage_limits_drawing() {
824bf02b6e55atagen2026-10-16 09:15 UTC117
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC118
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC119
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC120
        let damage = Rectangle::new((0, 0).into(), (8, 8).into());
824bf02b6e55atagen2026-10-16 09:15 UTC121
        f.draw_solid(full(), &[damage], rgba(BLUE)).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC122
    });
824bf02b6e55atagen2026-10-16 09:15 UTC123
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC124
    assert_eq!(at(&px, 7, 7), BLUE);
824bf02b6e55atagen2026-10-16 09:15 UTC125
    assert_eq!(at(&px, 8, 8), BLACK);
824bf02b6e55atagen2026-10-16 09:15 UTC126
}
824bf02b6e55atagen2026-10-16 09:15 UTC127
824bf02b6e55atagen2026-10-16 09:15 UTC128
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC129
fn partial_clear_keeps_previous_contents() {
824bf02b6e55atagen2026-10-16 09:15 UTC130
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC131
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC132
        f.clear(rgba(RED), &[full()]).unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC133
    });
824bf02b6e55atagen2026-10-16 09:15 UTC134
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC135
        f.clear(
824bf02b6e55atagen2026-10-16 09:15 UTC136
            rgba(GREEN),
824bf02b6e55atagen2026-10-16 09:15 UTC137
            &[Rectangle::new((0, 0).into(), (32, 64).into())],
824bf02b6e55atagen2026-10-16 09:15 UTC138
        )
824bf02b6e55atagen2026-10-16 09:15 UTC139
        .unwrap()
824bf02b6e55atagen2026-10-16 09:15 UTC140
    });
824bf02b6e55atagen2026-10-16 09:15 UTC141
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC142
    assert_eq!(at(&px, 0, 0), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC143
    assert_eq!(at(&px, 63, 0), RED);
824bf02b6e55atagen2026-10-16 09:15 UTC144
}
824bf02b6e55atagen2026-10-16 09:15 UTC145
824bf02b6e55atagen2026-10-16 09:15 UTC146
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC147
fn textures_sample_with_nearest_filtering() {
824bf02b6e55atagen2026-10-16 09:15 UTC148
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC149
    s.renderer.upscale_filter(TextureFilter::Nearest).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC150
    let tex = s.texture((2, 2), &[RED, GREEN, BLUE, WHITE]);
824bf02b6e55atagen2026-10-16 09:15 UTC151
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC152
        f.render_texture_from_to(
824bf02b6e55atagen2026-10-16 09:15 UTC153
            &tex,
824bf02b6e55atagen2026-10-16 09:15 UTC154
            Rectangle::from_size((2.0, 2.0).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC155
            full(),
824bf02b6e55atagen2026-10-16 09:15 UTC156
            &[full()],
824bf02b6e55atagen2026-10-16 09:15 UTC157
            &[],
824bf02b6e55atagen2026-10-16 09:15 UTC158
            Transform::Normal,
824bf02b6e55atagen2026-10-16 09:15 UTC159
            1.0,
824bf02b6e55atagen2026-10-16 09:15 UTC160
        )
824bf02b6e55atagen2026-10-16 09:15 UTC161
        .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC162
    });
824bf02b6e55atagen2026-10-16 09:15 UTC163
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC164
    assert_eq!(at(&px, 8, 8), RED);
824bf02b6e55atagen2026-10-16 09:15 UTC165
    assert_eq!(at(&px, 56, 8), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC166
    assert_eq!(at(&px, 8, 56), BLUE);
824bf02b6e55atagen2026-10-16 09:15 UTC167
    assert_eq!(at(&px, 56, 56), WHITE);
824bf02b6e55atagen2026-10-16 09:15 UTC168
}
824bf02b6e55atagen2026-10-16 09:15 UTC169
824bf02b6e55atagen2026-10-16 09:15 UTC170
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC171
fn alpha_blends_premultiplied() {
824bf02b6e55atagen2026-10-16 09:15 UTC172
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC173
    let tex = s.texture((1, 1), &[WHITE]);
824bf02b6e55atagen2026-10-16 09:15 UTC174
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC175
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC176
        f.render_texture_from_to(
824bf02b6e55atagen2026-10-16 09:15 UTC177
            &tex,
824bf02b6e55atagen2026-10-16 09:15 UTC178
            Rectangle::from_size((1.0, 1.0).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC179
            full(),
824bf02b6e55atagen2026-10-16 09:15 UTC180
            &[full()],
824bf02b6e55atagen2026-10-16 09:15 UTC181
            &[],
824bf02b6e55atagen2026-10-16 09:15 UTC182
            Transform::Normal,
824bf02b6e55atagen2026-10-16 09:15 UTC183
            0.5,
824bf02b6e55atagen2026-10-16 09:15 UTC184
        )
824bf02b6e55atagen2026-10-16 09:15 UTC185
        .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC186
    });
824bf02b6e55atagen2026-10-16 09:15 UTC187
    let p = at(&s.pixels(), 32, 32);
824bf02b6e55atagen2026-10-16 09:15 UTC188
    assert!(
824bf02b6e55atagen2026-10-16 09:15 UTC189
        (127..=129).contains(&p[0]) && p[0] == p[1] && p[1] == p[2],
824bf02b6e55atagen2026-10-16 09:15 UTC190
        "{p:?}"
824bf02b6e55atagen2026-10-16 09:15 UTC191
    );
824bf02b6e55atagen2026-10-16 09:15 UTC192
}
824bf02b6e55atagen2026-10-16 09:15 UTC193
824bf02b6e55atagen2026-10-16 09:15 UTC194
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC195
fn output_transform_moves_content() {
824bf02b6e55atagen2026-10-16 09:15 UTC196
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC197
    s.draw(Transform::_180, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC198
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC199
        let dst = Rectangle::new((0, 0).into(), (16, 16).into());
824bf02b6e55atagen2026-10-16 09:15 UTC200
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN))
824bf02b6e55atagen2026-10-16 09:15 UTC201
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC202
    });
824bf02b6e55atagen2026-10-16 09:15 UTC203
    let px = s.pixels();
824bf02b6e55atagen2026-10-16 09:15 UTC204
    assert_eq!(at(&px, 0, 0), BLACK);
824bf02b6e55atagen2026-10-16 09:15 UTC205
    assert_eq!(at(&px, 63, 63), GREEN);
824bf02b6e55atagen2026-10-16 09:15 UTC206
}
824bf02b6e55atagen2026-10-16 09:15 UTC207
824bf02b6e55atagen2026-10-16 09:15 UTC208
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC209
fn offscreen_images_can_be_sampled() {
824bf02b6e55atagen2026-10-16 09:15 UTC210
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC211
    let mut inner = s
824bf02b6e55atagen2026-10-16 09:15 UTC212
        .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC213
        .create_buffer(Fourcc::Argb8888, Size::from((4, 4)))
824bf02b6e55atagen2026-10-16 09:15 UTC214
        .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC215
    {
824bf02b6e55atagen2026-10-16 09:15 UTC216
        let mut t = s.renderer.bind(&mut inner).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC217
        let mut f = s
824bf02b6e55atagen2026-10-16 09:15 UTC218
            .renderer
824bf02b6e55atagen2026-10-16 09:15 UTC219
            .render(&mut t, (4, 4).into(), Transform::Normal)
824bf02b6e55atagen2026-10-16 09:15 UTC220
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC221
        f.clear(rgba(BLUE), &[Rectangle::from_size((4, 4).into())])
824bf02b6e55atagen2026-10-16 09:15 UTC222
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC223
        f.finish().unwrap().wait().unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC224
    }
824bf02b6e55atagen2026-10-16 09:15 UTC225
    let tex = inner.texture();
824bf02b6e55atagen2026-10-16 09:15 UTC226
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC227
        f.render_texture_from_to(
824bf02b6e55atagen2026-10-16 09:15 UTC228
            &tex,
824bf02b6e55atagen2026-10-16 09:15 UTC229
            Rectangle::from_size((4.0, 4.0).into()),
824bf02b6e55atagen2026-10-16 09:15 UTC230
            full(),
824bf02b6e55atagen2026-10-16 09:15 UTC231
            &[full()],
824bf02b6e55atagen2026-10-16 09:15 UTC232
            &[],
824bf02b6e55atagen2026-10-16 09:15 UTC233
            Transform::Normal,
824bf02b6e55atagen2026-10-16 09:15 UTC234
            1.0,
824bf02b6e55atagen2026-10-16 09:15 UTC235
        )
824bf02b6e55atagen2026-10-16 09:15 UTC236
        .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC237
    });
824bf02b6e55atagen2026-10-16 09:15 UTC238
    assert_eq!(at(&s.pixels(), 32, 32), BLUE);
824bf02b6e55atagen2026-10-16 09:15 UTC239
}
824bf02b6e55atagen2026-10-16 09:15 UTC240
824bf02b6e55atagen2026-10-16 09:15 UTC241
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC242
fn reports_the_device() {
824bf02b6e55atagen2026-10-16 09:15 UTC243
    let r = VulkanRenderer::new().unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC244
    eprintln!("rendering on {}", r.device_name());
824bf02b6e55atagen2026-10-16 09:15 UTC245
    assert!(!r.device_name().is_empty());
824bf02b6e55atagen2026-10-16 09:15 UTC246
}
824bf02b6e55atagen2026-10-16 09:15 UTC247
824bf02b6e55atagen2026-10-16 09:15 UTC248
#[test]
824bf02b6e55atagen2026-10-16 09:15 UTC249
fn solid_fill_in_the_corner_keeps_its_colour() {
824bf02b6e55atagen2026-10-16 09:15 UTC250
    let mut s = Scene::new();
824bf02b6e55atagen2026-10-16 09:15 UTC251
    s.draw(Transform::Normal, |f| {
824bf02b6e55atagen2026-10-16 09:15 UTC252
        f.clear(rgba(BLACK), &[full()]).unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC253
        let dst = Rectangle::new((48, 48).into(), (16, 16).into());
824bf02b6e55atagen2026-10-16 09:15 UTC254
        f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(RED))
824bf02b6e55atagen2026-10-16 09:15 UTC255
            .unwrap();
824bf02b6e55atagen2026-10-16 09:15 UTC256
    });
824bf02b6e55atagen2026-10-16 09:15 UTC257
    assert_eq!(at(&s.pixels(), 56, 56), RED);
824bf02b6e55atagen2026-10-16 09:15 UTC258
}