#[cfg(test)] mod tests; use crate::compiler::Module; use std::fmt::Write; use std::fmt; use super::StorageClass; #[derive(Debug, PartialEq, Clone)] pub enum Type { Pointer(Box, StorageClass), Vector(Box, u32), Float(u32), Int(u32), Unsigned(u32), } impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Type::Pointer(typ, storage_class) => { match storage_class { StorageClass::Input => write!(f, "*{}i", typ), StorageClass::Output => write!(f, "*{}o", typ), StorageClass::Undefined => panic!("Bound a non-declared variable"), } }, Type::Vector(typ, size) => write!(f, "v{}{}", size, typ), Type::Float(size) => write!(f, "f{}", size), Type::Int(size) => write!(f, "s{}", size), Type::Unsigned(size) => write!(f, "u{}", size), } } } pub fn parse_type(typ: String) -> Type { // pointers have the format *i or *o, for the storage class // floats have the format f, so f32, f64... // ints have the format s, so s32, s64... // unsigned ints have the format u, so u32, u64... // So, *v4f32i is a pointer to a vector of 4 floats, with the storage class Input. // *v4f32o is a pointer to a vector of 4 floats, with the storage class Output. let c = typ.chars().next().unwrap(); match c { '*' => { let mut chars = typ.chars(); chars.next(); let typ = chars.collect::(); let storage_class = match typ.chars().last().unwrap() { 'i' => StorageClass::Input, 'o' => StorageClass::Output, _ => panic!("Invalid storage class"), }; let typ = typ.chars().take(typ.len() - 1).collect::(); Type::Pointer(Box::new(parse_type(typ)), storage_class) }, 'v' => { let mut chars = typ.chars(); chars.next(); let size = chars.next().unwrap().to_digit(10).unwrap(); let typ = chars.collect::(); Type::Vector(Box::new(parse_type(typ)), size) }, 'f' => { let size = typ.chars().skip(1).collect::().parse().unwrap(); Type::Float(size) }, 's' => { let size = typ.chars().skip(1).collect::().parse().unwrap(); Type::Int(size) }, 'u' => { let size = typ.chars().skip(1).collect::().parse().unwrap(); Type::Unsigned(size) }, _ => panic!("Invalid type"), } } fn emit_type(typ: Type, ops: &mut Vec<(String, String)>) { match typ { Type::Unsigned(size) => { ops.push((format!("%u{}", size).to_string(), format!("OpTypeInt {}", size))); }, Type::Int(size) => { ops.push((format!("%s{}", size).to_string(), format!("OpTypeInt {}", size))); }, Type::Float(size) => { ops.push((format!("%f{}", size).to_string(), format!("OpTypeFloat {}", size))); }, Type::Vector(typ, size) => { emit_type(*typ.clone(), ops); let typ_id = format!("%{}", *typ.clone()); ops.push((format!("%v{}{}", typ_id, size).to_string(), format!("OpTypeVector {} {}", typ_id, size))) }, Type::Pointer(typ, storage_class) => { emit_type(*typ.clone(), ops); let typ_id = format!("%{}", *typ.clone()); let storage_class = match storage_class { StorageClass::Input => "Input", StorageClass::Output => "Output", StorageClass::Undefined => panic!("Bound a non-declared variable"), }; ops.push((format!("%{}{}", typ_id, storage_class).to_string(), format!("OpTypePointer {} {}", storage_class, typ_id))) }, } } fn fix_name(name: &String) -> String { format!("%{}", name.clone().replace("-", "_")) } pub fn spirv_meta(module: Module) -> String { let mut spirv_asm = String::new(); let mut ops: Vec<(Option, Vec)> = Vec::new(); let capabilities: Vec = module.capabilities.iter() .map(|c| format!("{:?}", c)) .collect(); for cap in capabilities { ops.push((None, vec!["OpCapability".to_string(), cap])); } let memory_model_address = match module.memory_model.addressing_model { crate::compiler::AddressingModel::Logical => "Logical", crate::compiler::AddressingModel::Physical32 => "Physical32", crate::compiler::AddressingModel::Physical64 => "Physical64", crate::compiler::AddressingModel::PhysicalStorageBuffer64 => "PhysicalStorageBuffer64", }; let memory_model_model = match module.memory_model.memory_model { crate::compiler::MemoryModel::Simple => "Simple", crate::compiler::MemoryModel::GLSL450 => "GLSL450", crate::compiler::MemoryModel::OpenCL => "OpenCL", _ => todo!(), }; ops.push((None, vec!["OpMemoryModel".to_string(), memory_model_address.to_string(), memory_model_model.to_string()])); for entry in module.entry_points { let exec_model = match entry.execution_model { crate::compiler::ExecutionModel::Fragment => "Fragment", crate::compiler::ExecutionModel::Vertex => "Vertex", }; let name = entry.name; let interface: Vec = entry.interface.iter() .map(|i| fix_name(&i.to_string())) .collect(); let exec_mode = match entry.execution_mode { crate::compiler::ExecutionMode::OriginUpperLeft => "OriginUpperLeft", }; ops.push((None, vec!["OpEntryPoint".to_string(), exec_model.to_string(), fix_name(&name), format!("\"{}\"", name), interface.join(" ")])); ops.push((None, vec!["OpExecutionMode".to_string(), fix_name(&name), exec_mode.to_string()])); } for global in module.globals { let name = fix_name(&global.name); let _typ = global.typ; let storage_class = match global.storage_class { crate::compiler::StorageClass::Input => "Input", crate::compiler::StorageClass::Output => "Output", crate::compiler::StorageClass::Undefined => panic!("Bound a non-declared variable"), }; let typ_id = format!("%{}", _typ); let mut type_ops = Vec::new(); emit_type(parse_type(_typ), &mut type_ops); for op in type_ops { ops.push((Some(op.0), vec![op.1])); } ops.push((Some(name.clone()), vec!["OpVariable".to_string(), typ_id.to_string(), storage_class.to_string()])); for dec in global.decorations { // Decorations have the format Location 0, or Builtin FragCoord let dec = match dec { crate::compiler::Decoration::Location(loc) => format!("Location {}", loc), crate::compiler::Decoration::BuiltIn(builtin) => { let builtin = match builtin { crate::compiler::BuiltinDecoration::FragCoord => "FragCoord", }; format!("BuiltIn {}", builtin) }, }; ops.push((None, vec!["OpDecorate".to_string(), name.clone(), dec])); } } for op in ops { if op.0.is_some() { write!(spirv_asm, "{} = ", op.0.unwrap()).unwrap(); } for arg in op.1 { write!(spirv_asm, "{} ", arg).unwrap(); } writeln!(spirv_asm).unwrap(); } spirv_asm }