feat(particles): Étape 28 A — ParticlePool infrastructure (no driver)
Creates the GPU resource layer for particles per ARCHI §3.2/§6, without simulation: the pool owns all buffers/pipeline/bind group but draws nothing (indirect args zeroed → no-op) until a driver is attached (Étape B). New: - resources/particle.rs: Particle (80 B, #[repr(C)], no padding — D19), SIZE/ZERO consts + offset/layout unit tests - shaders/particle_billboard.wgsl: camera-facing billboard, empty vertex layout (quad via vertex_index), instance slot via storage binding compact_index (D17), uv_rect atlas support (D15/D18) - core/particles.rs: ParticlePoolConfig, BlendingMode, ParticleDriver trait (D3), ParticlePool (4 buffers + pipeline + bind group), default disc texture (D11), unit tests Wired: - Scene: SceneGpu keeps queue/sample_count; particle_pools registry + create_particle_pool() (default disc when no texture given) - utils::conf PARTICLE_BILLBOARD_SHADER, module re-exports, prelude - tests/wgsl_validate.rs: particle billboard naga validation (2 entry points) Docs: DRAFT call-site/tree synced with the final code; AGENTS.md test count (138) + wgpu 30 API drift gotcha (contents/DeviceExt/ALPHA_BLENDING, DepthStencilState no Default, NonZero min_binding_size, const Zeroable). cargo test -p wsg-lib: 138 pass (121 lib + 10 wgsl + 7), 0 warnings.
This commit is contained in:
@@ -86,6 +86,37 @@ fn gpu_driven_shader_is_valid_wgsl() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses and fully validates the embedded `particle_billboard.wgsl` shader (Étape 28 A) via naga.
|
||||
/// The particle pool compiles it into one `RenderPipeline` (empty vertex layout — quad via
|
||||
/// `@builtin(vertex_index)`, instance slot via storage binding, D17/D19), so this offline
|
||||
/// validation is the guarantee of its validity. The contract expects two entry points:
|
||||
/// `vs_main` + `fs_main` (no compute in this step).
|
||||
#[test]
|
||||
fn particle_billboard_shader_is_valid_wgsl() {
|
||||
let src = include_str!("../src/shaders/particle_billboard.wgsl");
|
||||
let module = naga::front::wgsl::parse_str(src)
|
||||
.unwrap_or_else(|e| panic!("particle_billboard.wgsl: parsing error: {e:?}"));
|
||||
|
||||
let mut validator = naga::valid::Validator::new(
|
||||
naga::valid::ValidationFlags::all(),
|
||||
naga::valid::Capabilities::all(),
|
||||
);
|
||||
validator
|
||||
.validate(&module)
|
||||
.unwrap_or_else(|e| panic!("particle_billboard.wgsl: validation failed: {e:?}"));
|
||||
|
||||
let entry_names: Vec<&str> = module
|
||||
.entry_points
|
||||
.iter()
|
||||
.map(|ep| ep.name.as_str())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
entry_names,
|
||||
vec!["vs_main", "fs_main"],
|
||||
"the two entry points (vs + fs) are expected"
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses and fully validates the embedded `tonemap.wgsl` shader (Étape 20) via naga.
|
||||
/// The renderer compiles it into one `RenderPipeline` (vertex `vs_main` + one of the two
|
||||
/// fragment entry points `fs_aces` / `fs_reinhard`), so this offline validation is the
|
||||
|
||||
Reference in New Issue
Block a user