824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 1 | //! Renders small scenes offscreen and checks pixels. Runs on whatever Vulkan |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 2 | //! driver is available. `nix flake check` forces lavapipe. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 3 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 4 | use monolith_render_vk::{Clip, VulkanImage, VulkanRenderer, VulkanTexture}; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 5 | use smithay::backend::allocator::Fourcc; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 6 | use smithay::backend::renderer::{ |
5a760f013a02 | atagen | 2026-10-16 09:55 UTC | 7 | Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 8 | }; |
5a760f013a02 | atagen | 2026-10-16 09:55 UTC | 9 | use smithay::utils::{Physical, Rectangle, Transform}; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 10 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 11 | const W: i32 = 64; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 12 | const H: i32 = 64; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 13 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 14 | type Pixel = [u8; 4]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 15 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 16 | const RED: Pixel = [255, 0, 0, 255]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 17 | const GREEN: Pixel = [0, 255, 0, 255]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 18 | const WHITE: Pixel = [255, 255, 255, 255]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 19 | const BLACK: Pixel = [0, 0, 0, 255]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 20 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 21 | struct Scene { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 22 | renderer: VulkanRenderer, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 23 | image: VulkanImage, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 24 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 25 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 26 | impl Scene { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 27 | fn new() -> Self { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 28 | let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device"); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 29 | let image = renderer |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 30 | .create_buffer(Fourcc::Argb8888, (W, H).into()) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 31 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 32 | Self { renderer, image } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 33 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 34 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 35 | fn draw( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 36 | &mut self, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 37 | transform: Transform, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 38 | f: impl FnOnce(&mut monolith_render_vk::VulkanFrame<'_, '_>), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 39 | ) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 40 | let mut target = self.renderer.bind(&mut self.image).unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 41 | let mut frame = self |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 42 | .renderer |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 43 | .render(&mut target, (W, H).into(), transform) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 44 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 45 | f(&mut frame); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 46 | frame.finish().unwrap().wait().unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 47 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 48 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 49 | /// Pixels as RGBA, whatever the image's byte order. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 50 | fn pixels(&mut self) -> Vec<Pixel> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 51 | let target = self.renderer.bind(&mut self.image).unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 52 | let mapping = self |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 53 | .renderer |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 54 | .copy_framebuffer( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 55 | &target, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 56 | Rectangle::from_size((W, H).into()), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 57 | Fourcc::Abgr8888, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 58 | ) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 59 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 60 | self.renderer |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 61 | .map_texture(&mapping) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 62 | .unwrap() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 63 | .as_chunks::<4>() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 64 | .0 |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 65 | .to_vec() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 66 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 67 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 68 | fn texture(&mut self, size: (i32, i32), rgba: &[Pixel]) -> VulkanTexture { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 69 | let bytes: Vec<u8> = rgba.iter().flatten().copied().collect(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 70 | self.renderer |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 71 | .import_memory(&bytes, Fourcc::Abgr8888, size.into(), false) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 72 | .unwrap() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 73 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 74 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 75 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 76 | fn at(pixels: &[Pixel], x: i32, y: i32) -> Pixel { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 77 | pixels[(y * W + x) as usize] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 78 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 79 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 80 | fn full() -> Rectangle<i32, Physical> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 81 | Rectangle::from_size((W, H).into()) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 82 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 83 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 84 | fn rgba(p: Pixel) -> Color32F { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 85 | p.map(|c| f32::from(c) / 255.0).into() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 86 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 87 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 88 | #[test] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 89 | fn partial_clear_keeps_previous_contents() { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 90 | let mut s = Scene::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 91 | s.draw(Transform::Normal, |f| { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 92 | f.clear(rgba(RED), &[full()]).unwrap() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 93 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 94 | s.draw(Transform::Normal, |f| { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 95 | f.clear( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 96 | rgba(GREEN), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 97 | &[Rectangle::new((0, 0).into(), (32, 64).into())], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 98 | ) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 99 | .unwrap() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 100 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 101 | let px = s.pixels(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 102 | assert_eq!(at(&px, 0, 0), GREEN); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 103 | assert_eq!(at(&px, 63, 0), RED); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 104 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 105 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 106 | #[test] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 107 | fn alpha_blends_premultiplied() { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 108 | let mut s = Scene::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 109 | let tex = s.texture((1, 1), &[WHITE]); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 110 | s.draw(Transform::Normal, |f| { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 111 | f.clear(rgba(BLACK), &[full()]).unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 112 | f.render_texture_from_to( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 113 | &tex, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 114 | Rectangle::from_size((1.0, 1.0).into()), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 115 | full(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 116 | &[full()], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 117 | &[], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 118 | Transform::Normal, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 119 | 0.5, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 120 | ) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 121 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 122 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 123 | let p = at(&s.pixels(), 32, 32); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 124 | assert!( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 125 | (127..=129).contains(&p[0]) && p[0] == p[1] && p[1] == p[2], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 126 | "{p:?}" |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 127 | ); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 128 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 129 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 130 | #[test] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 131 | fn output_transform_moves_content() { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 132 | let mut s = Scene::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 133 | s.draw(Transform::_180, |f| { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 134 | f.clear(rgba(BLACK), &[full()]).unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 135 | let dst = Rectangle::new((0, 0).into(), (16, 16).into()); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 136 | f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 137 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 138 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 139 | let px = s.pixels(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 140 | assert_eq!(at(&px, 0, 0), BLACK); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 141 | assert_eq!(at(&px, 63, 63), GREEN); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 142 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 143 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 144 | #[test] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 145 | fn solid_fill_in_the_corner_keeps_its_colour() { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 146 | let mut s = Scene::new(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 147 | s.draw(Transform::Normal, |f| { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 148 | f.clear(rgba(BLACK), &[full()]).unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 149 | let dst = Rectangle::new((48, 48).into(), (16, 16).into()); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 150 | f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(RED)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 151 | .unwrap(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 152 | }); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 153 | assert_eq!(at(&s.pixels(), 56, 56), RED); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 154 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 155 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 156 | /// The radius follows its corner through an output rotation. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 157 | #[test] |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 158 | fn clip_radius_stays_on_its_corner_when_rotated() { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 159 | let corners = [(0, 0), (W - 1, 0), (0, H - 1), (W - 1, H - 1)]; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 160 | let mut s = Scene::new(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 161 | s.draw(Transform::_90, |f| { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 162 | f.clear(rgba(BLACK), &[full()]).unwrap(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 163 | let dst = Rectangle::new((0, 0).into(), (4, 4).into()); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 164 | f.draw_solid(dst, &[Rectangle::from_size(dst.size)], rgba(GREEN)) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 165 | .unwrap(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 166 | }); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 167 | let px = s.pixels(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 168 | let top_left = corners |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 169 | .into_iter() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 170 | .find(|&(x, y)| at(&px, x, y) == GREEN) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 171 | .unwrap(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 172 | s.draw(Transform::_90, |f| { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 173 | f.clear(rgba(BLACK), &[full()]).unwrap(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 174 | f.set_clip(Some(Clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 175 | rect: full(), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 176 | radii: [20.0, 0.0, 0.0, 0.0], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 177 | ring: 0.0, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 178 | })); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 179 | f.draw_solid(full(), &[full()], rgba(GREEN)).unwrap(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 180 | }); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 181 | let px = s.pixels(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 182 | for (x, y) in corners { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 183 | let expected = if (x, y) == top_left { BLACK } else { GREEN }; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 184 | assert_eq!(at(&px, x, y), expected, "corner {x},{y}"); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 185 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 186 | assert_eq!(at(&px, W / 2, H / 2), GREEN); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 187 | } |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 188 | |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 189 | /// Draws arrive in the rotated space, which for a quarter turn on a wide |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 190 | /// output is tall, so its far corner has to land inside the framebuffer. |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 191 | #[test] |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 192 | fn quarter_turn_on_a_wide_output_fills_it() { |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 193 | let (fw, fh) = (64, 32); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 194 | let mut renderer = VulkanRenderer::new().expect("no usable Vulkan device"); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 195 | let mut image = renderer |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 196 | .create_buffer(Fourcc::Argb8888, (fw, fh).into()) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 197 | .unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 198 | for transform in [Transform::_90, Transform::_270, Transform::Flipped90] { |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 199 | let mut target = renderer.bind(&mut image).unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 200 | let mut frame = renderer |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 201 | .render(&mut target, (fw, fh).into(), transform) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 202 | .unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 203 | let area = Rectangle::from_size((fh, fw).into()); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 204 | frame.clear(rgba(BLACK), &[area]).unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 205 | let near = Rectangle::new((0, 0).into(), (8, 8).into()); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 206 | let far = Rectangle::new((fh - 8, fw - 8).into(), (8, 8).into()); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 207 | frame |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 208 | .draw_solid(near, &[Rectangle::from_size(near.size)], rgba(GREEN)) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 209 | .unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 210 | frame |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 211 | .draw_solid(far, &[Rectangle::from_size(far.size)], rgba(RED)) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 212 | .unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 213 | frame.finish().unwrap().wait().unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 214 | drop(target); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 215 | let target = renderer.bind(&mut image).unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 216 | let mapping = renderer |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 217 | .copy_framebuffer( |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 218 | &target, |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 219 | Rectangle::from_size((fw, fh).into()), |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 220 | Fourcc::Abgr8888, |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 221 | ) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 222 | .unwrap(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 223 | let px = renderer |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 224 | .map_texture(&mapping) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 225 | .unwrap() |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 226 | .as_chunks::<4>() |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 227 | .0 |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 228 | .to_vec(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 229 | let at = |x: i32, y: i32| px[(y * fw + x) as usize]; |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 230 | let corners = [(0, 0), (fw - 1, 0), (0, fh - 1), (fw - 1, fh - 1)]; |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 231 | let green: Vec<_> = corners |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 232 | .iter() |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 233 | .filter(|&&(x, y)| at(x, y) == GREEN) |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 234 | .collect(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 235 | let red: Vec<_> = corners.iter().filter(|&&(x, y)| at(x, y) == RED).collect(); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 236 | assert_eq!((green.len(), red.len()), (1, 1), "{transform:?}"); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 237 | let (g, r) = (green[0], red[0]); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 238 | assert!(g.0 != r.0 && g.1 != r.1, "{transform:?} not diagonal"); |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 239 | } |
0b68ce424a32 | atagen | 2026-10-18 03:17 UTC | 240 | } |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 241 | |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 242 | #[test] |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 243 | fn queued_uploads_to_one_texture_land_in_order() { |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 244 | let mut s = Scene::new(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 245 | let size = (2, 2); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 246 | let bytes = |p: Pixel| -> Vec<u8> { [p; 4].iter().flatten().copied().collect() }; |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 247 | let tex = s.texture(size, &[RED; 4]); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 248 | for colour in [WHITE, GREEN] { |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 249 | s.renderer |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 250 | .update_memory(&tex, &bytes(colour), Rectangle::from_size(size.into())) |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 251 | .unwrap(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 252 | } |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 253 | let read = s |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 254 | .renderer |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 255 | .copy_texture(&tex, Rectangle::from_size(size.into()), Fourcc::Abgr8888) |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 256 | .unwrap(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 257 | assert_eq!(s.renderer.map_texture(&read).unwrap()[..4], GREEN); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 258 | s.renderer |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 259 | .update_memory(&tex, &bytes(WHITE), Rectangle::from_size(size.into())) |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 260 | .unwrap(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 261 | s.renderer |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 262 | .update_memory(&tex, &bytes(RED), Rectangle::from_size(size.into())) |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 263 | .unwrap(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 264 | s.draw(Transform::Normal, |f| { |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 265 | f.render_texture_from_to( |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 266 | &tex, |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 267 | Rectangle::from_size((2.0, 2.0).into()), |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 268 | full(), |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 269 | &[full()], |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 270 | &[], |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 271 | Transform::Normal, |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 272 | 1.0, |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 273 | ) |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 274 | .unwrap(); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 275 | }); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 276 | assert_eq!(at(&s.pixels(), 32, 32), RED); |
61ebcd0a3619 | atagen | 2026-10-19 07:29 UTC | 277 | } |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 278 | |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 279 | #[test] |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 280 | fn opaque_format_texture_replaces_what_is_under_it() { |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 281 | let mut s = Scene::new(); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 282 | let bytes = [0u8, 255, 0, 0]; |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 283 | let tex = s |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 284 | .renderer |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 285 | .import_memory(&bytes, Fourcc::Xbgr8888, (1, 1).into(), false) |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 286 | .unwrap(); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 287 | s.draw(Transform::Normal, |f| { |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 288 | f.clear(rgba(RED), &[full()]).unwrap(); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 289 | f.render_texture_from_to( |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 290 | &tex, |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 291 | Rectangle::from_size((1.0, 1.0).into()), |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 292 | full(), |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 293 | &[full()], |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 294 | &[], |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 295 | Transform::Normal, |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 296 | 1.0, |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 297 | ) |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 298 | .unwrap(); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 299 | }); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 300 | assert_eq!(at(&s.pixels(), 32, 32), GREEN); |
c0caa658decf | atagen | 2026-10-19 07:37 UTC | 301 | } |