#![allow(clippy::unwrap_used)]
use dxbc_reader::decode::{Declared, ProgramType, Register, Selection};
fn instruction(token: u32, operands: &[u32]) -> Vec<u32> {
let mut v = vec![token | (((operands.len() + 1) as u32) << 24)];
v.extend_from_slice(operands);
v
}
fn masked(kind: u32, index: u32, mask: u32) -> [u32; 2] {
[2 | mask << 4 | kind << 12 | 1 << 20, index]
}
fn swizzled(kind: u32, index: u32) -> [u32; 2] {
[2 | 1 << 2 | 0b11_10_01_00 << 4 | kind << 12 | 1 << 20, index]
}
fn stream(version: u32, body: &[Vec<u32>]) -> Vec<u32> {
let mut v = vec![version, 0];
for b in body {
v.extend(b);
}
v[1] = v.len() as u32;
v
}
fn vertex() -> Vec<u32> {
let mut siv = masked(2, 0, 15).to_vec();
siv.push(1);
stream(
0x10050,
&[
instruction(95, &masked(1, 0, 15)),
instruction(103, &siv),
instruction(104, &[1]),
instruction(
54 | 1 << 13,
&[masked(0, 0, 15).to_vec(), swizzled(1, 0).to_vec()].concat(),
),
instruction(54, &[masked(2, 0, 15).to_vec(), swizzled(0, 0).to_vec()].concat()),
instruction(62, &[]),
],
)
}
#[test]
fn a_vertex_program_decodes_to_typed_declarations_and_instructions() {
let d = dxbc_reader::decode(&vertex()).unwrap();
assert_eq!((d.kind, d.major, d.minor), (ProgramType::Vertex, 5, 0));
assert_eq!(d.target(), "vs_5_0");
assert_eq!(d.declarations.len(), 3);
let Declared::Input {
operand,
interpolation,
name,
} = &d.declarations[0].kind
else {
panic!("{:?}", d.declarations[0]);
};
assert_eq!(operand.register, Register::Input);
assert_eq!(operand.selection, Selection::Mask(15));
assert_eq!((*interpolation, *name), (None, None));
let Declared::Output { name, .. } = &d.declarations[1].kind else {
panic!("{:?}", d.declarations[1]);
};
assert_eq!(*name, Some(1));
assert!(matches!(d.declarations[2].kind, Declared::Temps(1)));
let names: Vec<_> = d.instructions.iter().map(|i| i.name()).collect();
assert_eq!(names, ["mov", "mov", "ret"]);
assert!(d.instructions[0].saturate && !d.instructions[1].saturate);
assert_eq!(
d.instructions[0].operands[1].selection,
Selection::Swizzle([0, 1, 2, 3])
);
}
#[test]
fn declarations_carry_their_fields() {
let cb = [8 << 12 | 2 << 20, 3, 12];
let sampler = [6 << 12 | 1 << 20, 1];
let texture = [7 << 12 | 1 << 20, 5, 0x5555];
let structured = [7 << 12 | 1 << 20, 2, 64];
let d = dxbc_reader::decode(&stream(
0x50,
&[
instruction(89 | 1 << 11, &cb),
instruction(90 | 1 << 11, &sampler),
instruction(88 | 3 << 11 | 4 << 16, &texture),
instruction(162, &structured),
instruction(62, &[]),
],
))
.unwrap();
let kinds: Vec<_> = d.declarations.iter().map(|d| &d.kind).collect();
assert!(matches!(
kinds[0],
Declared::ConstantBuffer {
slot: 3,
rows: 12,
dynamic: true,
..
}
));
assert!(matches!(kinds[1], Declared::Sampler { slot: 1, mode: 1 }));
assert!(matches!(
kinds[2],
Declared::Resource {
slot: 5,
dimension: 3,
samples: 4,
returns: [5, 5, 5, 5],
..
}
));
assert!(matches!(
kinds[3],
Declared::ResourceStructured { slot: 2, stride: 64 }
));
}
#[test]
fn unreadable_tokens_are_errors_naming_the_word() {
assert!(
dxbc_reader::decode(&[0x70050, 2])
.unwrap_err()
.detail
.contains("program type 7")
);
assert!(
dxbc_reader::decode(&[0x50, 5])
.unwrap_err()
.detail
.contains("declared length")
);
let bad = stream(
0x50,
&[instruction(54, &[2 | 15 << 4 | 99 << 12 | 1 << 20, 0, 1])],
);
let e = dxbc_reader::decode(&bad).unwrap_err();
assert!(e.detail.contains("operand register type 99"), "{e}");
let mut short = vertex();
short.truncate(short.len() - 2);
short[1] = short.len() as u32;
assert!(dxbc_reader::decode(&short).is_err());
for n in 0..vertex().len() {
let mut cut = vertex()[..n].to_vec();
if n >= 2 {
cut[1] = n as u32;
}
let _ = dxbc_reader::decode(&cut);
}
}
#[test]
fn a_geometry_program_s_instance_count_is_a_declaration() {
let d = dxbc_reader::decode(&stream(0x20050, &[instruction(206, &[2]), instruction(62, &[])])).unwrap();
assert_eq!(d.instructions.len(), 1);
assert!(matches!(
&d.declarations[0].kind,
Declared::Other { opcode: 206, words, .. } if words == &[2]
));
}