#![allow(clippy::unwrap_used)]
use iridia::{GsInput, GsOutput};
use iridia_dxil::lift;
use iridia_exec::geometry::{GeometryShader, Strip, Topology, Vertex};
use iridia_exec::{Sample, Sampler};
use std::collections::BTreeMap;
use std::path::Path;
struct NoSampler;
impl Sampler for NoSampler {
fn sample(&mut self, _: Sample) -> Result<[f32; 4], String> {
Err("no sampling in this fixture".into())
}
}
fn shader(name: &str) -> GeometryShader {
let bytes = std::fs::read(
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name),
)
.unwrap();
GeometryShader::new(lift(&bytes).unwrap_or_else(|e| panic!("{name}: {e}"))).unwrap()
}
fn constants(scale: f32) -> BTreeMap<(u32, u32), Vec<[u32; 4]>> {
BTreeMap::from([((0, 0), vec![[scale.to_bits(), 0, 0, 0]])])
}
fn vertex(x: f32, y: f32, u: f32) -> Vertex {
vec![
[x, y, 0.0, 1.0].map(f32::to_bits),
[u, 0.0, 0.0, 0.0].map(f32::to_bits),
]
}
fn xyu(v: &Vertex) -> [f32; 3] {
let at = |register: usize, lane: usize| f32::from_bits(v[register][lane]);
[at(0, 0), at(0, 1), at(1, 0)]
}
fn values(strip: &Strip) -> Vec<[f32; 3]> {
strip
.vertices
.iter()
.map(|v| {
assert_eq!(f32::from_bits(v[0][2]), 0.0);
assert_eq!(f32::from_bits(v[0][3]), 1.0);
xyu(v)
})
.collect()
}
#[test]
fn the_program_is_described_by_its_facts_and_names() {
for name in ["geometry.dxil", "geometry.od.dxil"] {
let gs = shader(name);
let facts = gs.facts();
assert_eq!(
(facts.input, facts.output, facts.max_vertices, facts.instances),
(GsInput::Triangle, GsOutput::TriangleStrip, 6, 2),
"{name}"
);
assert_eq!(
gs.inputs().collect::<Vec<_>>(),
["SV_Position0", "TEXCOORD0"],
"{name}"
);
assert_eq!(
gs.outputs().collect::<Vec<_>>(),
["SV_Position0", "TEXCOORD0"],
"{name}"
);
}
}
#[test]
fn every_primitive_runs_every_instance_and_its_strips_are_cut_where_the_program_cuts() {
for name in ["geometry.dxil", "geometry.od.dxil"] {
let gs = shader(name);
let corners = [
vertex(1.0, 2.0, 0.5),
vertex(3.0, 4.0, 1.5),
vertex(5.0, 6.0, 2.5),
vertex(7.0, 8.0, 3.5),
vertex(9.0, 10.0, 4.5),
vertex(11.0, 12.0, 5.5),
];
let strips = gs
.draw(&constants(2.0), &mut NoSampler, &corners, Topology::TriangleList)
.unwrap_or_else(|e| panic!("{name}: {e}"));
assert_eq!(strips.len(), 8, "{name}");
let mut at = strips.iter();
for primitive in 0..2u32 {
let first = primitive as usize * 3;
for instance in 0..2u32 {
let (p, i) = (primitive as f32, instance as f32);
let one = at.next().unwrap();
let two = at.next().unwrap();
assert_eq!(
((one.primitive, one.instance), (two.primitive, two.instance)),
((primitive, instance), (primitive, instance)),
"{name}"
);
let scaled: Vec<[f32; 3]> = (0..3)
.map(|k| {
let [x, y, u] = xyu(&corners[first + k]);
[x * 2.0, y, u]
})
.collect();
assert_eq!(
values(one),
scaled,
"{name} primitive {primitive} instance {instance}"
);
let moved: Vec<[f32; 3]> = (0..3)
.map(|k| {
let [x, y, u] = xyu(&corners[first + k]);
[x + p, y + i, u + i]
})
.collect();
assert_eq!(
values(two),
moved,
"{name} primitive {primitive} instance {instance}"
);
}
}
}
}
#[test]
fn a_triangle_strip_is_read_as_the_assembler_makes_it() {
let gs = shader("geometry.dxil");
let strip = [
vertex(1.0, 0.0, 0.0),
vertex(2.0, 0.0, 0.0),
vertex(3.0, 0.0, 0.0),
vertex(4.0, 0.0, 0.0),
];
let strips = gs
.draw(&constants(1.0), &mut NoSampler, &strip, Topology::TriangleStrip)
.unwrap();
let second = strips
.iter()
.find(|s| (s.primitive, s.instance) == (1, 0))
.unwrap();
let xs: Vec<f32> = values(second).iter().map(|v| v[0]).collect();
assert_eq!(xs, [2.0, 4.0, 3.0]);
}
#[test]
fn a_draw_of_another_kind_of_primitive_is_refused() {
let gs = shader("geometry.dxil");
let vertices = [vertex(0.0, 0.0, 0.0), vertex(1.0, 0.0, 0.0)];
let e = gs
.draw(&constants(1.0), &mut NoSampler, &vertices, Topology::LineList)
.unwrap_err();
assert!(e.contains("reads Triangle"), "{e}");
let e = gs
.run(&constants(1.0), &mut NoSampler, &[&vertices[0]], 0, 0)
.unwrap_err();
assert!(e.contains("which has 3"), "{e}");
}
#[test]
fn an_indexed_draw_with_a_restart_index_is_refused() {
let gs = shader("geometry.dxil");
let vertices = [
vertex(1.0, 0.0, 0.0),
vertex(2.0, 0.0, 0.0),
vertex(3.0, 0.0, 0.0),
];
let drawn = gs
.draw_indexed(
&constants(1.0),
&mut NoSampler,
&vertices,
&[2, 1, 0],
Some(0xFFFF),
Topology::TriangleList,
)
.unwrap();
let first = drawn
.iter()
.find(|s| (s.primitive, s.instance) == (0, 0))
.unwrap();
let xs: Vec<f32> = values(first).iter().map(|v| v[0]).collect();
assert_eq!(xs, [3.0, 2.0, 1.0]);
let e = gs
.draw_indexed(
&constants(1.0),
&mut NoSampler,
&vertices,
&[0, 1, 2, 0xFFFF, 0, 1, 2],
Some(0xFFFF),
Topology::TriangleStrip,
)
.unwrap_err();
assert!(e.contains("restart index 0xffff"), "{e}");
}