Handle struct members alignments in generated code

This commit is contained in:
Pierre Krieger 2016-02-29 16:03:39 +01:00
parent 85ded76ead
commit e53a7e7bcc
2 changed files with 97 additions and 15 deletions

View File

@ -486,6 +486,7 @@ unsafe impl ::vulkano::descriptor_set::PipelineLayoutDesc for Layout {{
output
}
// TODO: struct definitions don't use this function, so irrelevant elements should be removed
fn type_from_id(doc: &parse::Spirv, searched: u32) -> String {
for instruction in doc.instructions.iter() {
match instruction {

View File

@ -1,3 +1,5 @@
use std::mem;
use parse;
use enums;
@ -24,16 +26,21 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
let mut members_defs = Vec::with_capacity(members.len());
// Contains the offset of the next field.
// Equals to `None` if there's a runtime-sized field in there.
let mut current_rust_offset = Some(0);
for (num, &member) in members.iter().enumerate() {
let ty = ::type_from_id(doc, member);
let (ty, rust_size, rust_align) = type_from_id(doc, member);
let member_name = ::member_name_from_id(doc, struct_id, num as u32);
// Ignore the field if it is built in, which includes `gl_Position` for example.
// 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) {
continue;
return String::new();
}
let offset = doc.instructions.iter().filter_map(|i| {
let spirv_offset = doc.instructions.iter().filter_map(|i| {
match *i {
parse::Instruction::MemberDecorate { target_id, member,
decoration: enums::Decoration::DecorationOffset,
@ -47,20 +54,39 @@ fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
None
}).next().expect(&format!("Struct member `{}` of `{}` is missing an `Offset` decoration",
member_name, name));
member_name, name)) as usize;
members_defs.push(format!("{name}: {ty} /* {off} */", name = member_name, ty = ty, off = offset));
// We need to add a dummy field if necessary.
{
let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");
// Updating current_rust_offset to take the alignment of the next field into account
*current_rust_offset = if *current_rust_offset == 0 {
0
} else {
(1 + (*current_rust_offset - 1) / rust_align) * rust_align
};
if spirv_offset != *current_rust_offset {
let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
members_defs.push(format!("_dummy: [u8; {}]", diff)); // FIXME: fix name if there are multiple dummies
*current_rust_offset += diff;
}
}
// Updating `current_rust_offset`.
if let Some(s) = rust_size {
*current_rust_offset.as_mut().unwrap() += s;
} else {
current_rust_offset = None;
}
members_defs.push(format!("{name}: {ty}", name = member_name, ty = ty));
}
// GLSL doesn't allow empty structures. However it is possible to have structures entirely
// made of built-in members, hence this check.
if !members_defs.is_empty() {
format!("#[repr(C)]\n#[derive(Copy, Clone, Debug, Default)]\n\
pub struct {name} {{\n\t{members}\n}}\n",
name = name, members = members_defs.join(",\n\t"))
} else {
String::new()
}
}
/// Returns true if a `BuiltIn` decorator is applied on a struct member.
@ -81,3 +107,58 @@ fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
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.
fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
for instruction in doc.instructions.iter() {
match instruction {
&parse::Instruction::TypeBool { result_id } if result_id == searched => {
return ("bool".to_owned(), Some(mem::size_of::<bool>()), mem::align_of::<bool>())
},
&parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
// FIXME: width
return ("i32".to_owned(), Some(mem::size_of::<i32>()), mem::align_of::<i32>())
},
&parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
// FIXME: width
return ("f32".to_owned(), Some(mem::size_of::<f32>()), mem::align_of::<f32>())
},
&parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, component_id);
return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
},
&parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
// FIXME: row-major or column-major
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, column_type_id);
return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
},
&parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, t_size, t_align) = type_from_id(doc, type_id);
let len = doc.instructions.iter().filter_map(|e| {
match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
}).next().expect("failed to find array length");
let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align); // FIXME:
},
&parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (t, _, t_align) = type_from_id(doc, type_id);
return (format!("[{}]", t), None, t_align);
},
&parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
let name = ::name_from_id(doc, result_id);
let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
return (name, Some(size), align);
},
_ => ()
}
}
panic!("Type #{} not found", searched)
}