use valo_dl::{BlurStyle, MaskKind, SpreadMode};
use crate::host_buffer::UNIFORM_SIZE;
use crate::pipelines::{AdvancedBlend, PipelineBlend};
#[derive(Clone)]
pub(crate) struct UniformRecord {
bytes: [u8; UNIFORM_SIZE as usize],
}
const MVP_BYTES: usize = 0;
const TINT_BYTES: usize = 64;
const PAYLOAD_BYTES: usize = 80;
pub(crate) const PAYLOAD_SLOTS: usize = (UNIFORM_SIZE as usize - PAYLOAD_BYTES) / 16;
const _: () = assert!(slot::COLOR_MATRIX + 5 <= PAYLOAD_SLOTS);
impl UniformRecord {
pub fn tinted(tint: [f32; 4]) -> Self {
let mut record = Self {
bytes: [0; UNIFORM_SIZE as usize],
};
record.bytes[TINT_BYTES..TINT_BYTES + 16].copy_from_slice(bytemuck::cast_slice(&tint));
record
}
pub fn set_mvp(&mut self, mvp: [f32; 16]) {
self.bytes[MVP_BYTES..MVP_BYTES + 64].copy_from_slice(bytemuck::cast_slice(&mvp));
}
pub fn set(&mut self, slot: usize, value: [f32; 4]) {
let start = PAYLOAD_BYTES + slot * 16;
self.bytes[start..start + 16].copy_from_slice(bytemuck::cast_slice(&value));
}
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
}
pub(crate) mod slot {
pub const RECT: usize = 0;
pub const GEOM: usize = 1;
pub const MISC: usize = 2;
pub const OFFSETS: usize = 3;
pub const RADII: usize = 3;
pub const DECAL: usize = 3;
pub const DOWNSAMPLE_EDGES: usize = 3;
pub const DOWNSAMPLE_MODES: usize = 8;
pub const SECOND_UV: usize = 3;
pub const SOURCE_UV: usize = 3;
pub const COLORS: usize = 5;
pub const LOCAL: usize = 13;
pub const CONICAL: usize = 15;
pub const CONICAL_FLAGS: usize = 16;
pub const COLOR_MATRIX: usize = 17;
}
pub(crate) fn spread_id(spread: SpreadMode) -> f32 {
match spread {
SpreadMode::Pad => 0.0,
SpreadMode::Repeat => 1.0,
SpreadMode::Reflect => 2.0,
}
}
pub(crate) mod conical {
pub const CONCENTRIC: f32 = 0.0;
pub const GENERAL: f32 = 1.0;
pub const EMPTY: f32 = 2.0;
pub const STRIP: f32 = 3.0;
}
pub(crate) mod gamma {
pub const SRGB_TO_LINEAR: f32 = 0.0;
pub const LINEAR_TO_SRGB: f32 = 1.0;
}
pub(crate) fn blur_style_id(style: BlurStyle) -> f32 {
match style {
BlurStyle::Normal => 0.0,
BlurStyle::Solid => 1.0,
BlurStyle::Inner => 2.0,
BlurStyle::Outer => 3.0,
}
}
pub(crate) fn mask_kind_id(kind: MaskKind) -> f32 {
match kind {
MaskKind::Alpha => 0.0,
MaskKind::Luminance => 1.0,
}
}
pub(crate) const ADVANCED_FILTER_BASE: u32 = 15;
pub(crate) fn wgsl_prelude() -> String {
let mut prelude = String::from("// Generated from shader_abi.rs: payload slots and ids.\n");
let mut line = |name: &str, value: String| {
prelude.push_str(&format!("const {name} = {value};\n"));
};
let u32_value = |value: usize| format!("{value}u");
line("PAYLOAD_SLOTS", u32_value(PAYLOAD_SLOTS));
for (name, value) in [
("PAYLOAD_RECT", slot::RECT),
("PAYLOAD_GEOM", slot::GEOM),
("PAYLOAD_MISC", slot::MISC),
("PAYLOAD_OFFSETS", slot::OFFSETS),
("PAYLOAD_RADII", slot::RADII),
("PAYLOAD_DECAL", slot::DECAL),
("PAYLOAD_DOWNSAMPLE_EDGES", slot::DOWNSAMPLE_EDGES),
("PAYLOAD_DOWNSAMPLE_MODES", slot::DOWNSAMPLE_MODES),
("PAYLOAD_SECOND_UV", slot::SECOND_UV),
("PAYLOAD_SOURCE_UV", slot::SOURCE_UV),
("PAYLOAD_COLORS", slot::COLORS),
("PAYLOAD_LOCAL", slot::LOCAL),
("PAYLOAD_CONICAL", slot::CONICAL),
("PAYLOAD_CONICAL_FLAGS", slot::CONICAL_FLAGS),
("PAYLOAD_COLOR_MATRIX", slot::COLOR_MATRIX),
(
"MAX_KERNEL_SAMPLES",
crate::planner::gaussian::MAX_KERNEL_SAMPLES,
),
] {
line(name, u32_value(value));
}
for (name, spread) in [
("SPREAD_REPEAT", SpreadMode::Repeat),
("SPREAD_REFLECT", SpreadMode::Reflect),
] {
line(name, format!("{}u", spread_id(spread) as u32));
}
for (name, value) in [
("CONICAL_CONCENTRIC", conical::CONCENTRIC),
("CONICAL_EMPTY", conical::EMPTY),
("CONICAL_STRIP", conical::STRIP),
("GAMMA_LINEAR_TO_SRGB", gamma::LINEAR_TO_SRGB),
("MASK_LUMINANCE", mask_kind_id(MaskKind::Luminance)),
] {
line(name, format!("{value:?}"));
}
for (name, style) in [
("STYLE_SOLID", BlurStyle::Solid),
("STYLE_INNER", BlurStyle::Inner),
("STYLE_OUTER", BlurStyle::Outer),
] {
line(name, format!("{}u", blur_style_id(style) as u32));
}
for blend in PipelineBlend::ALL {
line(
&format!("BLEND_{}", blend.wgsl_name()),
format!("{}u", blend.id()),
);
}
for blend in AdvancedBlend::ALL {
line(
&format!("ADVANCED_{}", blend.wgsl_name()),
format!("{}u", blend.id()),
);
}
line("ADVANCED_FILTER_BASE", format!("{ADVANCED_FILTER_BASE}u"));
prelude
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_payload_fills_the_record() {
assert_eq!(PAYLOAD_SLOTS, 27);
}
#[test]
fn the_prelude_names_every_id() {
let prelude = wgsl_prelude();
assert!(prelude.contains("const PAYLOAD_COLOR_MATRIX = 17u;"));
assert!(prelude.contains("const BLEND_SCREEN = 14u;"));
assert!(prelude.contains("const ADVANCED_LUMINOSITY = 13u;"));
assert!(prelude.contains("const STYLE_OUTER = 3u;"));
assert!(prelude.contains("const SPREAD_REFLECT = 2u;"));
assert!(prelude.contains("const CONICAL_STRIP = 3.0;"));
}
}