use vyre_foundation::ir::model::expr::GeneratorRef;
use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
const OP_ID: &str = "vyre-libs::visual::cell_grid";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GridShape {
pub cols: u32,
pub rows: u32,
pub cell_width: u32,
pub cell_height: u32,
}
impl GridShape {
#[must_use]
pub const fn width(&self) -> u32 {
self.cols * self.cell_width
}
#[must_use]
pub const fn height(&self) -> u32 {
self.rows * self.cell_height
}
#[must_use]
pub const fn cell_count(&self) -> u32 {
self.cols * self.rows
}
#[must_use]
pub const fn pixel_count(&self) -> u32 {
self.width() * self.height()
}
pub(super) fn validated(self) -> Self {
assert!(
self.cols > 0 && self.rows > 0,
"Fix: a cell grid needs at least one row and one column, got {}x{}",
self.cols,
self.rows
);
assert!(
self.cell_width > 0 && self.cell_height > 0,
"Fix: a cell needs a non-zero size, got {}x{} pixels",
self.cell_width,
self.cell_height
);
let width = self
.cols
.checked_mul(self.cell_width)
.expect("Fix: cols * cell_width overflows u32");
let height = self
.rows
.checked_mul(self.cell_height)
.expect("Fix: rows * cell_height overflows u32");
width
.checked_mul(height)
.expect("Fix: the surface pixel count overflows u32");
self.cols
.checked_mul(self.rows)
.expect("Fix: cols * rows overflows u32");
self
}
}
pub(super) fn cell_lookup_nodes(shape: GridShape) -> Vec<Node> {
let width = shape.width();
vec![
Node::let_bind("y", Expr::div(Expr::var("idx"), Expr::u32(width))),
Node::let_bind(
"x",
Expr::sub(
Expr::var("idx"),
Expr::mul(Expr::var("y"), Expr::u32(width)),
),
),
Node::let_bind(
"col",
Expr::div(Expr::var("x"), Expr::u32(shape.cell_width)),
),
Node::let_bind(
"row",
Expr::div(Expr::var("y"), Expr::u32(shape.cell_height)),
),
Node::let_bind(
"cell",
Expr::add(
Expr::mul(Expr::var("row"), Expr::u32(shape.cols)),
Expr::var("col"),
),
),
]
}
#[must_use]
pub fn cell_grid_fill(cells: &str, output: &str, shape: GridShape) -> Program {
let shape = shape.validated();
let pixels = shape.pixel_count();
let width = shape.width();
Program::wrapped(
vec![
BufferDecl::storage(cells, 0, BufferAccess::ReadOnly, DataType::U32)
.with_count(shape.cell_count()),
BufferDecl::storage(output, 1, BufferAccess::ReadWrite, DataType::U32)
.with_count(pixels),
],
super::PIXEL_WORKGROUP_SIZE,
vec![crate::region::wrap_anonymous(
OP_ID,
vec![crate::region::wrap_child(
vyre_primitives::visual::packed_rgba_map::OP_ID,
GeneratorRef {
name: OP_ID.to_string(),
},
vec![
Node::let_bind("idx", Expr::gid_x()),
Node::if_then(
Expr::lt(Expr::var("idx"), Expr::u32(pixels)),
{
let mut body = cell_lookup_nodes(shape);
body.push(Node::let_bind(
"colour",
Expr::load(cells, Expr::var("cell")),
));
body.push(Node::store(output, Expr::var("idx"), Expr::var("colour")));
body
},
),
],
)],
)],
)
}
inventory::submit! {
vyre_foundation::operation::OperationRegistration {
semantic_version: 1,
signature: None,
tier: vyre_foundation::operation::OperationTier::Library,
laws: &[],
tolerance: vyre_foundation::operation::TolerancePolicy::EXACT,
id: OP_ID,
build: Some(|| {
cell_grid_fill(
"cells",
"out",
GridShape { cols: 2, rows: 2, cell_width: 2, cell_height: 2 },
)
}),
test_inputs: Some(|| {
let cells = [0xFF00_00FFu32, 0xFF00_FF00, 0xFFFF_0000, 0xFFFF_FFFF];
vec![vec![
crate::visual::byte_helpers::u32_words_to_le_bytes(&cells),
vec![0u8; 16 * 4],
]]
}),
expected_output: Some(|| {
const R: u32 = 0xFF00_00FF;
const G: u32 = 0xFF00_FF00;
const B: u32 = 0xFFFF_0000;
const W: u32 = 0xFFFF_FFFF;
let expected = [
R, R, G, G,
R, R, G, G,
B, B, W, W,
B, B, W, W,
];
vec![vec![crate::visual::byte_helpers::u32_words_to_le_bytes(&expected)]]
}),
category: Some("visual"),
}
}