mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Add basic tests for SPIR-V back-end writer
This commit is contained in:
committed by
Dzmitry Malyshau
parent
a9228d2aed
commit
9940460fe6
@@ -3,7 +3,7 @@ use spirv::*;
|
||||
use std::iter;
|
||||
|
||||
impl PhysicalLayout {
|
||||
pub(crate) fn new(header: &crate::Header) -> Self {
|
||||
pub(super) fn new(header: &crate::Header) -> Self {
|
||||
let version: Word = ((header.version.0 as u32) << 16)
|
||||
| ((header.version.1 as u32) << 8)
|
||||
| header.version.2 as u32;
|
||||
@@ -17,7 +17,7 @@ impl PhysicalLayout {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(iter::once(self.magic_number));
|
||||
sink.extend(iter::once(self.version));
|
||||
sink.extend(iter::once(self.generator));
|
||||
@@ -27,7 +27,7 @@ impl PhysicalLayout {
|
||||
}
|
||||
|
||||
impl LogicalLayout {
|
||||
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(self.capabilities.iter().cloned());
|
||||
sink.extend(self.extensions.iter().cloned());
|
||||
sink.extend(self.ext_inst_imports.iter().cloned());
|
||||
@@ -43,7 +43,7 @@ impl LogicalLayout {
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub(crate) fn new(op: Op) -> Self {
|
||||
pub(super) fn new(op: Op) -> Self {
|
||||
Instruction {
|
||||
op,
|
||||
wc: 1, // Always start at 1 for the first word (OP + WC),
|
||||
@@ -53,30 +53,30 @@ impl Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_type(&mut self, id: Word) {
|
||||
pub(super) fn set_type(&mut self, id: Word) {
|
||||
assert!(self.type_id.is_none(), "Type can only be set once");
|
||||
self.type_id = Some(id);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn set_result(&mut self, id: Word) {
|
||||
pub(super) fn set_result(&mut self, id: Word) {
|
||||
assert!(self.result_id.is_none(), "Result can only be set once");
|
||||
self.result_id = Some(id);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn add_operand(&mut self, operand: Word) {
|
||||
pub(super) fn add_operand(&mut self, operand: Word) {
|
||||
self.operands.push(operand);
|
||||
self.wc += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn add_operands(&mut self, operands: Vec<Word>) {
|
||||
pub(super) fn add_operands(&mut self, operands: Vec<Word>) {
|
||||
for operand in operands.into_iter() {
|
||||
self.add_operand(operand)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_words(&self, sink: &mut impl Extend<Word>) {
|
||||
pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
|
||||
sink.extend(Some((self.wc << 16 | self.op as u32) as u32));
|
||||
sink.extend(self.type_id);
|
||||
sink.extend(self.result_id);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::back::spv::test_framework::*;
|
||||
use crate::back::spv::{helpers, Instruction, LogicalLayout, PhysicalLayout};
|
||||
use crate::Header;
|
||||
use spirv::*;
|
||||
@@ -76,7 +77,7 @@ fn test_logical_layout_in_words() {
|
||||
for instruction in instructions {
|
||||
let wc = instruction.wc as usize;
|
||||
let instruction_output = &output[index..index + wc];
|
||||
validate_instruction(instruction_output, instruction);
|
||||
validate_instruction(instruction_output, &instruction);
|
||||
index += wc;
|
||||
}
|
||||
}
|
||||
@@ -157,32 +158,7 @@ fn test_instruction_to_words() {
|
||||
|
||||
let mut output = vec![];
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), instruction);
|
||||
}
|
||||
|
||||
fn validate_instruction(words: &[Word], instruction: Instruction) {
|
||||
let mut inst_index = 0;
|
||||
let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);
|
||||
inst_index += 1;
|
||||
|
||||
assert_eq!(wc, words.len() as u16);
|
||||
assert_eq!(op, instruction.op as u16);
|
||||
|
||||
if instruction.type_id.is_some() {
|
||||
assert_eq!(words[inst_index], instruction.type_id.unwrap());
|
||||
inst_index += 1;
|
||||
}
|
||||
|
||||
if instruction.result_id.is_some() {
|
||||
assert_eq!(words[inst_index], instruction.result_id.unwrap());
|
||||
inst_index += 1;
|
||||
}
|
||||
|
||||
let mut op_index = 0;
|
||||
for i in inst_index..wc as usize {
|
||||
assert_eq!(words[i as usize], instruction.operands[op_index]);
|
||||
op_index += 1;
|
||||
}
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
fn to_word(bytes: &[u8]) -> Word {
|
||||
|
||||
@@ -2,6 +2,9 @@ mod helpers;
|
||||
mod layout;
|
||||
mod writer;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_framework;
|
||||
|
||||
#[cfg(test)]
|
||||
mod layout_tests;
|
||||
|
||||
@@ -25,7 +28,7 @@ struct PhysicalLayout {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct LogicalLayout {
|
||||
struct LogicalLayout {
|
||||
capabilities: Vec<Word>,
|
||||
extensions: Vec<Word>,
|
||||
ext_inst_imports: Vec<Word>,
|
||||
@@ -39,7 +42,7 @@ pub(crate) struct LogicalLayout {
|
||||
function_definitions: Vec<Word>,
|
||||
}
|
||||
|
||||
struct Instruction {
|
||||
pub(self) struct Instruction {
|
||||
op: Op,
|
||||
wc: u32,
|
||||
type_id: Option<Word>,
|
||||
|
||||
58
src/back/spv/test_framework.rs
Normal file
58
src/back/spv/test_framework.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use crate::back::spv::Instruction;
|
||||
use spirv::{Op, Word};
|
||||
|
||||
pub(super) struct SpecRequirements {
|
||||
pub(super) op: Op,
|
||||
pub(super) wc: u32,
|
||||
pub(super) type_id: bool,
|
||||
pub(super) result_id: bool,
|
||||
pub(super) operands: bool,
|
||||
}
|
||||
|
||||
/// Basic validation for checking if the instruction complies to the spec requirements
|
||||
pub(super) fn validate_spec_requirements(
|
||||
requirements: SpecRequirements,
|
||||
instruction: &Instruction,
|
||||
) {
|
||||
assert_eq!(requirements.op, instruction.op);
|
||||
|
||||
// Pass the assert if the minimum referred wc in the spec is met
|
||||
assert!(instruction.wc >= requirements.wc);
|
||||
|
||||
if requirements.type_id {
|
||||
assert!(instruction.type_id.is_some());
|
||||
}
|
||||
|
||||
if requirements.result_id {
|
||||
assert!(instruction.result_id.is_some());
|
||||
}
|
||||
|
||||
if requirements.operands {
|
||||
assert!(!instruction.operands.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn validate_instruction(words: &[Word], instruction: &Instruction) {
|
||||
let mut inst_index = 0;
|
||||
let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);
|
||||
inst_index += 1;
|
||||
|
||||
assert_eq!(wc, words.len() as u16);
|
||||
assert_eq!(op, instruction.op as u16);
|
||||
|
||||
if instruction.type_id.is_some() {
|
||||
assert_eq!(words[inst_index], instruction.type_id.unwrap());
|
||||
inst_index += 1;
|
||||
}
|
||||
|
||||
if instruction.result_id.is_some() {
|
||||
assert_eq!(words[inst_index], instruction.result_id.unwrap());
|
||||
inst_index += 1;
|
||||
}
|
||||
|
||||
let mut op_index = 0;
|
||||
for i in inst_index..wc as usize {
|
||||
assert_eq!(words[i as usize], instruction.operands[op_index]);
|
||||
op_index += 1;
|
||||
}
|
||||
}
|
||||
@@ -646,11 +646,9 @@ impl Writer {
|
||||
self.physical_layout.bound = self.id_count + 1;
|
||||
}
|
||||
|
||||
fn instruction_source(&self) -> Instruction {
|
||||
let version = 450u32;
|
||||
|
||||
fn instruction_source(&self, source_language: SourceLanguage, version: u32) -> Instruction {
|
||||
let mut instruction = Instruction::new(Op::Source);
|
||||
instruction.add_operand(SourceLanguage::GLSL as u32);
|
||||
instruction.add_operand(source_language as u32);
|
||||
instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes()));
|
||||
instruction
|
||||
}
|
||||
@@ -923,7 +921,8 @@ impl Writer {
|
||||
.to_words(&mut self.logical_layout.ext_inst_imports);
|
||||
|
||||
if self.writer_flags.contains(WriterFlags::DEBUG) {
|
||||
self.debugs.push(self.instruction_source());
|
||||
self.debugs
|
||||
.push(self.instruction_source(SourceLanguage::GLSL, 450));
|
||||
}
|
||||
|
||||
for (handle, function) in ir_module.functions.iter() {
|
||||
@@ -1000,3 +999,166 @@ impl Writer {
|
||||
words
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::back::spv::test_framework::*;
|
||||
use crate::back::spv::{Writer, WriterFlags};
|
||||
use crate::Header;
|
||||
use spirv::*;
|
||||
|
||||
#[test]
|
||||
fn test_writer_generate_id() {
|
||||
let mut writer = create_writer();
|
||||
|
||||
assert_eq!(writer.id_count, 0);
|
||||
writer.generate_id();
|
||||
assert_eq!(writer.id_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_add_capabilities() {
|
||||
let mut writer = create_writer();
|
||||
|
||||
assert_eq!(writer.capabilities.len(), 0);
|
||||
writer.try_add_capabilities(&[Capability::Shader]);
|
||||
assert_eq!(writer.capabilities.len(), 1);
|
||||
|
||||
writer.try_add_capabilities(&[Capability::Shader]);
|
||||
assert_eq!(writer.capabilities.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_capability() {
|
||||
let writer = create_writer();
|
||||
let instruction = writer.instruction_capability(Capability::Shader);
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::Capability,
|
||||
wc: 2,
|
||||
type_id: false,
|
||||
result_id: false,
|
||||
operands: true,
|
||||
};
|
||||
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_ext_inst_import() {
|
||||
let mut writer = create_writer();
|
||||
let import_name = "GLSL.std.450";
|
||||
let instruction = writer.instruction_ext_inst_import(import_name);
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::ExtInstImport,
|
||||
wc: 2,
|
||||
type_id: false,
|
||||
result_id: true,
|
||||
operands: true,
|
||||
};
|
||||
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_memory_model() {
|
||||
let mut writer = create_writer();
|
||||
let instruction = writer.instruction_memory_model();
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::MemoryModel,
|
||||
wc: 3,
|
||||
type_id: false,
|
||||
result_id: false,
|
||||
operands: true,
|
||||
};
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_source() {
|
||||
let writer = create_writer();
|
||||
let version = 450;
|
||||
let instruction = writer.instruction_source(SourceLanguage::GLSL, version);
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::Source,
|
||||
wc: 3,
|
||||
type_id: false,
|
||||
result_id: false,
|
||||
operands: true,
|
||||
};
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_label() {
|
||||
let mut writer = create_writer();
|
||||
let instruction = writer.instruction_label();
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::Label,
|
||||
wc: 2,
|
||||
type_id: false,
|
||||
result_id: true,
|
||||
operands: false,
|
||||
};
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_function_end() {
|
||||
let writer = create_writer();
|
||||
let instruction = writer.instruction_function_end();
|
||||
let mut output = vec![];
|
||||
|
||||
let requirements = SpecRequirements {
|
||||
op: Op::FunctionEnd,
|
||||
wc: 1,
|
||||
type_id: false,
|
||||
result_id: false,
|
||||
operands: false,
|
||||
};
|
||||
validate_spec_requirements(requirements, &instruction);
|
||||
|
||||
instruction.to_words(&mut output);
|
||||
validate_instruction(output.as_slice(), &instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_physical_layout() {
|
||||
let mut writer = create_writer();
|
||||
assert_eq!(writer.physical_layout.bound, 0);
|
||||
writer.write_physical_layout();
|
||||
assert_eq!(writer.physical_layout.bound, 1);
|
||||
}
|
||||
|
||||
fn create_writer() -> Writer {
|
||||
let header = Header {
|
||||
generator: 0,
|
||||
version: (1, 0, 0),
|
||||
};
|
||||
Writer::new(&header, WriterFlags::NONE)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user