refresh: stack al día (vello 0.7 / wgpu 27 / parley 0.6) + motor 3D voxel
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>
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
//! Smoke test del `CellPipeline` (Fase 4.2 del SDD-TERMINAL).
|
||||
//!
|
||||
//! No verifica píxeles — eso requiere conocer la fuente exacta, font hinting
|
||||
//! del rasterizer y un pipeline de comparación. Sí verifica:
|
||||
//!
|
||||
//! - `CellPipeline::new` compila el shader WGSL sin errores de naga.
|
||||
//! - `create_atlas_texture` sube bytes a una `R8Unorm` sin pánico.
|
||||
//! - `draw` ejecuta sin errores wgpu con un atlas vivo y N instancias —
|
||||
//! por debajo y por arriba del cap del adapter, sin reasignar buffers.
|
||||
//!
|
||||
//! Corre en cualquier adapter wgpu disponible (en CI sin GPU = llvmpipe).
|
||||
|
||||
use llimphi_hal::{wgpu, Hal};
|
||||
use llimphi_widget_terminal::cell_pipeline::{
|
||||
pack_rgba, CellInstance, CellPipeline, CellUniforms,
|
||||
};
|
||||
use llimphi_widget_terminal::glyph_atlas::GlyphAtlas;
|
||||
|
||||
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("cell-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 pipeline_compila_y_dibuja_sin_panico() {
|
||||
let hal = pollster::block_on(Hal::new(None)).expect("hal");
|
||||
let pipeline = CellPipeline::new(&hal.device, FMT);
|
||||
let (_tex, view) = make_target(&hal.device);
|
||||
|
||||
// Atlas con un par de glifos.
|
||||
let mut atlas = GlyphAtlas::new(
|
||||
llimphi_ui::llimphi_text::MONO_FONT_BYTES,
|
||||
14.0,
|
||||
16,
|
||||
4,
|
||||
)
|
||||
.expect("atlas");
|
||||
let slot_a = atlas.glyph_for('A').unwrap();
|
||||
let slot_b = atlas.glyph_for('B').unwrap();
|
||||
let (atlas_w, atlas_h) = atlas.size();
|
||||
let (cell_w, cell_h) = atlas.cell_size();
|
||||
|
||||
let (_atlas_tex, atlas_view) =
|
||||
CellPipeline::create_atlas_texture(&hal.device, &hal.queue, atlas.pixels(), atlas.size());
|
||||
|
||||
// Dos celdas, A y B en (0,0) y (cell_w,0).
|
||||
let cells = vec![
|
||||
CellInstance {
|
||||
cell_x: 0.0,
|
||||
cell_y: 0.0,
|
||||
uv_x: slot_a.px as f32,
|
||||
uv_y: slot_a.py as f32,
|
||||
uv_w: cell_w as f32,
|
||||
uv_h: cell_h as f32,
|
||||
fg_rgba: pack_rgba(255, 255, 255, 255),
|
||||
bg_rgba: pack_rgba(20, 20, 20, 255),
|
||||
},
|
||||
CellInstance {
|
||||
cell_x: cell_w as f32,
|
||||
cell_y: 0.0,
|
||||
uv_x: slot_b.px as f32,
|
||||
uv_y: slot_b.py as f32,
|
||||
uv_w: cell_w as f32,
|
||||
uv_h: cell_h as f32,
|
||||
fg_rgba: pack_rgba(100, 255, 100, 255),
|
||||
bg_rgba: pack_rgba(0, 0, 0, 255),
|
||||
},
|
||||
];
|
||||
|
||||
let uniforms = CellUniforms {
|
||||
viewport_w: W as f32,
|
||||
viewport_h: H as f32,
|
||||
cell_w: cell_w as f32,
|
||||
cell_h: cell_h as f32,
|
||||
atlas_w: atlas_w as f32,
|
||||
atlas_h: atlas_h as f32,
|
||||
_pad0: 0.0,
|
||||
_pad1: 0.0,
|
||||
};
|
||||
|
||||
let mut encoder = hal.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("cell-smoke-encoder"),
|
||||
});
|
||||
|
||||
// Clear primero para tener un load:Load coherente.
|
||||
{
|
||||
let _pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("cell-smoke-clear"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
depth_slice: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
}
|
||||
|
||||
pipeline.draw(
|
||||
&hal.device,
|
||||
&hal.queue,
|
||||
&mut encoder,
|
||||
&view,
|
||||
&atlas_view,
|
||||
&cells,
|
||||
uniforms,
|
||||
);
|
||||
|
||||
hal.queue.submit(std::iter::once(encoder.finish()));
|
||||
hal.device.poll(wgpu::PollType::wait_indefinitely());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_con_cero_instancias_es_no_op() {
|
||||
let hal = pollster::block_on(Hal::new(None)).expect("hal");
|
||||
let pipeline = CellPipeline::new(&hal.device, FMT);
|
||||
let (_tex, view) = make_target(&hal.device);
|
||||
|
||||
let mut atlas = GlyphAtlas::new(
|
||||
llimphi_ui::llimphi_text::MONO_FONT_BYTES,
|
||||
14.0,
|
||||
16,
|
||||
4,
|
||||
)
|
||||
.unwrap();
|
||||
let _ = atlas.glyph_for('A'); // tener algo en el atlas
|
||||
let (_atlas_tex, atlas_view) =
|
||||
CellPipeline::create_atlas_texture(&hal.device, &hal.queue, atlas.pixels(), atlas.size());
|
||||
|
||||
let (atlas_w, atlas_h) = atlas.size();
|
||||
let (cell_w, cell_h) = atlas.cell_size();
|
||||
let mut encoder = hal.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("cell-smoke-empty-encoder"),
|
||||
});
|
||||
pipeline.draw(
|
||||
&hal.device,
|
||||
&hal.queue,
|
||||
&mut encoder,
|
||||
&view,
|
||||
&atlas_view,
|
||||
&[],
|
||||
CellUniforms {
|
||||
viewport_w: W as f32,
|
||||
viewport_h: H as f32,
|
||||
cell_w: cell_w as f32,
|
||||
cell_h: cell_h as f32,
|
||||
atlas_w: atlas_w as f32,
|
||||
atlas_h: atlas_h as f32,
|
||||
_pad0: 0.0,
|
||||
_pad1: 0.0,
|
||||
},
|
||||
);
|
||||
hal.queue.submit(std::iter::once(encoder.finish()));
|
||||
hal.device.poll(wgpu::PollType::wait_indefinitely());
|
||||
}
|
||||
Reference in New Issue
Block a user