mod consts;
mod parse;
mod instr;
mod reflect;
pub mod sym;
pub mod error;
pub mod ty;
use std::convert::TryInto;
use std::collections::{HashMap};
use std::fmt;
use std::iter::FromIterator;
use std::ops::Deref;
use parse::{Instrs, Instr};
use ty::{Type, DescriptorType};
pub use sym::*;
pub use error::*;
pub use spirv_headers::ExecutionModel;
use num_derive::FromPrimitive;
#[derive(Debug, Default, Clone)]
pub struct SpirvBinary(Vec<u32>);
impl From<Vec<u32>> for SpirvBinary {
fn from(x: Vec<u32>) -> Self { SpirvBinary(x) }
}
impl FromIterator<u32> for SpirvBinary {
fn from_iter<I: IntoIterator<Item=u32>>(iter: I) -> Self { SpirvBinary(iter.into_iter().collect::<Vec<u32>>()) }
}
impl From<Vec<u8>> for SpirvBinary {
fn from(x: Vec<u8>) -> Self {
if x.len() == 0 { return SpirvBinary::default(); }
x.chunks_exact(4)
.map(|x| x.try_into().unwrap())
.map(match x[0] {
0x03 => u32::from_le_bytes,
0x07 => u32::from_be_bytes,
_ => return SpirvBinary::default(),
})
.collect::<SpirvBinary>()
}
}
impl SpirvBinary {
pub(crate) fn instrs<'a>(&'a self) -> Instrs<'a> { Instrs::new(&self.0) }
pub fn reflect(&self) -> Result<Box<[EntryPoint]>> {
reflect::reflect_spirv(&self)
}
pub fn words(&self) -> &[u32] {
&self.0
}
pub fn bytes(&self) -> &[u8] {
unsafe {
let len = self.0.len() * std::mem::size_of::<u32>();
let ptr = self.0.as_ptr() as *const u8;
std::slice::from_raw_parts(ptr, len)
}
}
pub fn into_words(self) -> Vec<u32> { self.0 }
}
pub(crate) fn hash<H: std::hash::Hash>(h: &H) -> u64 {
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
let mut hasher = DefaultHasher::new();
h.hash(&mut hasher);
hasher.finish()
}
#[derive(PartialEq, Eq, Hash, Default, Clone, Copy)]
pub struct InterfaceLocation(u32, u32);
impl InterfaceLocation {
pub fn new(loc: u32, comp: u32) -> Self { InterfaceLocation(loc, comp) }
pub fn into_inner(self) -> (u32, u32) { (self.0, self.1) }
}
impl fmt::Display for InterfaceLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(loc={}, comp={})", self.0, self.1)
}
}
impl fmt::Debug for InterfaceLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (self as &dyn fmt::Display).fmt(f) }
}
#[derive(PartialEq, Eq, Hash, Default, Clone, Copy)]
pub struct DescriptorBinding(Option<(u32, u32)>);
impl DescriptorBinding {
pub fn push_const() -> Self { DescriptorBinding(None) }
pub fn desc_bind(desc_set: u32, bind_point: u32) -> Self { DescriptorBinding(Some((desc_set, bind_point))) }
pub fn is_push_const(&self) -> bool { self.0.is_none() }
pub fn is_desc_bind(&self) -> bool { self.0.is_some() }
pub fn into_inner(self) -> Option<(u32, u32)> { self.0 }
}
impl fmt::Display for DescriptorBinding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some((set, bind)) = self.0 {
write!(f, "(set={}, bind={})", set, bind)
} else {
write!(f, "(push_constant)")
}
}
}
impl fmt::Debug for DescriptorBinding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (self as &dyn fmt::Display).fmt(f) }
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub(crate) enum ResourceLocator {
Input(InterfaceLocation),
Output(InterfaceLocation),
Descriptor(DescriptorBinding),
}
#[derive(Debug)]
pub struct InterfaceVariableResolution<'a> {
pub location: InterfaceLocation,
pub ty: &'a Type,
}
#[derive(Debug)]
pub struct DescriptorResolution<'a> {
pub desc_bind: DescriptorBinding,
pub desc_ty: &'a DescriptorType,
pub member_var_res: Option<MemberVariableResolution<'a>>,
}
#[derive(Debug)]
pub struct MemberVariableResolution<'a> {
pub offset: usize,
pub ty: &'a Type,
}
#[repr(u32)]
#[derive(Debug, FromPrimitive, Clone, Copy, PartialEq, Eq)]
pub enum AccessType {
ReadOnly = 1,
WriteOnly = 2,
ReadWrite = 3,
}
#[derive(Default, Clone)]
pub struct Manifest {
pub(crate) input_map: HashMap<InterfaceLocation, Type>,
pub(crate) output_map: HashMap<InterfaceLocation, Type>,
pub(crate) desc_map: HashMap<DescriptorBinding, DescriptorType>,
pub(crate) var_name_map: HashMap<String, ResourceLocator>,
pub(crate) desc_access_map: HashMap<DescriptorBinding, AccessType>
}
impl Manifest {
pub fn merge(&mut self, other: &Manifest) -> Result<()> {
use std::collections::hash_map::Entry::{Vacant, Occupied};
self.output_map = other.output_map.clone();
for (desc_bind, desc_ty) in other.desc_map.iter() {
match self.desc_map.entry(*desc_bind) {
Vacant(entry) => { entry.insert(desc_ty.clone()); },
Occupied(mut entry) => {
if let DescriptorType::PushConstant(Type::Struct(dst_struct_ty)) = entry.get_mut() {
if let DescriptorType::PushConstant(Type::Struct(src_struct_ty)) = desc_ty {
dst_struct_ty.merge(&src_struct_ty)?;
} else {
unreachable!("push constant merging with push constant");
}
} else {
if hash(entry.get()) != hash(&desc_ty) {
return Err(Error::MismatchedManifest);
}
}
}
}
}
for (name, locator) in other.var_name_map.iter() {
match self.var_name_map.entry(name.to_owned()) {
Vacant(entry) => { entry.insert(locator.clone()); },
Occupied(entry) => if entry.get() != locator {
return Err(Error::MismatchedManifest);
},
}
}
for (desc_bind, access) in other.desc_access_map.iter() {
if let Some(acc) = self.desc_access_map.get_mut(&desc_bind) {
use num_traits::FromPrimitive;
let access = *acc as u32 | *access as u32;
*acc = AccessType::from_u32(access).unwrap();
} else {
self.desc_access_map.insert(*desc_bind, *access);
}
}
Ok(())
}
pub fn get_input<'a>(&'a self, location: InterfaceLocation) -> Option<&'a Type> {
self.input_map.get(&location)
}
pub fn get_output<'a>(&'a self, location: InterfaceLocation) -> Option<&'a Type> {
self.output_map.get(&location)
}
pub fn get_desc<'a>(&'a self, desc_bind: DescriptorBinding) -> Option<&'a DescriptorType> {
self.desc_map.get(&desc_bind)
}
pub fn get_input_name<'a>(&'a self, location: InterfaceLocation) -> Option<&'a str> {
self.var_name_map.iter()
.find_map(|x| if let ResourceLocator::Input(loc) = x.1 {
if *loc == location { Some(x.0.as_ref()) } else { None }
} else { None })
}
pub fn get_output_name<'a>(&'a self, location: InterfaceLocation) -> Option<&'a str> {
self.var_name_map.iter()
.find_map(|x| if let ResourceLocator::Output(loc) = x.1 {
if *loc == location { Some(x.0.as_ref()) } else { None }
} else { None })
}
pub fn get_desc_name<'a>(&'a self, desc_bind: DescriptorBinding) -> Option<&'a str> {
self.var_name_map.iter()
.find_map(|x| if let ResourceLocator::Descriptor(db) = x.1 {
if *db == desc_bind { Some(x.0.as_ref()) } else { None }
} else { None })
}
pub fn get_desc_access(&self, desc_bind: DescriptorBinding) -> Option<AccessType> {
self.desc_access_map
.get(&desc_bind)
.map(|x| *x)
}
fn resolve_ivar<'a>(&self, map: &'a HashMap<InterfaceLocation, Type>, sym: &Sym) -> Option<InterfaceVariableResolution<'a>> {
let mut segs = sym.segs();
let location = match segs.next() {
Some(Seg::Index(loc)) => {
if let Some(Seg::Index(comp)) = segs.next() {
InterfaceLocation::new(loc as u32, comp as u32)
} else { return None; }
},
Some(Seg::Name(name)) => {
if let Some(ResourceLocator::Input(location)) = self.var_name_map.get(name) {
*location
} else { return None }
},
_ => return None,
};
if segs.next().is_some() { return None }
let ty = map.get(&location)?;
let ivar_res = InterfaceVariableResolution { location, ty };
Some(ivar_res)
}
pub fn resolve_input<S: AsRef<Sym>>(&self, sym: S) -> Option<InterfaceVariableResolution> {
self.resolve_ivar(&self.output_map, sym.as_ref())
}
pub fn resolve_output<S: AsRef<Sym>>(&self, sym: S) -> Option<InterfaceVariableResolution> {
self.resolve_ivar(&self.input_map, sym.as_ref())
}
pub fn resolve_desc<S: AsRef<Sym>>(&self, sym: S) -> Option<DescriptorResolution> {
let mut segs = sym.as_ref().segs();
let desc_bind = match segs.next() {
Some(Seg::Index(desc_set)) => {
if let Some(Seg::Index(bind_point)) = segs.next() {
DescriptorBinding::desc_bind(desc_set as u32, bind_point as u32)
} else { return None; }
},
Some(Seg::Empty) => {
DescriptorBinding::push_const()
}
Some(Seg::Name(name)) => {
if let Some(ResourceLocator::Descriptor(desc_bind)) = self.var_name_map.get(name) {
*desc_bind
} else { return None; }
},
None => return None,
};
let desc_ty = self.desc_map.get(&desc_bind)?;
let rem_sym = segs.remaining();
let member_var_res = desc_ty.resolve(rem_sym);
let desc_res = DescriptorResolution { desc_bind, desc_ty, member_var_res };
Some(desc_res)
}
pub fn inputs<'a>(&'a self) -> impl Iterator<Item=InterfaceVariableResolution<'a>> {
self.input_map.iter()
.map(|(&location, ty)| {
InterfaceVariableResolution { location, ty }
})
}
pub fn outputs<'a>(&'a self) -> impl Iterator<Item=InterfaceVariableResolution<'a>> {
self.output_map.iter()
.map(|(&location, ty)| {
InterfaceVariableResolution { location, ty }
})
}
pub fn descs<'a>(&'a self) -> impl Iterator<Item=DescriptorResolution<'a>> {
self.desc_map.iter()
.map(|(&desc_bind, desc_ty)| {
DescriptorResolution{ desc_bind, desc_ty, member_var_res: None }
})
}
}
#[derive(Clone)]
pub struct EntryPoint {
pub exec_model: ExecutionModel,
pub name: String,
pub manifest: Manifest,
}
impl Deref for EntryPoint {
type Target = Manifest;
fn deref(&self) -> &Self::Target { &self.manifest }
}
impl fmt::Debug for EntryPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(&self.name)
.field("inputs", &self.manifest.input_map)
.field("outputs", &self.manifest.output_map)
.field("descriptors", &self.manifest.desc_map)
.finish()
}
}