diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index e37f123d..108b8f5a 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -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 { diff --git a/vulkano-shaders/src/structs.rs b/vulkano-shaders/src/structs.rs index b577b104..427ee67c 100644 --- a/vulkano-shaders/src/structs.rs +++ b/vulkano-shaders/src/structs.rs @@ -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() - } + 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")) } /// 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) { + 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::()), mem::align_of::()) + }, + &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => { + // FIXME: width + return ("i32".to_owned(), Some(mem::size_of::()), mem::align_of::()) + }, + &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => { + // FIXME: width + return ("f32".to_owned(), Some(mem::size_of::()), mem::align_of::()) + }, + &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => { + debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::()); + 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::()); + 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::()); + 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::()); + 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) +}