refactor
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
||||
//! The visualiser contract: one trait the bin drives, one render context.
|
||||
//!
|
||||
//! `sigil.rs` no longer hard-codes a `Visual { Sigil, Scope, Breakcore }`
|
||||
//! enum + an inherent `match` per call. It holds a `Box<dyn Visualizer>` and
|
||||
//! talks to this trait; adding a mode is a new `impl Visualizer` + one arm in
|
||||
//! [`next_visual`], nothing in the bin's hot path.
|
||||
//!
|
||||
//! The CPU/GPU split is *relocated, not deleted* (it is a real dichotomy, not
|
||||
//! accidental complexity): `Draw`-based modes (`Sigil`/`Scope`) emit through
|
||||
//! the shared chromatic-aberration channel passes + `Post`; `Breakcore`
|
||||
//! presents/captures its own raymarch target. The caller still branches on
|
||||
//! [`Visualizer::is_gpu`] — the trait just standardises the contract and
|
||||
//! moves the `match` out of the bin.
|
||||
//!
|
||||
//! Determinism is unaffected: dispatch is pure indirection; each visualiser's
|
||||
//! `update` still advances its `Rng`/integration once per frame exactly as
|
||||
//! before, so `--render` stays bit-reproducible.
|
||||
|
||||
use crate::audio::Bands;
|
||||
use crate::viz::breakcore::Breakcore;
|
||||
use crate::viz::palette::Palette;
|
||||
use crate::viz::scope::Scope;
|
||||
use crate::viz::sigil::Sigil;
|
||||
use nannou::prelude::Draw;
|
||||
use nannou::wgpu;
|
||||
|
||||
/// Per-frame shared tunables handed to a visualiser. Cheap to copy (it only
|
||||
/// borrows the palette) so the bin can spin one per chromatic-aberration
|
||||
/// channel pass with just `tint` swapped (`RenderContext { tint, ..ctx }`).
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RenderContext<'a> {
|
||||
pub pal: &'a Palette,
|
||||
pub fit: f32,
|
||||
pub scale: f32,
|
||||
pub warp: f32,
|
||||
pub glow: bool,
|
||||
pub seg: usize,
|
||||
pub tint: [f32; 3],
|
||||
// GPU-path extras (ignored by the Draw-based modes).
|
||||
pub feedback: bool,
|
||||
pub fade: f32,
|
||||
pub ca_px: f32,
|
||||
pub drive: f32,
|
||||
}
|
||||
|
||||
/// One visualiser mode. `Draw`-based modes implement [`draw`](Self::draw);
|
||||
/// a GPU mode sets [`is_gpu`](Self::is_gpu) and implements
|
||||
/// [`render_gpu`](Self::render_gpu)/[`current_tex`](Self::current_tex)/
|
||||
/// [`capture_raw`](Self::capture_raw) instead — the bin dispatches on
|
||||
/// `is_gpu()`. Object-safe: held as `Box<dyn Visualizer>`.
|
||||
pub trait Visualizer {
|
||||
fn name(&self) -> &'static str;
|
||||
fn seed(&self) -> u64;
|
||||
fn reseed(&mut self, seed: u64);
|
||||
fn update(&mut self, b: &Bands, dt: f32);
|
||||
/// Element count for the HUD (tendrils / beam points / capsules).
|
||||
fn element_count(&self) -> usize;
|
||||
|
||||
/// `true` ⇒ this mode owns a wgpu pipeline and uses the GPU methods
|
||||
/// below; `false` ⇒ it draws through `Draw`/`Post`.
|
||||
fn is_gpu(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// CPU/`Draw` path. No-op for GPU-driven modes.
|
||||
fn draw(&self, _d: &Draw, _ctx: &RenderContext) {}
|
||||
|
||||
/// GPU path: render this frame's own pipeline target and return it.
|
||||
/// `None` for `Draw`-based modes (the bin uses the `Post` chain).
|
||||
fn render_gpu(
|
||||
&mut self,
|
||||
_device: &wgpu::Device,
|
||||
_queue: &wgpu::Queue,
|
||||
_ctx: &RenderContext,
|
||||
) -> Option<&wgpu::Texture> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The texture to present this frame (GPU modes only; else `None` and the
|
||||
/// bin presents the `Post` accumulator).
|
||||
fn current_tex(&self) -> Option<&wgpu::Texture> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Synchronous RGBA readback of the current target (GPU modes only).
|
||||
fn capture_raw(
|
||||
&self,
|
||||
_device: &wgpu::Device,
|
||||
_queue: &wgpu::Queue,
|
||||
) -> Option<anyhow::Result<Vec<u8>>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Visualizer for Sigil {
|
||||
fn name(&self) -> &'static str {
|
||||
"sigil"
|
||||
}
|
||||
fn seed(&self) -> u64 {
|
||||
self.seed
|
||||
}
|
||||
fn reseed(&mut self, seed: u64) {
|
||||
Sigil::reseed(self, seed)
|
||||
}
|
||||
fn update(&mut self, b: &Bands, dt: f32) {
|
||||
Sigil::update(self, b, dt)
|
||||
}
|
||||
fn element_count(&self) -> usize {
|
||||
self.tendril_count()
|
||||
}
|
||||
fn draw(&self, d: &Draw, c: &RenderContext) {
|
||||
Sigil::draw(self, d, c.pal, c.fit, c.scale, c.warp, c.glow, c.seg, c.tint)
|
||||
}
|
||||
}
|
||||
|
||||
impl Visualizer for Scope {
|
||||
fn name(&self) -> &'static str {
|
||||
"scope"
|
||||
}
|
||||
fn seed(&self) -> u64 {
|
||||
self.seed
|
||||
}
|
||||
fn reseed(&mut self, seed: u64) {
|
||||
Scope::reseed(self, seed)
|
||||
}
|
||||
fn update(&mut self, b: &Bands, dt: f32) {
|
||||
Scope::update(self, b, dt)
|
||||
}
|
||||
fn element_count(&self) -> usize {
|
||||
self.point_count()
|
||||
}
|
||||
fn draw(&self, d: &Draw, c: &RenderContext) {
|
||||
Scope::draw(self, d, c.pal, c.fit, c.scale, c.warp, c.glow, c.seg, c.tint)
|
||||
}
|
||||
}
|
||||
|
||||
impl Visualizer for Breakcore {
|
||||
fn name(&self) -> &'static str {
|
||||
"breakcore"
|
||||
}
|
||||
fn seed(&self) -> u64 {
|
||||
self.seed
|
||||
}
|
||||
fn reseed(&mut self, seed: u64) {
|
||||
Breakcore::reseed(self, seed)
|
||||
}
|
||||
fn update(&mut self, b: &Bands, dt: f32) {
|
||||
Breakcore::update(self, b, dt)
|
||||
}
|
||||
fn element_count(&self) -> usize {
|
||||
self.point_count()
|
||||
}
|
||||
fn is_gpu(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn render_gpu(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
c: &RenderContext,
|
||||
) -> Option<&wgpu::Texture> {
|
||||
Some(Breakcore::render(
|
||||
self, device, queue, c.pal, c.scale, c.warp, c.feedback, c.fade, c.ca_px, c.drive,
|
||||
))
|
||||
}
|
||||
fn current_tex(&self) -> Option<&wgpu::Texture> {
|
||||
Some(self.current())
|
||||
}
|
||||
fn capture_raw(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
) -> Option<anyhow::Result<Vec<u8>>> {
|
||||
Some(Breakcore::capture_raw(self, device, queue))
|
||||
}
|
||||
}
|
||||
|
||||
/// Live `M`-key cycle: Sigil → Scope → Breakcore → Sigil, keeping the seed.
|
||||
/// `Breakcore` needs the device to (re)build its pipeline.
|
||||
pub fn next_visual(cur: &dyn Visualizer, device: &wgpu::Device) -> Box<dyn Visualizer> {
|
||||
let s = cur.seed();
|
||||
match cur.name() {
|
||||
"sigil" => Box::new(Scope::new(s)),
|
||||
"scope" => Box::new(Breakcore::new(s, device)),
|
||||
_ => Box::new(Sigil::new(s)),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user