gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 660Markdown 302TOML 157RON 312382 Definiciones

⎇ 333dca1d5fb473825c9128d402d4cc4e2a5176fc llimphi-raster/tests/gpu_batch_smoke.rs Rust · 128 líneas · 4.0 KB Historia Quién tocó qué crudo

refresh: stack al día (vello 0.7 / wgpu 27 / parley 0.6) + motor 3D voxel ccab39f14 · Sergio ·

1//! Smoke test del backend GPU directo (`llimphi_raster::gpu`).
2//!
3//! No verifica píxeles — eso requiere AA y un patrón conocido, y por
4//! ahora el módulo no garantiza pixel-exactness. Sí verifica que:
5//!
6//! - `GpuPipelines::new` compila los 3 shaders WGSL sin errores de naga.
7//! - `GpuBatch` acepta líneas, triángulos y rects mezclados sin pánico.
8//! - `flush` ejecuta sin errores wgpu y la `Maintain::Wait` retorna
9//!   (= la GPU/llvmpipe terminó las pasadas).
10//!
11//! Corre en cualquier adapter wgpu disponible — en CI sin GPU usa
12//! llvmpipe, donde igual valida el ensamblado y la sintaxis WGSL.
13
14use llimphi_hal::{wgpu, Hal};
15use llimphi_raster::gpu::{GpuBatch, GpuPipelines};
16use llimphi_raster::peniko::Color;
17
18const W: u32 = 256;
19const H: u32 = 256;
20const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
21
22fn make_target(device: &wgpu::Device) -> (wgpu::Texture, wgpu::TextureView) {
23    let tex = device.create_texture(&wgpu::TextureDescriptor {
24        label: Some("smoke-target"),
25        size: wgpu::Extent3d {
26            width: W,
27            height: H,
28            depth_or_array_layers: 1,
29        },
30        mip_level_count: 1,
31        sample_count: 1,
32        dimension: wgpu::TextureDimension::D2,
33        format: FMT,
34        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
35        view_formats: &[],
36    });
37    let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
38    (tex, view)
39}
40
41#[test]
42fn batch_with_rects_lines_tris_does_not_panic() {
43    let hal = pollster::block_on(Hal::new(None)).expect("hal");
44    let pipelines = GpuPipelines::new(&hal.device, FMT);
45    let (_tex, view) = make_target(&hal.device);
46
47    let mut batch = GpuBatch::new(&pipelines);
48    batch.line_width(2.0);
49
50    // Cuadrícula 8×8 de rects con color que varía.
51    for j in 0..8 {
52        for i in 0..8 {
53            let x = 8.0 + i as f32 * 30.0;
54            let y = 8.0 + j as f32 * 30.0;
55            let c = Color::from_rgba8(
56                (i * 32) as u8,
57                (j * 32) as u8,
58                100,
59                255,
60            );
61            batch.add_rect(x, y, 24.0, 24.0, c);
62        }
63    }
64
65    // Diagonal de líneas.
66    for k in 0..16 {
67        batch.add_line(
68            (0.0, k as f32 * 16.0),
69            (W as f32, (k + 1) as f32 * 16.0),
70            Color::from_rgba8(220, 220, 250, 180),
71        );
72    }
73
74    // Triángulo grande con color por vértice.
75    batch.add_tri(
76        (128.0, 32.0),
77        (64.0, 220.0),
78        (220.0, 220.0),
79        Color::from_rgba8(255, 80, 80, 200),
80        Color::from_rgba8(80, 255, 80, 200),
81        Color::from_rgba8(80, 80, 255, 200),
82    );
83
84    assert!(batch.primitive_count() > 0, "batch debería tener primitivas");
85
86    let mut encoder = hal
87        .device
88        .create_command_encoder(&wgpu::CommandEncoderDescriptor {
89            label: Some("smoke-enc"),
90        });
91    batch.flush(
92        &hal.device,
93        &hal.queue,
94        &mut encoder,
95        &view,
96        (W as f32, H as f32),
97        wgpu::LoadOp::Clear(wgpu::Color::BLACK),
98    );
99    hal.queue.submit(std::iter::once(encoder.finish()));
100    hal.device.poll(wgpu::PollType::wait_indefinitely());
101}
102
103#[test]
104fn empty_batch_flush_is_no_op() {
105    let hal = pollster::block_on(Hal::new(None)).expect("hal");
106    let pipelines = GpuPipelines::new(&hal.device, FMT);
107    let (_tex, view) = make_target(&hal.device);
108
109    let batch = GpuBatch::new(&pipelines);
110    assert_eq!(batch.primitive_count(), 0);
111
112    let mut encoder = hal
113        .device
114        .create_command_encoder(&wgpu::CommandEncoderDescriptor {
115            label: Some("smoke-empty-enc"),
116        });
117    // Con batch vacío, flush no debe crear render pass ni buffers.
118    batch.flush(
119        &hal.device,
120        &hal.queue,
121        &mut encoder,
122        &view,
123        (W as f32, H as f32),
124        wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
125    );
126    hal.queue.submit(std::iter::once(encoder.finish()));
127    hal.device.poll(wgpu::PollType::wait_indefinitely());
128}