diff --git a/shader-parser/.gitignore b/shader-parser/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/shader-parser/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/shader-parser/Cargo.toml b/shader-parser/Cargo.toml new file mode 100644 index 00000000..755ab390 --- /dev/null +++ b/shader-parser/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "shader-parser" +version = "0.1.0" +authors = ["Pierre Krieger "] diff --git a/shader-parser/src/lib.rs b/shader-parser/src/lib.rs new file mode 100644 index 00000000..70c9e9d7 --- /dev/null +++ b/shader-parser/src/lib.rs @@ -0,0 +1,37 @@ +use std::io::Error as IoError; +use std::io::Read; + +pub use parse::ParseError; + +mod parse; + +pub fn reflect(mut spirv: R) -> Result + where R: Read +{ + let mut data = Vec::new(); + try!(spirv.read_to_end(&mut data)); + + // now parsing the document + let doc = try!(parse::parse_spirv(&data)); + + unimplemented!() +} + +pub enum Error { + IoError(IoError), + ParseError(ParseError), +} + +impl From for Error { + #[inline] + fn from(err: IoError) -> Error { + Error::IoError(err) + } +} + +impl From for Error { + #[inline] + fn from(err: ParseError) -> Error { + Error::ParseError(err) + } +} diff --git a/shader-parser/src/parse.rs b/shader-parser/src/parse.rs new file mode 100644 index 00000000..dcfc12db --- /dev/null +++ b/shader-parser/src/parse.rs @@ -0,0 +1,298 @@ + +pub fn parse_spirv(data: &[u8]) -> Result { + if data.len() < 20 { + return Err(ParseError::MissingHeader); + } + + // we need to determine whether we are in big endian order or little endian order depending + // on the magic number at the start of the file + let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 { + // big endian + data.chunks(4).map(|c| { + ((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) | c[3] as u32 + }).collect::>() + + } else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 { + // little endian + data.chunks(4).map(|c| { + ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32 + }).collect::>() + + } else { + return Err(ParseError::MissingHeader); + }; + + parse_u32s(&data) +} + +fn parse_u32s(i: &[u32]) -> Result { + if i.len() < 5 { + return Err(ParseError::MissingHeader); + } + + if i[0] != 0x07230203 { + return Err(ParseError::WrongHeader); + } + + let version = (((i[1] & 0x00ff0000) >> 16) as u8, ((i[1] & 0x0000ff00) >> 8) as u8); + + let instructions = { + let mut ret = Vec::new(); + let mut i = &i[5..]; + while i.len() >= 1 { + let (instruction, rest) = try!(parse_instruction(i)); + ret.push(instruction); + i = rest; + } + ret + }; + + Ok(Spirv { + version: version, + bound: i[3], + instructions: instructions, + }) +} + +#[derive(Debug, Clone)] +pub enum ParseError { + MissingHeader, + WrongHeader, + IncompleteInstruction, + UnknownCapability(u32), + UnknownExecutionModel(u32), +} + +#[derive(Debug, Clone)] +pub struct Spirv { + pub version: (u8, u8), + pub bound: u32, + pub instructions: Vec, +} + +#[derive(Debug, Clone)] +pub enum Instruction { + Unknown(u16, Vec), + Nop, + ExtInstImport { result_id: u32, name: String }, + EntryPoint { execution: ExecutionModel, id: u32, name: String, interface: Vec }, + Capability(Capability), + FunctionEnd, + Kill, + Return, +} + +fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> { + assert!(i.len() >= 1); + + let word_count = (i[0] >> 16) as usize; + assert!(word_count >= 1); + let opcode = (i[0] & 0xffff) as u16; + + if i.len() < word_count { + return Err(ParseError::IncompleteInstruction); + } + + let opcode = try!(decode_instruction(opcode, &i[1 .. word_count])); + Ok((opcode, &i[word_count..])) +} + +fn decode_instruction(opcode: u16, operands: &[u32]) -> Result { + Ok(match opcode { + 0 => Instruction::Nop, + 11 => Instruction::ExtInstImport { + result_id: operands[0], + name: parse_string(&operands[1..]).0 + }, + 15 => { + let (n, r) = parse_string(&operands[2..]); + Instruction::EntryPoint { + execution: try!(ExecutionModel::from_num(operands[0])), + id: operands[1], + name: n, + interface: r.to_owned(), + } + }, + 17 => Instruction::Capability(try!(Capability::from_num(operands[0]))), + 56 => Instruction::FunctionEnd, + 252 => Instruction::Kill, + 253 => Instruction::Return, + _ => Instruction::Unknown(opcode, operands.to_owned()), + }) +} + +fn parse_string(data: &[u32]) -> (String, &[u32]) { + let bytes = data.iter().flat_map(|&n| { + let b1 = (n & 0xff) as u8; + let b2 = ((n >> 8) & 0xff) as u8; + let b3 = ((n >> 16) & 0xff) as u8; + let b4 = ((n >> 24) & 0xff) as u8; + vec![b1, b2, b3, b4].into_iter() + }).take_while(|&b| b != 0).collect::>(); + + let r = 1 + bytes.len() / 4; + let s = String::from_utf8(bytes).expect("Shader content is not UTF-8"); + + (s, &data[r..]) +} + +#[derive(Debug, Clone)] +pub enum Capability { + Matrix, + Shader, + Geometry, + Tessellation, + Addresses, + Linkage, + Kernel, + Vector16, + Float16Buffer, + Float16, + Float64, + Int64, + Int64Atomics, + ImageBasic, + ImageReadWrite, + ImageMipmap, + Pipes, + Groups, + DeviceEnqueue, + LiteralSampler, + AtomicStorage, + Int16, + TessellationPointSize, + GeometryPointSize, + ImageGatherExtended, + StorageImageMultisample, + UniformBufferArrayDynamicIndexing, + SampledImageArrayDynamicIndexing, + StorageBufferArrayDynamicIndexing, + StorageImageArrayDynamicIndexing, + ClipDistance, + CullDistance, + ImageCubeArray, + SampleRateShading, + ImageRect, + SampledRect, + GenericPointer, + Int8, + InputAttachment, + SparseResidency, + MinLod, + Sampled1D, + Image1D, + SampledCubeArray, + SampledBuffer, + ImageBuffer, + ImageMSArray, + StorageImageExtendedFormats, + ImageQuery, + DerivativeControl, + InterpolationFunction, + TransformFeedback, + GeometryStreams, + StorageImageReadWithoutFormat, + StorageImageWriteWithoutFormat, +} + +impl Capability { + fn from_num(num: u32) -> Result { + match num { + 0 => Ok(Capability::Matrix), + 1 => Ok(Capability::Shader), + 2 => Ok(Capability::Geometry), + 3 => Ok(Capability::Tessellation), + 4 => Ok(Capability::Addresses), + 5 => Ok(Capability::Linkage), + 6 => Ok(Capability::Kernel), + 7 => Ok(Capability::Vector16), + 8 => Ok(Capability::Float16Buffer), + 9 => Ok(Capability::Float16), + 10 => Ok(Capability::Float64), + 11 => Ok(Capability::Int64), + 12 => Ok(Capability::Int64Atomics), + 13 => Ok(Capability::ImageBasic), + 14 => Ok(Capability::ImageReadWrite), + 15 => Ok(Capability::ImageMipmap), + 17 => Ok(Capability::Pipes), + 18 => Ok(Capability::Groups), + 19 => Ok(Capability::DeviceEnqueue), + 20 => Ok(Capability::LiteralSampler), + 21 => Ok(Capability::AtomicStorage), + 22 => Ok(Capability::Int16), + 23 => Ok(Capability::TessellationPointSize), + 24 => Ok(Capability::GeometryPointSize), + 25 => Ok(Capability::ImageGatherExtended), + 27 => Ok(Capability::StorageImageMultisample), + 28 => Ok(Capability::UniformBufferArrayDynamicIndexing), + 29 => Ok(Capability::SampledImageArrayDynamicIndexing), + 30 => Ok(Capability::StorageBufferArrayDynamicIndexing), + 31 => Ok(Capability::StorageImageArrayDynamicIndexing), + 32 => Ok(Capability::ClipDistance), + 33 => Ok(Capability::CullDistance), + 34 => Ok(Capability::ImageCubeArray), + 35 => Ok(Capability::SampleRateShading), + 36 => Ok(Capability::ImageRect), + 37 => Ok(Capability::SampledRect), + 38 => Ok(Capability::GenericPointer), + 39 => Ok(Capability::Int8), + 40 => Ok(Capability::InputAttachment), + 41 => Ok(Capability::SparseResidency), + 42 => Ok(Capability::MinLod), + 43 => Ok(Capability::Sampled1D), + 44 => Ok(Capability::Image1D), + 45 => Ok(Capability::SampledCubeArray), + 46 => Ok(Capability::SampledBuffer), + 47 => Ok(Capability::ImageBuffer), + 48 => Ok(Capability::ImageMSArray), + 49 => Ok(Capability::StorageImageExtendedFormats), + 50 => Ok(Capability::ImageQuery), + 51 => Ok(Capability::DerivativeControl), + 52 => Ok(Capability::InterpolationFunction), + 53 => Ok(Capability::TransformFeedback), + 54 => Ok(Capability::GeometryStreams), + 55 => Ok(Capability::StorageImageReadWithoutFormat), + 56 => Ok(Capability::StorageImageWriteWithoutFormat), + _ => Err(ParseError::UnknownCapability(num)), + } + } +} + +#[derive(Debug, Clone)] +pub enum ExecutionModel { + Vertex, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment, + GLCompute, + Kernel, +} + +impl ExecutionModel { + fn from_num(num: u32) -> Result { + match num { + 0 => Ok(ExecutionModel::Vertex), + 1 => Ok(ExecutionModel::TessellationControl), + 2 => Ok(ExecutionModel::TessellationEvaluation), + 3 => Ok(ExecutionModel::Geometry), + 4 => Ok(ExecutionModel::Fragment), + 5 => Ok(ExecutionModel::GLCompute), + 6 => Ok(ExecutionModel::Kernel), + _ => Err(ParseError::UnknownExecutionModel(num)), + } + } +} + +#[cfg(test)] +mod test { + use parse; + + #[test] + fn test() { + let data = include_bytes!("../tests/frag.spv"); + let doc = parse::parse_spirv(data).unwrap(); + + println!("{:?}", doc); + } +} diff --git a/shader-parser/tests/frag.spv b/shader-parser/tests/frag.spv new file mode 100644 index 00000000..3e8a07b7 Binary files /dev/null and b/shader-parser/tests/frag.spv differ