summaryrefslogtreecommitdiff
path: root/rust/crates/compositor/tests/offscreen_render.rs
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-28 19:41:16 +0200
committercashmere <cashmere@autistici.org>2026-07-28 19:41:16 +0200
commitf5a82152394fde4d8f740f664ab7737b82861618 (patch)
tree5a51b181abead1ce03d2dbfc12b77918d3be0e1b /rust/crates/compositor/tests/offscreen_render.rs
parente837e7504379e100ff3b56282577ec67bb88bec9 (diff)
downloadopencut-f5a82152394fde4d8f740f664ab7737b82861618.tar.gz
feat: GPUI desktop port foundation
- scene crate: full project/scene/element model, serde round-trip proven against the web SerializedProject shape - transfer crate: .ocp zip container, cross-compat with web fflate archives proven in both directions - storage crate: filesystem project store replacing IndexedDB, .ocp import/export - media crate: ffmpeg-next 8 decoder (frames, audio, thumbnails, waveform summaries) - playback crate: wall-clock anchored, frame-rounded playback clock - gpu crate: native texture readback; compositor renders headless offscreen on Vulkan (spike test) - flake devshell: rust toolchain, GPUI native deps, ffmpeg dev, bindgen clang args; GPUI hello-world builds - port plan in notes/gpui-port.md
Diffstat (limited to 'rust/crates/compositor/tests/offscreen_render.rs')
-rwxr-xr-xrust/crates/compositor/tests/offscreen_render.rs96
1 files changed, 96 insertions, 0 deletions
diff --git a/rust/crates/compositor/tests/offscreen_render.rs b/rust/crates/compositor/tests/offscreen_render.rs
new file mode 100755
index 00000000..a3c6df10
--- /dev/null
+++ b/rust/crates/compositor/tests/offscreen_render.rs
@@ -0,0 +1,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"
+ );
+}