Shader types derive (#1460)

* Shader struct types derives and impls

* Vulkano Shaders fixes and cleanup. Example for Types-meta option

* Changelog update

* cargo fmt
This commit is contained in:
Ilya Lakhin 2020-12-20 12:08:55 +07:00 committed by GitHub
parent ba6fe09155
commit af3ea98d5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 551 additions and 44 deletions

View File

@ -22,6 +22,9 @@
- Check usage bits on image when creating image view.
- Fixing an assertion panic in the SyncCommandBuffer. If the buffer encountered an error while locking the necessary resources, it would unlock all previously locked resources. Images were unlocked incorrectly and an assert in the image unlock function would panic.
- Added support for including precompiled shaders in vulkano-shaders using the `bytes` option.
- Added an option for Vulkano-Shaders macro to automatically generate standard
traits(Default, PartialEq, etc) implementations for Rust types generated from
the Shader types, and to derive these structs with external traits.
# Version 0.19.0 (2020-06-01)

View File

@ -20,3 +20,5 @@ vulkano-win = { path = "../vulkano-win" }
cgmath = "0.17"
png = "0.15.0"
time = "0.1.38"
serde = { version="1.0.114", features = ["derive"] }
ron = "0.6.0"

View File

@ -0,0 +1,137 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
// This example demonstrates how to put derives onto generated Rust structs from
// the Shader types through the "types-meta" options of
// `vulkano_shaders::shader!` macro.
// Vulkano Shader macro is capable to generate Rust structs representing each
// type found in the shader source. These structs appear in the `ty` module
// generated in the same module where the macro was called.
//
// By default each type has only `Clone` and `Copy` implementations. For
// ergonomic purposes developer may want to implement more traits on top of each
// type. For example "standard" traits such as `Default` or `Debug`.
//
// One way to do so is implementing them manually, but it would be hard to do,
// and complicates code maintenances.
//
// Another way is to specify a macro option to automatically put derives and
// blanket impls onto each generated type by the Macro itself.
//
// The macro is capable to implement standard trait derives in smart way ignoring
// `_dummyX` fields whenever these fields make no sense. And in addition to that
// developer can also specify derives of traits from external modules/crates
// whenever such traits provide custom derive feature.
use ron::from_str;
use ron::ser::{to_string_pretty, PrettyConfig};
use std::fmt::{Debug, Display, Error, Formatter};
vulkano_shaders::shader! {
ty: "compute",
src: "
#version 450
struct Foo {
float x;
vec3 z;
};
struct Bar {
vec2 y;
Foo foo;
};
layout(push_constant) uniform PushConstantData {
int multiple;
} pc;
layout(set = 0, binding = 1) buffer Bars {
Bar bar[];
};
void main() {}
",
types_meta: {
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
impl Eq
}
}
// In the example above the macro generated `Clone`, `Copy`, `PartialEq`,
// `Debug` and `Default` implementations for each declared
// type(`PushConstantData`, `Foo` and `Bar`) in the shader, and applied
// `impl Eq` for each of them too. And it also applied derives of
// `Serialize` and `Deserialize` traits from Serde crate, but it didn't apply
// these things to `Bars` since the `Bars` type does not have size known in
// compile time.
//
// The macro also didn't generate `Display` implementation since we didn't
// specify it. As such we still can implement this trait manually for some
// selected types.
impl Display for crate::ty::Foo {
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), Error> {
Debug::fmt(self, formatter)
}
}
fn main() {
use crate::ty::*;
// Prints "Foo { x: 0.0, z: [100.0, 200.0, 300.0] }" skipping "_dummyX" fields.
println!(
"{}",
Foo {
z: [100.0, 200.0, 300.0],
..Default::default()
}
);
let mut bar = Bar {
y: [5.1, 6.2],
// Fills all fields with zeroes including "_dummyX" fields, so we don't
// have to maintain them manually anymore.
..Default::default()
};
// The data inside "_dummyX" has no use, but we still can fill it with
// something different from zeroes.
bar._dummy0 = [5; 8];
// Objects are equal since "_dummyX" fields ignoring during comparison
assert_eq!(
Bar {
y: [5.1, 6.2],
..Default::default()
},
bar,
);
assert_ne!(Bar::default(), bar);
bar.foo.x = 125.0;
// Since we put `Serialize` and `Deserialize` traits to derives list we can
// serialize and deserialize Shader data
let serialized = to_string_pretty(&bar, PrettyConfig::default()).unwrap();
println!("Serialized Bar: {}", serialized);
let deserialized = from_str::<Bar>(&serialized).unwrap();
assert_eq!(deserialized.foo.x, 125.0);
}

View File

@ -16,7 +16,7 @@ proc-macro = true
[dependencies]
shaderc = "0.6"
syn = "1.0"
syn = { version = "1.0", features = ["full", "extra-traits"] }
quote = "1.0"
proc-macro2 = "1.0"

View File

@ -21,12 +21,12 @@ use crate::enums::Capability;
use crate::enums::StorageClass;
use crate::parse::Instruction;
use crate::descriptor_sets;
use crate::entry_point;
use crate::parse;
use crate::read_file_to_string;
use crate::spec_consts;
use crate::structs;
use crate::{descriptor_sets, TypesMeta};
fn include_callback(
requested_source_path_raw: &str,
@ -187,7 +187,12 @@ pub fn compile(
Ok(content)
}
pub fn reflect(name: &str, spirv: &[u32], dump: bool) -> Result<TokenStream, Error> {
pub(super) fn reflect(
name: &str,
spirv: &[u32],
types_meta: TypesMeta,
dump: bool,
) -> Result<TokenStream, Error> {
let struct_name = Ident::new(&name, Span::call_site());
let doc = parse::parse_spirv(spirv)?;
@ -248,9 +253,10 @@ pub fn reflect(name: &str, spirv: &[u32], dump: bool) -> Result<TokenStream, Err
}
}
let structs = structs::write_structs(&doc);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc);
let specialization_constants = spec_consts::write_specialization_constants(&doc);
let structs = structs::write_structs(&doc, &types_meta);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc, &types_meta);
let specialization_constants = spec_consts::write_specialization_constants(&doc, &types_meta);
let uses = &types_meta.uses;
let ast = quote! {
#[allow(unused_imports)]
use std::sync::Arc;
@ -324,6 +330,7 @@ pub fn reflect(name: &str, spirv: &[u32], dump: bool) -> Result<TokenStream, Err
#( #entry_points_outside_impl )*
pub mod ty {
#( #uses )*
#structs
}
@ -534,7 +541,7 @@ mod tests {
)
.unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
let res = std::panic::catch_unwind(|| structs::write_structs(&doc));
let res = std::panic::catch_unwind(|| structs::write_structs(&doc, &TypesMeta::default()));
assert!(res.is_err());
}
#[test]
@ -560,7 +567,7 @@ mod tests {
)
.unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
structs::write_structs(&doc, &TypesMeta::default());
}
#[test]
fn test_wrap_alignment() {
@ -590,7 +597,7 @@ mod tests {
)
.unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
structs::write_structs(&doc, &TypesMeta::default());
}
#[test]

View File

@ -13,9 +13,9 @@ use proc_macro2::TokenStream;
use crate::enums::{Decoration, Dim, ImageFormat, StorageClass};
use crate::parse::{Instruction, Spirv};
use crate::spirv_search;
use crate::{spirv_search, TypesMeta};
pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream {
// TODO: not implemented correctly
// Finding all the descriptors.
@ -71,7 +71,7 @@ pub fn write_descriptor_sets(doc: &Spirv) -> TokenStream {
_ => continue,
};
let (_, size, _) = crate::structs::type_from_id(doc, type_id);
let (_, size, _) = crate::structs::type_from_id(doc, type_id, types_meta);
let size = size.expect("Found runtime-sized push constants");
push_constants_size = cmp::max(push_constants_size, size);
}

View File

@ -51,6 +51,9 @@
//! return the various entry point structs that can be found in the
//! [vulkano::pipeline::shader][pipeline::shader] module.
//! * A Rust struct translated from each struct contained in the shader data.
//! By default each structure has a `Clone` and a `Copy` implemenetations. This
//! behavior could be customized through the `types_meta` macro option(see below
//! for details).
//! * The `Layout` newtype. This contains a [`ShaderStages`][ShaderStages] struct.
//! An implementation of [`PipelineLayoutDesc`][PipelineLayoutDesc] is also
//! generated for the newtype.
@ -146,6 +149,29 @@
//! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE`
//! on the command line.
//!
//! ## `types_meta: { use a::b; #[derive(Clone, Default, PartialEq ...)] impl Eq }`
//!
//! Extends implementations of Rust structs that represent Shader structs.
//!
//! By default each generated struct has a `Clone` and a `Copy` implementations
//! only. If the struct has unsized members none of derives or impls applied on
//! this struct.
//!
//! The block may have as many `use`, `derive` or `impl` statements as needed
//! and in any order.
//!
//! Each `use` declaration will be added to generated `ty` module. And each
//! `derive`'s trait and `impl` statement will be applied to each generated
//! struct inside `ty` module.
//!
//! For `Default` derive implementation fills a struct data with all zeroes.
//! For `Display` and `Debug` derive implementation prints all fields except `_dummyX`.
//! For `PartialEq` derive implementation all non-`_dummyX` are checking for equality.
//!
//! The macro performs trivial checking for duplicate declarations. To see the
//! final output of generated code the user can also use `dump` macro
//! option(see below).
//!
//! ## `dump: true`
//!
//! The crate fails to compile but prints the generated rust code to stdout.
@ -176,7 +202,9 @@ use std::io::{Read, Result as IoResult};
use std::path::Path;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, LitBool, LitStr};
use syn::{
Ident, ItemUse, LitBool, LitStr, Meta, MetaList, NestedMeta, Path as SynPath, TypeImplTrait,
};
mod codegen;
mod descriptor_sets;
@ -196,11 +224,58 @@ enum SourceKind {
Bytes(String),
}
struct TypesMeta {
custom_derives: Vec<SynPath>,
clone: bool,
copy: bool,
display: bool,
debug: bool,
default: bool,
partial_eq: bool,
uses: Vec<ItemUse>,
impls: Vec<TypeImplTrait>,
}
impl Default for TypesMeta {
#[inline]
fn default() -> Self {
Self {
custom_derives: vec![],
clone: true,
copy: true,
partial_eq: false,
debug: false,
display: false,
default: false,
uses: Vec::new(),
impls: Vec::new(),
}
}
}
impl TypesMeta {
#[inline]
fn empty() -> Self {
Self {
custom_derives: Vec::new(),
clone: false,
copy: false,
partial_eq: false,
debug: false,
display: false,
default: false,
uses: Vec::new(),
impls: Vec::new(),
}
}
}
struct MacroInput {
shader_kind: ShaderKind,
source_kind: SourceKind,
include_directories: Vec<String>,
macro_defines: Vec<(String, String)>,
types_meta: TypesMeta,
dump: bool,
}
@ -211,6 +286,7 @@ impl Parse for MacroInput {
let mut source_kind = None;
let mut include_directories = Vec::new();
let mut macro_defines = Vec::new();
let mut types_meta = None;
while !input.is_empty() {
let name: Ident = input.parse()?;
@ -290,6 +366,144 @@ impl Parse for MacroInput {
}
}
}
"types_meta" => {
let in_braces;
braced!(in_braces in input);
let mut meta = TypesMeta::empty();
while !in_braces.is_empty() {
if in_braces.peek(Token![#]) {
in_braces.parse::<Token![#]>()?;
let in_brackets;
bracketed!(in_brackets in in_braces);
let derive_list: MetaList = in_brackets.parse()?;
for derive in derive_list.nested {
match derive {
NestedMeta::Meta(Meta::Path(path)) => {
let custom_derive = if let Some(derive_ident) =
path.get_ident()
{
match derive_ident.to_string().as_str() {
"Clone" => {
if meta.default {
return Err(in_brackets
.error("Duplicate Clone derive"));
}
meta.clone = true;
false
}
"Copy" => {
if meta.copy {
return Err(in_brackets
.error("Duplicate Copy derive"));
}
meta.copy = true;
false
}
"PartialEq" => {
if meta.partial_eq {
return Err(in_brackets
.error("Duplicate PartialEq derive"));
}
meta.partial_eq = true;
false
}
"Debug" => {
if meta.debug {
return Err(in_brackets
.error("Duplicate Debug derive"));
}
meta.debug = true;
false
}
"Display" => {
if meta.display {
return Err(in_brackets
.error("Duplicate Display derive"));
}
meta.display = true;
false
}
"Default" => {
if meta.default {
return Err(in_brackets
.error("Duplicate Default derive"));
}
meta.default = true;
false
}
_ => true,
}
} else {
true
};
if custom_derive {
if meta
.custom_derives
.iter()
.any(|candidate| candidate.eq(&path))
{
return Err(
in_braces.error("Duplicate derive declaration")
);
}
meta.custom_derives.push(path);
}
}
_ => return Err(in_brackets.error("Unsupported syntax")),
}
}
continue;
}
if in_braces.peek(Token![impl]) {
let impl_trait: TypeImplTrait = in_braces.parse()?;
if meta.impls.iter().any(|candidate| candidate == &impl_trait) {
return Err(in_braces.error("Duplicate \"impl\" declaration"));
}
meta.impls.push(impl_trait);
continue;
}
if in_braces.peek(Token![use]) {
let item_use: ItemUse = in_braces.parse()?;
if meta.uses.iter().any(|candidate| candidate == &item_use) {
return Err(in_braces.error("Duplicate \"use\" declaration"));
}
meta.uses.push(item_use);
continue;
}
return Err(in_braces.error("Type meta must by \"use a::b::c\", \"#[derive(Type1, Type2, ..)]\" or \"impl Type\""));
}
types_meta = Some(meta);
}
"dump" => {
if dump.is_some() {
panic!("Only one `dump` can be defined")
@ -317,12 +531,13 @@ impl Parse for MacroInput {
let dump = dump.unwrap_or(false);
Ok(MacroInput {
Ok(Self {
shader_kind,
source_kind,
include_directories,
dump,
macro_defines,
types_meta: types_meta.unwrap_or_else(|| TypesMeta::default()),
})
}
}
@ -358,6 +573,7 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
codegen::reflect(
"Shader",
unsafe { from_raw_parts(bytes.as_slice().as_ptr() as *const u32, bytes.len() / 4) },
input.types_meta,
input.dump,
)
.unwrap()
@ -401,7 +617,7 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Err(e) => panic!(e.replace("(s): ", "(s):\n")),
};
codegen::reflect("Shader", content.as_binary(), input.dump)
codegen::reflect("Shader", content.as_binary(), input.types_meta, input.dump)
.unwrap()
.into()
}

View File

@ -14,8 +14,8 @@ use syn::Ident;
use crate::enums::Decoration;
use crate::parse::{Instruction, Spirv};
use crate::spirv_search;
use crate::structs;
use crate::{spirv_search, TypesMeta};
/// Returns true if the document has specialization constants.
pub fn has_specialization_constants(doc: &Spirv) -> bool {
@ -34,7 +34,7 @@ pub fn has_specialization_constants(doc: &Spirv) -> bool {
/// Writes the `SpecializationConstants` struct that contains the specialization constants and
/// implements the `Default` and the `vulkano::pipeline::shader::SpecializationConstants` traits.
pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
pub(super) fn write_specialization_constants(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream {
struct SpecConst {
name: String,
constant_id: u32,
@ -81,7 +81,8 @@ pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
_ => continue,
};
let (rust_ty, rust_size, rust_alignment) = spec_const_type_from_id(doc, type_id);
let (rust_ty, rust_size, rust_alignment) =
spec_const_type_from_id(doc, type_id, types_meta);
let rust_size = rust_size.expect("Found runtime-sized specialization constant");
let constant_id = doc.get_decoration_params(result_id, Decoration::DecorationSpecId);
@ -168,7 +169,11 @@ pub fn write_specialization_constants(doc: &Spirv) -> TokenStream {
}
// Wrapper around `type_from_id` that also handles booleans.
fn spec_const_type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
fn spec_const_type_from_id(
doc: &Spirv,
searched: u32,
types_meta: &TypesMeta,
) -> (TokenStream, Option<usize>, usize) {
for instruction in doc.instructions.iter() {
match instruction {
&Instruction::TypeBool { result_id } if result_id == searched => {
@ -182,5 +187,5 @@ fn spec_const_type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<u
}
}
structs::type_from_id(doc, searched)
structs::type_from_id(doc, searched, types_meta)
}

View File

@ -14,17 +14,18 @@ use syn::Ident;
use crate::enums::Decoration;
use crate::parse::{Instruction, Spirv};
use crate::spirv_search;
use crate::{spirv_search, TypesMeta};
use syn::LitStr;
/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &Spirv) -> TokenStream {
pub(super) fn write_structs(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream {
let mut structs = vec![];
for instruction in &doc.instructions {
match *instruction {
Instruction::TypeStruct {
result_id,
ref member_types,
} => structs.push(write_struct(doc, result_id, member_types).0),
} => structs.push(write_struct(doc, result_id, member_types, types_meta).0),
_ => (),
}
}
@ -35,7 +36,12 @@ pub fn write_structs(doc: &Spirv) -> TokenStream {
}
/// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, Option<usize>) {
fn write_struct(
doc: &Spirv,
struct_id: u32,
members: &[u32],
types_meta: &TypesMeta,
) -> (TokenStream, Option<usize>) {
let name = Ident::new(
&spirv_search::name_from_id(doc, struct_id),
Span::call_site(),
@ -44,6 +50,7 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
// The members of this struct.
struct Member {
pub name: Ident,
pub dummy: bool,
pub ty: TokenStream,
}
let mut rust_members = Vec::with_capacity(members.len());
@ -57,7 +64,7 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
for (num, &member) in members.iter().enumerate() {
// Compute infos about the member.
let (ty, rust_size, rust_align) = type_from_id(doc, member);
let (ty, rust_size, rust_align) = type_from_id(doc, member, types_meta);
let member_name = spirv_search::member_name_from_id(doc, struct_id, num as u32);
// Ignore the whole struct is a member is built in, which includes
@ -100,6 +107,7 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
next_padding_num += 1;
rust_members.push(Member {
name: Ident::new(&format!("_dummy{}", padding_num), Span::call_site()),
dummy: true,
ty: quote! { [u8; #diff] },
});
*current_rust_offset += diff;
@ -115,6 +123,7 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
rust_members.push(Member {
name: Ident::new(&member_name, Span::call_site()),
dummy: false,
ty,
});
}
@ -149,36 +158,154 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
if diff >= 1 {
rust_members.push(Member {
name: Ident::new(&format!("_dummy{}", next_padding_num), Span::call_site()),
dummy: true,
ty: quote! { [u8; #diff as usize] },
});
}
}
// We can only implement Clone if there's no unsized member in the struct.
let (clone_impl, copy_derive) = if current_rust_offset.is_some() {
let mut copies = vec![];
let (clone_impl, copy_derive) =
if current_rust_offset.is_some() && (types_meta.clone || types_meta.copy) {
(
if types_meta.clone {
let mut copies = vec![];
for member in &rust_members {
let name = &member.name;
copies.push(quote! { #name: self.#name, });
}
// Clone is implemented manually because members can be large arrays
// that do not implement Clone, but do implement Copy
quote! {
impl Clone for #name {
fn clone(&self) -> Self {
#name {
#( #copies )*
}
}
}
}
} else {
quote! {}
},
if types_meta.copy {
quote! { #[derive(Copy)] }
} else {
quote! {}
},
)
} else {
(quote! {}, quote! {})
};
let partial_eq_impl = if current_rust_offset.is_some() && types_meta.partial_eq {
let mut fields = vec![];
for member in &rust_members {
let name = &member.name;
copies.push(quote! { #name: self.#name, });
if !member.dummy {
let name = &member.name;
fields.push(quote! {
if self.#name != other.#name {
return false
}
});
}
}
quote! {
impl PartialEq for #name {
fn eq(&self, other: &Self) -> bool {
#( #fields )*
true
}
}
}
} else {
quote! {}
};
let (debug_impl, display_impl) = if current_rust_offset.is_some()
&& (types_meta.debug || types_meta.display)
{
let mut fields = vec![];
for member in &rust_members {
if !member.dummy {
let name = &member.name;
let name_string = LitStr::new(name.to_string().as_ref(), name.span());
fields.push(quote! {.field(#name_string, &self.#name)});
}
}
let name_string = LitStr::new(name.to_string().as_ref(), name.span());
(
// Clone is implemented manually because members can be large arrays
// that do not implement Clone, but do implement Copy
quote! {
impl Clone for #name {
fn clone(&self) -> Self {
#name {
#( #copies )*
if types_meta.debug {
quote! {
impl std::fmt::Debug for #name {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
formatter
.debug_struct(#name_string)
#( #fields )*
.finish()
}
}
}
} else {
quote! {}
},
if types_meta.display {
quote! {
impl std::fmt::Display for #name {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
formatter
.debug_struct(#name_string)
#( #fields )*
.finish()
}
}
}
} else {
quote! {}
},
quote! { #[derive(Copy)] },
)
} else {
(quote! {}, quote! {})
};
let default_impl = if current_rust_offset.is_some() && types_meta.default {
quote! {
impl Default for #name {
fn default() -> Self {
unsafe {
std::mem::MaybeUninit::<Self>::zeroed().assume_init()
}
}
}
}
} else {
quote! {}
};
// If the struct has unsized members none of custom impls applied.
let custom_impls = if current_rust_offset.is_some() {
let impls = &types_meta.impls;
quote! {
#( #impls for #name {} )*
}
} else {
quote! {}
};
// If the struct has unsized members none of custom derives applied.
let custom_derives = if current_rust_offset.is_some() && !types_meta.custom_derives.is_empty() {
let derive_list = &types_meta.custom_derives;
quote! { #[derive(#( #derive_list ),*)] }
} else {
quote! {}
};
let mut members = vec![];
for member in &rust_members {
let name = &member.name;
@ -189,11 +316,17 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
let ast = quote! {
#[repr(C)]
#copy_derive
#custom_derives
#[allow(non_snake_case)]
pub struct #name {
#( #members )*
}
#clone_impl
#partial_eq_impl
#debug_impl
#display_impl
#default_impl
#custom_impls
};
(
@ -207,7 +340,11 @@ fn write_struct(doc: &Spirv, struct_id: u32, members: &[u32]) -> (TokenStream, O
/// 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.
pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>, usize) {
pub(super) fn type_from_id(
doc: &Spirv,
searched: u32,
types_meta: &TypesMeta,
) -> (TokenStream, Option<usize>, usize) {
for instruction in doc.instructions.iter() {
match instruction {
&Instruction::TypeBool { result_id } if result_id == searched => {
@ -349,7 +486,7 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
count,
} if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, t_size, t_align) = type_from_id(doc, component_id);
let (ty, t_size, t_align) = type_from_id(doc, component_id, types_meta);
let array_length = count as usize;
let size = t_size.map(|s| s * count as usize);
return (quote! { [#ty; #array_length] }, size, t_align);
@ -361,7 +498,7 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
} if result_id == searched => {
// FIXME: row-major or column-major
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, t_size, t_align) = type_from_id(doc, column_type_id);
let (ty, t_size, t_align) = type_from_id(doc, column_type_id, types_meta);
let array_length = column_count as usize;
let size = t_size.map(|s| s * column_count as usize);
return (quote! { [#ty; #array_length] }, size, t_align);
@ -372,7 +509,7 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
length_id,
} if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, t_size, t_align) = type_from_id(doc, type_id);
let (ty, t_size, t_align) = type_from_id(doc, type_id, types_meta);
let t_size = t_size.expect("array components must be sized");
let len = doc
.instructions
@ -403,7 +540,7 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
}
&Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
let (ty, _, t_align) = type_from_id(doc, type_id);
let (ty, _, t_align) = type_from_id(doc, type_id, types_meta);
return (quote! { [#ty] }, None, t_align);
}
&Instruction::TypeStruct {
@ -416,10 +553,10 @@ pub fn type_from_id(doc: &Spirv, searched: u32) -> (TokenStream, Option<usize>,
Span::call_site(),
);
let ty = quote! { #name };
let (_, size) = write_struct(doc, result_id, member_types);
let (_, size) = write_struct(doc, result_id, member_types, types_meta);
let align = member_types
.iter()
.map(|&t| type_from_id(doc, t).2)
.map(|&t| type_from_id(doc, t, types_meta).2)
.max()
.unwrap_or(1);
return (ty, size, align);