summaryrefslogtreecommitdiff
path: root/rust/crates/compositor/tests/offscreen_render.rs
blob: a3c6df101faaec77018c0672703ff2044e770d14 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#![cfg(not(target_arch = "wasm32"))]

use compositor::{
    CanvasClearDescriptor, Compositor, FrameDescriptor, FrameItemDescriptor, LayerDescriptor,
    QuadTransformDescriptor,
};
use gpu::GpuContext;

fn block_on<F: Future>(future: F) -> F::Output {
    use std::sync::Arc;
    use std::task::{Context, Poll, Wake, Waker};

    struct ThreadWaker(std::thread::Thread);

    impl Wake for ThreadWaker {
        fn wake(self: Arc<Self>) {
            self.0.unpark();
        }
    }

    let waker = Waker::from(Arc::new(ThreadWaker(std::thread::current())));
    let mut context = Context::from_waker(&waker);
    let mut future = Box::pin(future);
    loop {
        match future.as_mut().poll(&mut context) {
            Poll::Ready(output) => return output,
            Poll::Pending => std::thread::park(),
        }
    }
}

#[test]
fn renders_solid_layer_offscreen() {
    let Some(context) = block_on(GpuContext::new()).ok() else {
        eprintln!("skipping: no GPU adapter available");
        return;
    };
    let mut compositor = Compositor::new(&context);

    let width = 64;
    let height = 64;
    let red = [255u8, 0, 0, 255];
    let pixels: Vec<u8> = red.repeat((width * height) as usize);
    let texture = context.create_render_texture(width, height, "test-source");
    context.queue().write_texture(
        texture.as_image_copy(),
        &pixels,
        gpu::wgpu::TexelCopyBufferLayout {
            offset: 0,
            bytes_per_row: Some(width * 4),
            rows_per_image: Some(height),
        },
        texture.size(),
    );
    compositor.upsert_texture("source".to_string(), texture);

    let frame = FrameDescriptor {
        width,
        height,
        clear: CanvasClearDescriptor {
            color: [0.0, 0.0, 0.0, 1.0],
        },
        items: vec![FrameItemDescriptor::Layer(LayerDescriptor {
            texture_id: "source".to_string(),
            transform: QuadTransformDescriptor {
                center_x: 32.0,
                center_y: 32.0,
                width: width as f32,
                height: height as f32,
                rotation_degrees: 0.0,
                flip_x: false,
                flip_y: false,
            },
            opacity: 1.0,
            blend_mode: compositor::BlendMode::Normal,
            effect_pass_groups: Vec::new(),
            mask: None,
        })],
    };

    let output = compositor
        .render_frame_to_texture(&context, &frame)
        .expect("render");
    let rendered = block_on(context.read_texture(&output)).expect("readback");
    assert_eq!(rendered.len(), (width * height * 4) as usize);

    let red_count = rendered
        .chunks_exact(4)
        .filter(|px| px[0] > 200 && px[1] < 60 && px[2] < 60)
        .count();
    let total = (width * height) as usize;
    assert!(
        red_count * 100 / total > 90,
        "expected mostly red frame, got {red_count}/{total} red pixels"
    );
}