handle spirv group decorations (#1126)

This commit is contained in:
Lucas Kent 2018-12-04 19:10:47 +11:00 committed by GitHub
parent 51cc13a0f4
commit 84d6f1b33a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 208 additions and 142 deletions

View File

@ -29,12 +29,9 @@ pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
}
// Looping to find all the elements that have the `DescriptorSet` decoration.
for instruction in doc.instructions.iter() {
let (variable_id, set) = match instruction {
&Instruction::Decorate { target_id, decoration: Decoration::DecorationDescriptorSet, ref params }
=> (target_id, params[0]),
_ => continue,
};
for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) {
let variable_id = set_decoration.target_id;
let set = set_decoration.params[0];
// Find which type is pointed to by this variable.
let pointed_ty = pointer_variable_ty(doc, variable_id);
@ -42,20 +39,8 @@ pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
let name = spirv_search::name_from_id(doc, variable_id);
// Find the binding point of this descriptor.
let binding = doc.instructions
.iter()
.filter_map(|i| {
match i {
&Instruction::Decorate {
target_id,
decoration: Decoration::DecorationBinding,
ref params,
} if target_id == variable_id => Some(params[0]),
_ => None, // TODO: other types
}
})
.next()
.expect(&format!("Uniform `{}` is missing a binding", name));
// TODO: There was a previous todo here, I think it was asking for this to be implemented for member decorations? check git history
let binding = doc.get_decoration_params(variable_id, Decoration::DecorationBinding).unwrap()[0];
// Find information about the kind of binding for this descriptor.
let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
@ -200,18 +185,10 @@ fn descriptor_infos(doc: &Spirv, pointed_ty: u32, force_combined_image_sampled:
match i {
&Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
// Determine whether there's a Block or BufferBlock decoration.
let is_ssbo = doc.instructions.iter().filter_map(|i| {
match i {
&Instruction::Decorate
{ target_id, decoration: Decoration::DecorationBufferBlock, .. }
if target_id == pointed_ty => Some(true),
&Instruction::Decorate
{ target_id, decoration: Decoration::DecorationBlock, .. }
if target_id == pointed_ty => Some(false),
_ => None,
}
}).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
decorations");
let decoration_buffer_block = doc.get_decoration_params(pointed_ty, Decoration::DecorationBufferBlock).is_some();
let decoration_block = doc.get_decoration_params(pointed_ty, Decoration::DecorationBlock).is_some();
assert!(decoration_buffer_block ^ decoration_block, "Found a buffer uniform with neither the Block nor BufferBlock decorations, or both.");
let is_ssbo = decoration_buffer_block && !decoration_block;
// Determine whether there's a NonWritable decoration.
//let non_writable = false; // TODO: tricky because the decoration is on struct members

View File

@ -10,7 +10,7 @@
use syn::Ident;
use proc_macro2::{Span, TokenStream};
use enums::{StorageClass, ExecutionModel, ExecutionMode};
use enums::{StorageClass, ExecutionModel, ExecutionMode, Decoration};
use parse::{Instruction, Spirv};
use spirv_search;
@ -221,11 +221,9 @@ fn write_interface_structs(doc: &Spirv, capitalized_ep_name: &str, interface: &[
continue;
} // FIXME: hack
let location = match spirv_search::location_decoration(doc, result_id) {
Some(l) => l,
None => panic!("Attribute `{}` (id {}) is missing a location",
name,
result_id),
let location = match doc.get_decoration_params(result_id, Decoration::DecorationLocation) {
Some(l) => l[0],
None => panic!("Attribute `{}` (id {}) is missing a location", name, result_id),
};
let (format, location_len) = spirv_search::format_from_id(doc, result_type_id, ignore_first_array);

View File

@ -15,7 +15,7 @@ use parse::ParseError;
macro_rules! enumeration {
($(typedef enum $unused:ident { $($elem:ident = $value:expr,)+ } $name:ident;)+) => (
$(
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum $name {
$($elem),+
}

View File

@ -161,6 +161,17 @@ pub enum Instruction {
decoration: Decoration,
params: Vec<u32>,
},
DecorationGroup {
result_id: u32,
},
GroupDecorate {
decoration_group: u32,
targets: Vec<u32>,
},
GroupMemberDecorate {
decoration_group: u32,
targets: Vec<(u32, u32)>,
},
Label { result_id: u32 },
Branch { result_id: u32 },
Kill,
@ -331,6 +342,17 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
decoration: Decoration::from_num(operands[2])?,
params: operands[3 ..].to_owned(),
},
73 => Instruction::DecorationGroup {
result_id: operands[0],
},
74 => Instruction::GroupDecorate {
decoration_group: operands[0],
targets: operands[1 ..].to_owned(),
},
75 => Instruction::GroupMemberDecorate {
decoration_group: operands[0],
targets: operands.chunks(2).map(|x| (x[0], x[1])).collect(),
},
248 => Instruction::Label { result_id: operands[0] },
249 => Instruction::Branch { result_id: operands[0] },
252 => Instruction::Kill,
@ -357,6 +379,151 @@ fn parse_string(data: &[u32]) -> (String, &[u32]) {
(s, &data[r ..])
}
pub(crate) struct FoundDecoration {
pub target_id: u32,
pub params: Vec<u32>
}
impl Spirv {
/// Returns the params and the id of all decorations that match the passed Decoration type
///
/// for each matching OpDecorate:
/// if it points at a regular target:
/// creates a FoundDecoration with its params and target_id
/// if it points at a group:
/// the OpDecorate's target_id is ignored and a seperate FoundDecoration is created only for each target_id given in matching OpGroupDecorate instructions.
pub(crate) fn get_decorations(&self, find_decoration: Decoration) -> Vec<FoundDecoration> {
let mut decorations = vec!();
for instruction in &self.instructions {
if let Instruction::Decorate { target_id, ref decoration, ref params } = instruction {
if *decoration == find_decoration {
// assume by default it is just pointing at the target_id
let mut target_ids = vec!(*target_id);
// however it might be pointing at a group, which can have multiple target_ids
for inner_instruction in &self.instructions {
if let Instruction::DecorationGroup { result_id } = inner_instruction {
if *result_id == *target_id {
target_ids.clear();
for inner_instruction in &self.instructions {
if let Instruction::GroupDecorate { decoration_group, targets } = inner_instruction {
if *decoration_group == *target_id {
target_ids.extend(targets);
}
}
}
// result_id must be unique so we can safely break here
break
}
}
}
// create for all target_ids found
for target_id in target_ids {
decorations.push(FoundDecoration {
target_id,
params: params.clone()
});
}
}
}
}
decorations
}
/// Returns the params held by the decoration for the specified id and type
/// Searches OpDecorate and OpGroupMemberDecorate
/// Returns None if such a decoration does not exist
pub(crate) fn get_decoration_params(&self, id: u32, find_decoration: Decoration) -> Option<Vec<u32>> {
for instruction in &self.instructions {
match instruction {
Instruction::Decorate { target_id, ref decoration, ref params }
if *target_id == id && *decoration == find_decoration => {
return Some(params.clone());
}
Instruction::GroupDecorate { decoration_group, ref targets } => {
for group_target_id in targets {
if *group_target_id == id {
for instruction in &self.instructions {
if let Instruction::Decorate { target_id, ref decoration, ref params } = instruction {
if target_id == decoration_group && *decoration == find_decoration {
return Some(params.clone());
}
}
}
}
}
}
_ => (),
};
}
None
}
/// Returns the params held by the decoration for the member specified by id, member and type
/// Searches OpMemberDecorate and OpGroupMemberDecorate
/// Returns None if such a decoration does not exist
pub(crate) fn get_member_decoration_params(&self, struct_id: u32, member_literal: u32, find_decoration: Decoration) -> Option<Vec<u32>> {
for instruction in &self.instructions {
match instruction {
Instruction::MemberDecorate { target_id, member, ref decoration, ref params }
if *target_id == struct_id && *member == member_literal && *decoration == find_decoration => {
return Some(params.clone());
}
Instruction::GroupMemberDecorate { decoration_group, ref targets } => {
for (group_target_struct_id, group_target_member_literal) in targets {
if *group_target_struct_id == struct_id && *group_target_member_literal == member_literal {
for instruction in &self.instructions {
if let Instruction::Decorate { target_id, ref decoration, ref params } = instruction {
if target_id == decoration_group && *decoration == find_decoration {
return Some(params.clone());
}
}
}
}
}
}
_ => (),
};
}
None
}
/// Returns the params held by the Decoration::DecorationBuiltIn for the specified struct id
/// Searches OpMemberDecorate and OpGroupMemberDecorate
/// Returns None if such a decoration does not exist
///
/// This function does not need a member_literal argument because the spirv spec requires that a
/// struct must contain either all builtin or all non-builtin members.
pub(crate) fn get_member_decoration_builtin_params(&self, struct_id: u32) -> Option<Vec<u32>> {
for instruction in &self.instructions {
match instruction {
Instruction::MemberDecorate { target_id, decoration: Decoration::DecorationBuiltIn, ref params, .. }
if *target_id == struct_id => {
return Some(params.clone());
}
Instruction::GroupMemberDecorate { decoration_group, ref targets } => {
for (group_target_struct_id, _) in targets {
if *group_target_struct_id == struct_id {
for instruction in &self.instructions {
if let Instruction::Decorate { target_id, decoration: Decoration::DecorationBuiltIn, ref params } = instruction {
if target_id == decoration_group {
return Some(params.clone());
}
}
}
}
}
}
_ => (),
};
}
None
}
}
#[cfg(test)]
mod test {
use parse;

View File

@ -72,15 +72,8 @@ pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
let (rust_ty, rust_size, rust_alignment) = spec_const_type_from_id(doc, type_id);
let rust_size = rust_size.expect("Found runtime-sized specialization constant");
let constant_id = doc.instructions
.iter()
.filter_map(|i| match i {
&Instruction::Decorate { target_id, decoration: Decoration::DecorationSpecId, ref params }
if target_id == result_id => Some(params[0]),
_ => None,
})
.next()
.expect("Found a specialization constant with no SpecId decoration");
let constant_id = doc.get_decoration_params(result_id, Decoration::DecorationSpecId)
.unwrap()[0];
spec_consts.push(SpecConst {
name: spirv_search::name_from_id(doc, result_id),

View File

@ -130,28 +130,13 @@ pub fn member_name_from_id(doc: &Spirv, searched: u32, searched_member: u32) ->
String::from("__unnamed")
}
pub fn location_decoration(doc: &Spirv, searched: u32) -> Option<u32> {
for instruction in &doc.instructions {
if let &Instruction::Decorate { target_id, decoration: Decoration::DecorationLocation, ref params } = instruction {
if target_id == searched {
return Some(params[0])
}
}
}
None
}
/// Returns true if a `BuiltIn` decorator is applied on an id.
pub fn is_builtin(doc: &Spirv, id: u32) -> bool {
for instruction in &doc.instructions {
match *instruction {
Instruction::Decorate { target_id, decoration: Decoration::DecorationBuiltIn, .. }
if target_id == id => { return true }
Instruction::MemberDecorate { target_id, decoration: Decoration::DecorationBuiltIn, .. }
if target_id == id => { return true }
_ => (),
}
if doc.get_decoration_params(id, Decoration::DecorationBuiltIn).is_some() {
return true
}
if doc.get_member_decoration_builtin_params(id).is_some() {
return true
}
for instruction in &doc.instructions {

View File

@ -57,29 +57,13 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
// Ignore the whole struct is a member is built in, which includes
// `gl_Position` for example.
if is_builtin_member(doc, struct_id, num as u32) {
if doc.get_member_decoration_params(struct_id, num as u32, Decoration::DecorationBuiltIn).is_some() {
return (quote!{}, None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
}
// Finding offset of the current member, as requested by the SPIR-V code.
let spirv_offset = doc.instructions
.iter()
.filter_map(|i| {
match *i {
Instruction::MemberDecorate {
target_id,
member,
decoration: Decoration::DecorationOffset,
ref params,
} if target_id == struct_id && member as usize == num => {
return Some(params[0]);
},
_ => (),
};
None
})
.next();
let spirv_offset = doc.get_member_decoration_params(struct_id, num as u32, Decoration::DecorationOffset)
.map(|x| x[0]);
// Some structs don't have `Offset` decorations, in the case they are used as local
// variables only. Ignoring these.
@ -128,39 +112,22 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
}
// Try determine the total size of the struct in order to add padding at the end of the struct.
let spirv_req_total_size = doc.instructions
.iter()
.filter_map(|i| match *i {
Instruction::Decorate {
target_id,
decoration: Decoration::DecorationArrayStride,
ref params,
} => {
for inst in doc.instructions.iter() {
match *inst {
Instruction::TypeArray {
result_id, type_id, ..
} if result_id == target_id && type_id == struct_id => {
return Some(params[0]);
},
Instruction::TypeRuntimeArray { result_id, type_id }
if result_id == target_id && type_id == struct_id => {
return Some(params[0]);
},
_ => (),
}
}
None
},
_ => None,
})
.fold(None, |a, b| if let Some(a) = a {
assert_eq!(a, b);
Some(a)
} else {
Some(b)
});
let mut spirv_req_total_size = None;
for inst in doc.instructions.iter() {
match *inst {
Instruction::TypeArray { result_id, type_id, .. } if type_id == struct_id => {
if let Some(params) = doc.get_decoration_params(result_id, Decoration::DecorationArrayStride) {
spirv_req_total_size = Some(params[0]);
}
}
Instruction::TypeRuntimeArray { result_id, type_id } if type_id == struct_id => {
if let Some(params) = doc.get_decoration_params(result_id, Decoration::DecorationArrayStride) {
spirv_req_total_size = Some(params[0]);
}
}
_ => ()
}
}
// Adding the final padding members.
if let (Some(cur_size), Some(req_size)) = (current_rust_offset, spirv_req_total_size) {
@ -218,19 +185,6 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
(ast, spirv_req_total_size.map(|sz| sz as usize).or(current_rust_offset))
}
/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &Spirv, id: u32, member_id: u32) -> bool {
for instruction in &doc.instructions {
match *instruction {
Instruction::MemberDecorate { target_id, member, decoration: Decoration::DecorationBuiltIn, .. }
if target_id == id && member == member_id => { return true }
_ => (),
}
}
false
}
/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
@ -381,15 +335,7 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
.next()
.expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
let stride = doc.instructions.iter().filter_map(|e| match e {
Instruction::Decorate {
target_id,
decoration: Decoration::DecorationArrayStride,
ref params,
} if *target_id == searched => Some(params[0]),
_ => None,
})
.next().expect("failed to find ArrayStride decoration");
let stride = doc.get_decoration_params(searched, Decoration::DecorationArrayStride).unwrap()[0];
if stride as usize > t_size {
panic!("Not possible to generate a rust array with the correct alignment since the SPIR-V \
ArrayStride is larger than the size of the array element in rust. Try wrapping \