Handle arrays in autogen (#2512)

* unwrap if else

reduce nesting using early returns to make code a bit less branchy

* handle array in autogen

handles the case where a type in vk.xml has a field which is an array.

See pr description for more info.

* use reference in from_vulkan call instead of cloning

let the callee decide to whether or not to clone it if they want

* remove extra constraint

oops

* add copy blanket impl for references

* add inline
This commit is contained in:
Martin Charles 2024-04-06 10:26:58 -05:00 committed by GitHub
parent f84479b491
commit 91015a5eb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 180 additions and 59 deletions

View File

@ -1,5 +1,5 @@
use super::{write_file, IndexMap, VkRegistryData}; use super::{write_file, IndexMap, VkRegistryData};
use ahash::HashMap; use ahash::{HashMap, HashSet};
use heck::ToSnakeCase; use heck::ToSnakeCase;
use nom::{ use nom::{
bytes::complete::{tag, take_until, take_while1}, bytes::complete::{tag, take_until, take_while1},
@ -8,10 +8,10 @@ use nom::{
sequence::{delimited, tuple}, sequence::{delimited, tuple},
IResult, IResult,
}; };
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use std::{collections::hash_map::Entry, fmt::Write as _}; use std::{collections::hash_map::Entry, fmt::Write as _};
use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec}; use vk_parse::{Extension, Type, TypeMember, TypeMemberDefinition, TypeMemberMarkup, TypeSpec};
pub fn write(vk_data: &VkRegistryData<'_>) { pub fn write(vk_data: &VkRegistryData<'_>) {
let properties_output = properties_output(&properties_members(&vk_data.types)); let properties_output = properties_output(&properties_members(&vk_data.types));
@ -37,10 +37,17 @@ struct PropertiesMember {
doc: String, doc: String,
raw: String, raw: String,
ffi_name: Ident, ffi_name: Ident,
ffi_members: Vec<(Ident, TokenStream)>, ffi_members: Vec<FFIMember>,
optional: bool, optional: bool,
} }
#[derive(Debug, Clone)]
struct FFIMember {
ident: Ident,
tokens: TokenStream,
len_field_name: Option<String>,
}
fn properties_output(members: &[PropertiesMember]) -> TokenStream { fn properties_output(members: &[PropertiesMember]) -> TokenStream {
let struct_items = members.iter().map( let struct_items = members.iter().map(
|PropertiesMember { |PropertiesMember {
@ -80,8 +87,23 @@ fn properties_output(members: &[PropertiesMember]) -> TokenStream {
.. ..
}| { }| {
if *optional { if *optional {
let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| { let ffi_members = ffi_members.iter().map(|FFIMember { ident: ffi_member, tokens: ffi_member_field, len_field_name }| {
quote! { properties_ffi.#ffi_member.map(|s| s #ffi_member_field .#ffi_name) } if let Some(len_field_name) = len_field_name {
let len_field_name = Ident::new(len_field_name.as_str(), Span::call_site());
quote! {
properties_ffi.#ffi_member.map(|s|
unsafe {
std::slice::from_raw_parts(
s #ffi_member_field .#ffi_name as _,
s #ffi_member_field .#len_field_name as _,
)
}
)
}
} else {
quote! { properties_ffi.#ffi_member.map(|s| s #ffi_member_field .#ffi_name) }
}
}); });
quote! { quote! {
@ -90,7 +112,8 @@ fn properties_output(members: &[PropertiesMember]) -> TokenStream {
].into_iter().flatten().next().and_then(<#ty>::from_vulkan), ].into_iter().flatten().next().and_then(<#ty>::from_vulkan),
} }
} else { } else {
let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| { let ffi_members = ffi_members.iter().map(|FFIMember { ident: ffi_member, tokens: ffi_member_field, len_field_name }| {
assert_eq!(*len_field_name, None);
quote! { properties_ffi.#ffi_member #ffi_member_field .#ffi_name } quote! { properties_ffi.#ffi_member #ffi_member_field .#ffi_name }
}); });
@ -190,7 +213,7 @@ fn properties_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<Properti
return; return;
} }
let vulkano_member = name.to_snake_case(); let ffi_name = name.to_snake_case();
let vulkano_ty = match name { let vulkano_ty = match name {
"apiVersion" => quote! { Version }, "apiVersion" => quote! { Version },
"bufferImageGranularity" "bufferImageGranularity"
@ -208,35 +231,71 @@ fn properties_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<Properti
} }
_ => vulkano_type(ty, len), _ => vulkano_type(ty, len),
}; };
match properties.entry(vulkano_member.clone()) {
let len_field_name = len.and_then(|it| match it {
LenKind::Field(it) => Some(it.to_snake_case()),
_ => None,
});
let vulkano_member = if len_field_name.is_some() {
ffi_name
.strip_prefix("p_")
.map(|it| it.to_string())
.unwrap()
} else {
ffi_name.clone()
};
let ffi_member = FFIMember {
ident: ty_name.0.clone(),
tokens: ty_name.1.clone(),
len_field_name,
};
match properties.entry(ffi_name.clone()) {
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let mut member = PropertiesMember { let mut member = PropertiesMember {
name: format_ident!("{}", vulkano_member), name: format_ident!("{}", vulkano_member),
ty: vulkano_ty, ty: vulkano_ty,
doc: String::new(), doc: String::new(),
raw: name.to_owned(), raw: name.to_owned(),
ffi_name: format_ident!("{}", vulkano_member), ffi_name: format_ident!("{}", ffi_name),
ffi_members: vec![ty_name.clone()], ffi_members: vec![ffi_member],
optional, optional,
}; };
make_doc(&mut member, vulkan_ty_name); make_doc(&mut member, vulkan_ty_name);
entry.insert(member); entry.insert(member);
} }
Entry::Occupied(entry) => { Entry::Occupied(entry) => {
entry.into_mut().ffi_members.push(ty_name.clone()); entry.into_mut().ffi_members.push(ffi_member);
} }
}; };
}); });
}); });
let mut names: Vec<_> = properties let mut ffi_names: Vec<_> = properties
.values() .values()
.map(|prop| prop.name.to_string()) .map(|prop| prop.ffi_name.to_string())
.collect(); .collect();
names.sort_unstable(); ffi_names.sort_unstable();
names
let to_remove = properties
.values()
.flat_map(|value| {
value
.ffi_members
.iter()
.flat_map(|ffi_member| ffi_member.len_field_name.clone())
})
.collect::<HashSet<String>>();
let ffi_names = ffi_names
.iter()
.filter(|it| !to_remove.contains(it.as_str()));
ffi_names
.into_iter() .into_iter()
.map(|name| properties.remove(&name).unwrap()) .map(|name| properties.remove(name).unwrap())
.collect() .collect()
} }
@ -248,7 +307,7 @@ fn make_doc(prop: &mut PropertiesMember, vulkan_ty_name: &str) {
vulkan_ty_name, vulkan_ty_name,
prop.raw prop.raw
) )
.unwrap(); .unwrap();
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -427,7 +486,14 @@ fn ffi_member(ty_name: &str) -> String {
struct Member<'a> { struct Member<'a> {
name: &'a str, name: &'a str,
ty: &'a str, ty: &'a str,
len: Option<&'a str>, len: Option<LenKind<'a>>,
}
#[derive(Copy, Clone)]
enum LenKind<'a> {
/// Length information in a member with this name
Field(&'a str),
Raw(&'a str),
} }
fn members(ty: &Type) -> Vec<Member<'_>> { fn members(ty: &Type) -> Vec<Member<'_>> {
@ -440,46 +506,81 @@ fn members(ty: &Type) -> Vec<Member<'_>> {
))(input) ))(input)
} }
if let TypeSpec::Members(members) = &ty.spec { fn type_member_name(def: &TypeMemberDefinition) -> Option<&str> {
members def.markup.iter().find_map(|markup| match markup {
.iter() TypeMemberMarkup::Name(name) => Some(name.as_str()),
.filter_map(|member| { _ => None,
if let TypeMember::Definition(def) = member { })
let name = def.markup.iter().find_map(|markup| match markup {
TypeMemberMarkup::Name(name) => Some(name.as_str()),
_ => None,
});
let ty = def.markup.iter().find_map(|markup| match markup {
TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
_ => None,
});
let len = def
.markup
.iter()
.find_map(|markup| match markup {
TypeMemberMarkup::Enum(len) => Some(len.as_str()),
_ => None,
})
.or_else(|| array_len(&def.code).map(|(_, len)| len).ok());
if name != Some("sType") && name != Some("pNext") {
return name.map(|name| Member {
name,
ty: ty.unwrap(),
len,
});
}
}
None
})
.collect()
} else {
vec![]
} }
fn member_by_name<'a>(
members: &'a [TypeMember],
name: &str,
) -> Option<&'a TypeMemberDefinition> {
members.iter().find_map(|it| {
let TypeMember::Definition(defs) = it else {
return None;
};
let member_name = type_member_name(defs)?;
if member_name != name {
return None;
};
Some(defs)
})
}
let TypeSpec::Members(members) = &ty.spec else {
return vec![];
};
members
.iter()
.filter_map(|member| {
let TypeMember::Definition(def) = member else {
return None;
};
let name = type_member_name(def);
let ty = def.markup.iter().find_map(|markup| match markup {
TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
_ => None,
});
let len = def
.markup
.iter()
.find_map(|markup| match markup {
TypeMemberMarkup::Enum(len) => Some(len.as_str()),
_ => None,
})
.or_else(|| array_len(&def.code).map(|(_, len)| len).ok())
.map(LenKind::Raw)
.or_else(|| {
let len = def.len.as_ref()?;
let _member = member_by_name(members.as_slice(), len)?;
Some(LenKind::Field(len.as_str()))
});
if name == Some("sType") || name == Some("pNext") {
return None;
}
name.map(|name| Member {
name,
ty: ty.unwrap(),
len,
})
})
.collect()
} }
fn vulkano_type(ty: &str, len: Option<&str>) -> TokenStream { fn vulkano_type(ty: &str, len: Option<LenKind<'_>>) -> TokenStream {
if let Some(len) = len { match len {
match ty { Some(LenKind::Raw(len)) => match ty {
"char" => quote! { String }, "char" => quote! { String },
"uint8_t" if len == "VK_LUID_SIZE" => quote! { [u8; 8] }, "uint8_t" if len == "VK_LUID_SIZE" => quote! { [u8; 8] },
"uint8_t" if len == "VK_UUID_SIZE" => quote! { [u8; 16] }, "uint8_t" if len == "VK_UUID_SIZE" => quote! { [u8; 16] },
@ -487,9 +588,13 @@ fn vulkano_type(ty: &str, len: Option<&str>) -> TokenStream {
"uint32_t" if len == "3" => quote! { [u32; 3] }, "uint32_t" if len == "3" => quote! { [u32; 3] },
"float" if len == "2" => quote! { [f32; 2] }, "float" if len == "2" => quote! { [f32; 2] },
_ => unimplemented!("{}[{}]", ty, len), _ => unimplemented!("{}[{}]", ty, len),
},
Some(LenKind::Field(_)) => {
let inner = vulkano_type(ty, None);
quote! { Vec<#inner> }
} }
} else { None => match ty {
match ty {
"float" => quote! { f32 }, "float" => quote! { f32 },
"int32_t" => quote! { i32 }, "int32_t" => quote! { i32 },
"int64_t" => quote! { i64 }, "int64_t" => quote! { i64 },
@ -518,6 +623,6 @@ fn vulkano_type(ty: &str, len: Option<&str>) -> TokenStream {
"VkShaderStageFlags" => quote! { ShaderStages }, "VkShaderStageFlags" => quote! { ShaderStages },
"VkSubgroupFeatureFlags" => quote! { SubgroupFeatures }, "VkSubgroupFeatureFlags" => quote! { SubgroupFeatures },
_ => unimplemented!("{}", ty), _ => unimplemented!("{}", ty),
} },
} }
} }

View File

@ -250,3 +250,19 @@ impl FromVulkan<ash::vk::SubgroupFeatureFlags> for SubgroupFeatures {
Some(val.into()) Some(val.into())
} }
} }
impl<U: for<'a> FromVulkan<&'a T>, T> FromVulkan<&[T]> for Vec<U> {
#[inline]
fn from_vulkan(val: &[T]) -> Option<Vec<U>> {
val.iter()
.map(|it| U::from_vulkan(it))
.collect::<Option<Vec<_>>>()
}
}
impl<U: FromVulkan<T>, T: Copy> FromVulkan<&T> for U {
#[inline]
fn from_vulkan(val: &T) -> Option<Self> {
U::from_vulkan(*val)
}
}