ccab39f140
Re-sincroniza las fuentes desde el monorepo (estaba en vello 0.5/wgpu 24 y con la estructura vieja de eventloop) y suma el 3D: - bump del workspace a vello 0.7 / wgpu 27 / parley 0.6, + accesskit 0.24 / accesskit_winit 0.33 / vello_hybrid 0.0.9. - nuevos crates: llimphi-3d (voxels ray-march + mallas en un depth compartido, montable dentro de un View 2D vía set_viewport+scissor) y llimphi-voxel (world-gen, personajes, director de escenas) + shared/foreign-vox (puente .vox). - README: sección "Not just 2D — a 3D voxel engine" + GIF (docs/llimphi_voxel.gif). - excluido modules/allichay (arrastra deps fuera del alcance del front-door). - cargo check --workspace: verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
129 lines
4.0 KiB
Rust
129 lines
4.0 KiB
Rust
//! Smoke test del backend GPU directo (`llimphi_raster::gpu`).
|
||
//!
|
||
//! No verifica píxeles — eso requiere AA y un patrón conocido, y por
|
||
//! ahora el módulo no garantiza pixel-exactness. Sí verifica que:
|
||
//!
|
||
//! - `GpuPipelines::new` compila los 3 shaders WGSL sin errores de naga.
|
||
//! - `GpuBatch` acepta líneas, triángulos y rects mezclados sin pánico.
|
||
//! - `flush` ejecuta sin errores wgpu y la `Maintain::Wait` retorna
|
||
//! (= la GPU/llvmpipe terminó las pasadas).
|
||
//!
|
||
//! Corre en cualquier adapter wgpu disponible — en CI sin GPU usa
|
||
//! llvmpipe, donde igual valida el ensamblado y la sintaxis WGSL.
|
||
|
||
use llimphi_hal::{wgpu, Hal};
|
||
use llimphi_raster::gpu::{GpuBatch, GpuPipelines};
|
||
use llimphi_raster::peniko::Color;
|
||
|
||
const W: u32 = 256;
|
||
const H: u32 = 256;
|
||
const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
|
||
|
||
fn make_target(device: &wgpu::Device) -> (wgpu::Texture, wgpu::TextureView) {
|
||
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
||
label: Some("smoke-target"),
|
||
size: wgpu::Extent3d {
|
||
width: W,
|
||
height: H,
|
||
depth_or_array_layers: 1,
|
||
},
|
||
mip_level_count: 1,
|
||
sample_count: 1,
|
||
dimension: wgpu::TextureDimension::D2,
|
||
format: FMT,
|
||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
||
view_formats: &[],
|
||
});
|
||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||
(tex, view)
|
||
}
|
||
|
||
#[test]
|
||
fn batch_with_rects_lines_tris_does_not_panic() {
|
||
let hal = pollster::block_on(Hal::new(None)).expect("hal");
|
||
let pipelines = GpuPipelines::new(&hal.device, FMT);
|
||
let (_tex, view) = make_target(&hal.device);
|
||
|
||
let mut batch = GpuBatch::new(&pipelines);
|
||
batch.line_width(2.0);
|
||
|
||
// Cuadrícula 8×8 de rects con color que varía.
|
||
for j in 0..8 {
|
||
for i in 0..8 {
|
||
let x = 8.0 + i as f32 * 30.0;
|
||
let y = 8.0 + j as f32 * 30.0;
|
||
let c = Color::from_rgba8(
|
||
(i * 32) as u8,
|
||
(j * 32) as u8,
|
||
100,
|
||
255,
|
||
);
|
||
batch.add_rect(x, y, 24.0, 24.0, c);
|
||
}
|
||
}
|
||
|
||
// Diagonal de líneas.
|
||
for k in 0..16 {
|
||
batch.add_line(
|
||
(0.0, k as f32 * 16.0),
|
||
(W as f32, (k + 1) as f32 * 16.0),
|
||
Color::from_rgba8(220, 220, 250, 180),
|
||
);
|
||
}
|
||
|
||
// Triángulo grande con color por vértice.
|
||
batch.add_tri(
|
||
(128.0, 32.0),
|
||
(64.0, 220.0),
|
||
(220.0, 220.0),
|
||
Color::from_rgba8(255, 80, 80, 200),
|
||
Color::from_rgba8(80, 255, 80, 200),
|
||
Color::from_rgba8(80, 80, 255, 200),
|
||
);
|
||
|
||
assert!(batch.primitive_count() > 0, "batch debería tener primitivas");
|
||
|
||
let mut encoder = hal
|
||
.device
|
||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||
label: Some("smoke-enc"),
|
||
});
|
||
batch.flush(
|
||
&hal.device,
|
||
&hal.queue,
|
||
&mut encoder,
|
||
&view,
|
||
(W as f32, H as f32),
|
||
wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||
);
|
||
hal.queue.submit(std::iter::once(encoder.finish()));
|
||
hal.device.poll(wgpu::PollType::wait_indefinitely());
|
||
}
|
||
|
||
#[test]
|
||
fn empty_batch_flush_is_no_op() {
|
||
let hal = pollster::block_on(Hal::new(None)).expect("hal");
|
||
let pipelines = GpuPipelines::new(&hal.device, FMT);
|
||
let (_tex, view) = make_target(&hal.device);
|
||
|
||
let batch = GpuBatch::new(&pipelines);
|
||
assert_eq!(batch.primitive_count(), 0);
|
||
|
||
let mut encoder = hal
|
||
.device
|
||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||
label: Some("smoke-empty-enc"),
|
||
});
|
||
// Con batch vacío, flush no debe crear render pass ni buffers.
|
||
batch.flush(
|
||
&hal.device,
|
||
&hal.queue,
|
||
&mut encoder,
|
||
&view,
|
||
(W as f32, H as f32),
|
||
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||
);
|
||
hal.queue.submit(std::iter::once(encoder.finish()));
|
||
hal.device.poll(wgpu::PollType::wait_indefinitely());
|
||
}
|