use iridia::output_uses::{AlphaDither, AlphaExpr, Known, alpha_tests, discard_compares};
use iridia_interface::AlphaComparison;
#[test]
fn alpha_reference_requires_structural_data_and_a_disjoint_dither_source() {
macro_rules! fixture {
($entry:literal, $expected:expr, $texture:literal) => {
(
$entry,
include_bytes!(concat!("fixtures/alpha_reference_", $entry, ".dxbc")).as_slice(),
include_bytes!(concat!("fixtures/alpha_reference_", $entry, ".dxil")).as_slice(),
$expected,
$texture,
)
};
}
let fixtures = [
fixture!("direct", Some(AlphaComparison::Output), "dither_map"),
fixture!("transformed", Some(AlphaComparison::Transformed), "dither_map"),
fixture!(
"dithered",
Some(AlphaComparison::Dithered { scale_word: 1 }),
"dither_map"
),
fixture!(
"product",
Some(AlphaComparison::Dithered { scale_word: 1 }),
"dither_map"
),
fixture!("same_source", Some(AlphaComparison::Unwritten), "base_map"),
fixture!("shared_scale", Some(AlphaComparison::Unwritten), "dither_map"),
fixture!("control_only", Some(AlphaComparison::Unwritten), "dither_map"),
fixture!("coordinate_only", Some(AlphaComparison::Unwritten), "dither_map"),
fixture!("unrelated", None, "dither_map"),
];
for (name, dxbc, dxil, expected, dither_name) in fixtures {
for (compiler, p) in [
("fxc", iridia_dxbc::lift(dxbc).unwrap()),
("dxc", iridia_dxil::lift(dxil).unwrap()),
] {
let g = p.graph().unwrap();
let buffer = g.reads.buffer_at(0, 0).unwrap();
let dither = g
.interface
.resources
.iter()
.find(|r| r.provenance.name == dither_name)
.map(|r| AlphaDither {
texture: r.id,
scale_word: 1,
});
assert_eq!(
discard_compares(&g, buffer, 0, dither),
expected,
"{name} ({compiler})"
);
if matches!(expected, Some(AlphaComparison::Dithered { .. })) {
assert_eq!(
discard_compares(&g, buffer, 0, None),
Some(AlphaComparison::Unwritten),
"dither needs its producer's role"
);
}
}
}
}
fn compiled(entry: &str) -> Vec<(&'static str, iridia::Program)> {
let read = |extension: &str| {
let path = format!(
"{}/tests/fixtures/alpha_reference_{entry}.{extension}",
env!("CARGO_MANIFEST_DIR")
);
std::fs::read(&path)
.unwrap_or_else(|e| panic!("{path}: {e} (compile alpha_reference.hlsl's `{entry}`)"))
};
vec![
(
"fxc",
iridia_dxbc::lift(&read("dxbc")).unwrap_or_else(|e| panic!("{entry}.dxbc: {e}")),
),
(
"dxc",
iridia_dxil::lift(&read("dxil")).unwrap_or_else(|e| panic!("{entry}.dxil: {e}")),
),
]
}
#[test]
fn a_test_of_two_texture_alphas_is_read_exactly_and_solved_only_where_one_varies() {
for (compiler, p) in compiled("unwritten") {
let g = p.graph().unwrap();
let buffer = g.reads.buffer_at(0, 0).unwrap();
assert_eq!(
discard_compares(&g, buffer, 0, None),
Some(AlphaComparison::Unwritten),
"{compiler}"
);
let tests: Vec<_> = alpha_tests(&g).into_iter().filter(|t| t.word == 0).collect();
assert_eq!(tests.len(), 1, "{compiler}: {tests:?}");
let tested = &tests[0].tested;
assert!(
tested.is_exact() && tested.has_alpha_texel(),
"{compiler}: {tested}"
);
let samples: Vec<_> = tested
.leaves()
.into_iter()
.filter(|l| matches!(l, AlphaExpr::Texel { .. }))
.collect();
assert_eq!(samples.len(), 2, "{compiler}: {tested}");
let name_of = |resource| {
g.interface
.resources
.iter()
.find(|r| r.id == resource)
.map(|r| r.provenance.name.clone())
.unwrap_or_default()
};
let both = tested.linear(&mut |leaf| match leaf {
AlphaExpr::Texel { .. } => Known::Varies,
_ => Known::Unknown,
});
let why = both.expect_err(compiler);
assert!(why.contains("two varying samples"), "{compiler}: {why}");
let mut half_detail = |leaf: &AlphaExpr| match leaf {
AlphaExpr::Texel { resource, .. } if name_of(*resource) == "detail_map" => Known::Value(0.5),
AlphaExpr::Texel { .. } => Known::Varies,
_ => Known::Unknown,
};
let linear = tested.linear(&mut half_detail).expect(compiler);
assert_eq!((linear.scale, linear.offset), (0.5, 0.), "{compiler}");
assert_eq!(
linear.sample.map(|(_, r, lane)| (name_of(r), lane)),
Some(("base_map".to_owned(), 3)),
"{compiler}"
);
assert_eq!(linear.cutoff(0.25).unwrap(), Some(0.5), "{compiler}");
let one = tested
.linear(&mut |leaf| match leaf {
AlphaExpr::Texel { resource, .. } if name_of(*resource) == "detail_map" => Known::Value(1.),
AlphaExpr::Texel { .. } => Known::Varies,
_ => Known::Unknown,
})
.expect(compiler);
assert_eq!(one.cutoff(0.3).unwrap(), Some(0.3), "{compiler}");
let zero = tested
.linear(&mut |leaf| match leaf {
AlphaExpr::Texel { resource, .. } if name_of(*resource) == "detail_map" => Known::Value(0.),
AlphaExpr::Texel { .. } => Known::Varies,
_ => Known::Unknown,
})
.expect(compiler);
assert!(zero.cutoff(0.3).is_err(), "{compiler}");
assert_eq!(
zero.cutoff(-100.).unwrap(),
None,
"{compiler}: a reference below the range never discards"
);
}
}
#[test]
fn a_dither_bound_to_its_default_adds_exactly_its_value() {
for (compiler, p) in compiled("unwritten_dither") {
let g = p.graph().unwrap();
let buffer = g.reads.buffer_at(0, 0).unwrap();
let tests: Vec<_> = alpha_tests(&g).into_iter().filter(|t| t.word == 0).collect();
assert_eq!(tests.len(), 1, "{compiler}: {tests:?}");
let tested = &tests[0].tested;
let name_of = |resource| {
g.interface
.resources
.iter()
.find(|r| r.id == resource)
.map(|r| r.provenance.name.clone())
.unwrap_or_default()
};
let solve = |dither: f64, scale: Option<f64>| {
tested.linear(&mut |leaf| match leaf {
AlphaExpr::Texel { resource, .. } if name_of(*resource) == "dither_map" => {
Known::Value(dither)
}
AlphaExpr::Texel { .. } => Known::Varies,
AlphaExpr::Word { buffer: b, word: 1 } if *b == buffer => {
scale.map_or(Known::Unknown, Known::Value)
}
_ => Known::Unknown,
})
};
let neutral = solve(0.5, Some(0.25)).expect(compiler);
assert_eq!((neutral.scale, neutral.offset), (1., 0.), "{compiler}");
assert_eq!(neutral.cutoff(0.4).unwrap(), Some(0.4), "{compiler}");
let bright = solve(1., Some(0.25)).expect(compiler);
assert!(
(bright.cutoff(0.4).unwrap().unwrap() - 0.275).abs() < 1e-12,
"{compiler}"
);
let moves = |scale: f64| {
let (a, b) = (solve(0.5, Some(scale)).unwrap(), solve(1.5, Some(scale)).unwrap());
(a.scale, a.offset) != (b.scale, b.offset)
};
assert!(!moves(0.), "{compiler}");
assert!(moves(0.5), "{compiler}");
let why = solve(0.5, None).expect_err(compiler);
assert!(why.contains("is not stated"), "{compiler}: {why}");
}
}