diff --git a/ash/src/vk/extensions.rs b/ash/src/vk/extensions.rs index cf4936d99..2e1a283a2 100644 --- a/ash/src/vk/extensions.rs +++ b/ash/src/vk/extensions.rs @@ -13331,7 +13331,7 @@ pub type PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR = unsafe extern "system" pub type PFN_vkCmdSetFragmentShadingRateKHR = unsafe extern "system" fn( command_buffer: CommandBuffer, p_fragment_size: *const Extent2D, - combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2], + combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2usize], ); #[derive(Clone)] pub struct KhrFragmentShadingRateFn { @@ -13372,7 +13372,7 @@ impl KhrFragmentShadingRateFn { unsafe extern "system" fn cmd_set_fragment_shading_rate_khr( _command_buffer: CommandBuffer, _p_fragment_size: *const Extent2D, - _combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2], + _combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2usize], ) { panic!(concat!( "Unable to load ", @@ -18058,7 +18058,7 @@ impl NvFragmentShadingRateEnumsFn { pub type PFN_vkCmdSetFragmentShadingRateEnumNV = unsafe extern "system" fn( command_buffer: CommandBuffer, shading_rate: FragmentShadingRateNV, - combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2], + combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2usize], ); #[derive(Clone)] pub struct NvFragmentShadingRateEnumsFn { @@ -18076,7 +18076,7 @@ impl NvFragmentShadingRateEnumsFn { unsafe extern "system" fn cmd_set_fragment_shading_rate_enum_nv( _command_buffer: CommandBuffer, _shading_rate: FragmentShadingRateNV, - _combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2], + _combiner_ops: *const [FragmentShadingRateCombinerOpKHR; 2usize], ) { panic!(concat!( "Unable to load ", diff --git a/ash/src/vk/features.rs b/ash/src/vk/features.rs index 4a52e377e..f7ddc8e6b 100644 --- a/ash/src/vk/features.rs +++ b/ash/src/vk/features.rs @@ -999,7 +999,7 @@ pub type PFN_vkCmdSetDepthBias = unsafe extern "system" fn( ); #[allow(non_camel_case_types)] pub type PFN_vkCmdSetBlendConstants = - unsafe extern "system" fn(command_buffer: CommandBuffer, blend_constants: *const [f32; 4]); + unsafe extern "system" fn(command_buffer: CommandBuffer, blend_constants: *const [f32; 4usize]); #[allow(non_camel_case_types)] pub type PFN_vkCmdSetDepthBounds = unsafe extern "system" fn( command_buffer: CommandBuffer, @@ -2876,7 +2876,7 @@ impl DeviceFnV1_0 { cmd_set_blend_constants: unsafe { unsafe extern "system" fn cmd_set_blend_constants( _command_buffer: CommandBuffer, - _blend_constants: *const [f32; 4], + _blend_constants: *const [f32; 4usize], ) { panic!(concat!( "Unable to load ", diff --git a/generator/Cargo.toml b/generator/Cargo.toml index a3288d2b1..13926e2f2 100644 --- a/generator/Cargo.toml +++ b/generator/Cargo.toml @@ -13,7 +13,7 @@ once_cell = "1.7" proc-macro2 = "1.0" quote = "1.0" regex = "1.4" -vk-parse = { version = "0.7", features = ["vkxml-convert"] } +vk-parse = { version = "0.9", features = ["vkxml-convert"] } vkxml = "0.3" [dependencies.syn] diff --git a/generator/src/lib.rs b/generator/src/lib.rs index 9d0f1dfd4..625f87644 100644 --- a/generator/src/lib.rs +++ b/generator/src/lib.rs @@ -6,11 +6,12 @@ use itertools::Itertools; use nom::{ branch::alt, bytes::complete::{tag, take_until, take_while1}, - character::complete::{char, digit1, hex_digit1, multispace1, newline, none_of, one_of}, - combinator::{map, opt, value}, - error::{ParseError, VerboseError}, + character::complete::{ + char, digit1, hex_digit1, multispace0, multispace1, newline, none_of, one_of, + }, + combinator::{map, map_res, opt, value}, multi::{many1, separated_list1}, - sequence::{delimited, pair, preceded, terminated}, + sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}, IResult, Parser, }; use once_cell::sync::Lazy; @@ -70,11 +71,11 @@ impl quote::ToTokens for CType { } } -fn parse_ctype<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, CType, E> { +fn parse_ctype(i: &str) -> IResult<&str, CType> { (alt((value(CType::U64, tag("ULL")), value(CType::U32, tag("U")))))(i) } -fn parse_cexpr<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (CType, String), E> { +fn parse_cexpr(i: &str) -> IResult<&str, (CType, String)> { (alt(( map(parse_cfloat, |f| (CType::Float, format!("{f:.2}"))), parse_inverse_number, @@ -83,13 +84,11 @@ fn parse_cexpr<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (CTyp )))(i) } -fn parse_cfloat<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, f32, E> { +fn parse_cfloat(i: &str) -> IResult<&str, f32> { (terminated(nom::number::complete::float, one_of("fF")))(i) } -fn parse_inverse_number<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, (CType, String), E> { +fn parse_inverse_number(i: &str) -> IResult<&str, (CType, String)> { (map( delimited( char('('), @@ -112,7 +111,7 @@ fn parse_inverse_number<'a, E: ParseError<&'a str>>( // Like a C string, but does not support quote escaping and expects at least one character. // If needed, use https://github.com/Geal/nom/blob/8e09f0c3029d32421b5b69fb798cef6855d0c8df/tests/json.rs#L61-L81 -fn parse_c_include_string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, String, E> { +fn parse_c_include_string(i: &str) -> IResult<&str, String> { (delimited( char('"'), map(many1(none_of("\"")), |c| { @@ -122,25 +121,21 @@ fn parse_c_include_string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a ))(i) } -fn parse_c_include<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, String, E> { +fn parse_c_include(i: &str) -> IResult<&str, String> { (preceded( tag("#include"), preceded(multispace1, parse_c_include_string), ))(i) } -fn parse_decimal_number<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, (CType, String), E> { +fn parse_decimal_number(i: &str) -> IResult<&str, (CType, String)> { (map( pair(digit1.map(str::to_string), parse_ctype), |(dig, ctype)| (ctype, dig), ))(i) } -fn parse_hexadecimal_number<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, (CType, String), E> { +fn parse_hexadecimal_number(i: &str) -> IResult<&str, (CType, String)> { (preceded( alt((tag("0x"), tag("0X"))), map(pair(hex_digit1, parse_ctype), |(num, typ)| { @@ -152,19 +147,15 @@ fn parse_hexadecimal_number<'a, E: ParseError<&'a str>>( ))(i) } -fn parse_c_identifier<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { +fn parse_c_identifier(i: &str) -> IResult<&str, &str> { take_while1(|c: char| c == '_' || c.is_alphanumeric())(i) } -fn parse_comment_suffix<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, Option<&'a str>, E> { +fn parse_comment_suffix(i: &str) -> IResult<&str, Option<&str>> { opt(delimited(tag("//"), take_until("\n"), newline))(i) } -fn parse_parameter_names<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, Vec<&'a str>, E> { +fn parse_parameter_names(i: &str) -> IResult<&str, Vec<&str>> { delimited( char('('), separated_list1(tag(", "), parse_c_identifier), @@ -175,9 +166,7 @@ fn parse_parameter_names<'a, E: ParseError<&'a str>>( /// Parses a C macro define optionally prefixed by a comment and optionally /// containing parameter names. The expression is left in the remainder #[allow(clippy::type_complexity)] -fn parse_c_define_header<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, (Option<&'a str>, (&'a str, Option>)), E> { +fn parse_c_define_header(i: &str) -> IResult<&str, (Option<&str>, (&str, Option>))> { (pair( parse_comment_suffix, preceded( @@ -187,6 +176,89 @@ fn parse_c_define_header<'a, E: ParseError<&'a str>>( ))(i) } +#[derive(Debug)] +enum CReferenceType { + Value, + PointerToConst, + Pointer, + PointerToPointer, + PointerToPointerToConst, + PointerToConstPointer, + PointerToConstPointerToConst, +} + +#[derive(Debug)] +struct CParameterType<'a> { + name: &'a str, + reference_type: CReferenceType, +} + +fn parse_c_type(i: &str) -> IResult<&str, CParameterType> { + (map( + separated_pair( + tuple(( + opt(tag("const ")), + preceded(opt(tag("struct ")), parse_c_identifier), + opt(char('*')), + )), + multispace0, + opt(pair(opt(tag("const")), char('*'))), + ), + |((const_, name, firstptr), secondptr)| CParameterType { + name, + reference_type: match (firstptr, secondptr) { + (None, None) => CReferenceType::Value, + + (Some(_), None) if const_.is_some() => CReferenceType::PointerToConst, + (Some(_), None) => CReferenceType::Pointer, + + (Some(_), Some((Some(_), _))) if const_.is_some() => { + CReferenceType::PointerToConstPointerToConst + } + (Some(_), Some((Some(_), _))) => CReferenceType::PointerToConstPointer, + + (Some(_), Some((None, _))) if const_.is_some() => { + CReferenceType::PointerToPointerToConst + } + (Some(_), Some((None, _))) => CReferenceType::PointerToPointer, + (None, Some(_)) => unreachable!(), + }, + }, + ))(i) +} + +#[derive(Debug)] +struct CParameter<'a> { + type_: CParameterType<'a>, + // Code only used to dissect the type surrounding this field name, + // not interested in the name itself. + _name: &'a str, + static_array: Option, +} + +/// Parses a single C parameter instance, for example: +/// +/// ```c +/// VkSparseImageMemoryRequirements2* pSparseMemoryRequirements +/// ``` +fn parse_c_parameter(i: &str) -> IResult<&str, CParameter> { + (map( + separated_pair( + parse_c_type, + multispace0, + pair( + parse_c_identifier, + opt(delimited(char('['), map_res(digit1, str::parse), char(']'))), + ), + ), + |(type_, (name, static_array))| CParameter { + type_, + _name: name, + static_array, + }, + ))(i) +} + fn khronos_link(name: &S) -> Literal { Literal::string(&format!( "" @@ -317,8 +389,8 @@ impl quote::ToTokens for Constant { } Constant::Text(ref text) => text.to_tokens(tokens), Constant::CExpr(ref expr) => { - let (_, (_, rexpr)) = - parse_cexpr::>(expr).expect("Unable to parse cexpr"); + let (rem, (_, rexpr)) = parse_cexpr(expr).expect("Unable to parse cexpr"); + assert!(rem.is_empty()); tokens.extend(rexpr.parse::()); } Constant::BitPos(pos) => { @@ -371,8 +443,8 @@ impl Constant { match self { Constant::Number(_) | Constant::Hex(_) => CType::USize, Constant::CExpr(expr) => { - let (_, (ty, _)) = - parse_cexpr::>(expr).expect("Unable to parse cexpr"); + let (rem, (ty, _)) = parse_cexpr(expr).expect("Unable to parse cexpr"); + assert!(rem.is_empty()); ty } _ => unimplemented!(), @@ -476,31 +548,18 @@ pub enum FunctionType { Device, } pub trait CommandExt { - /// Returns the ident in snake_case and without the 'vk' prefix. fn function_type(&self) -> FunctionType; - /// - /// Returns true if the command is a device level command. This is indicated by - /// the type of the first parameter. - fn command_ident(&self) -> Ident; } -impl CommandExt for vkxml::Command { - fn command_ident(&self) -> Ident { - format_ident!("{}", self.name.strip_prefix("vk").unwrap().to_snake_case()) - } - +impl CommandExt for vk_parse::CommandDefinition { fn function_type(&self) -> FunctionType { - let is_first_param_device = self - .param - .get(0) - .map(|field| { - matches!( - field.basetype.as_str(), - "VkDevice" | "VkCommandBuffer" | "VkQueue" - ) - }) - .unwrap_or(false); - match self.name.as_str() { + let is_first_param_device = self.params.get(0).map_or(false, |field| { + matches!( + field.definition.type_name.as_deref(), + Some("VkDevice" | "VkCommandBuffer" | "VkQueue") + ) + }); + match self.proto.name.as_str() { "vkGetInstanceProcAddr" => FunctionType::Static, "vkCreateInstance" | "vkEnumerateInstanceLayerProperties" @@ -508,13 +567,8 @@ impl CommandExt for vkxml::Command { | "vkEnumerateInstanceVersion" => FunctionType::Entry, // This is actually not a device level function "vkGetDeviceProcAddr" => FunctionType::Instance, - _ => { - if is_first_param_device { - FunctionType::Device - } else { - FunctionType::Instance - } - } + _ if is_first_param_device => FunctionType::Device, + _ => FunctionType::Instance, } } } @@ -600,7 +654,7 @@ fn name_to_tokens(type_name: &str) -> Ident { _ => type_name.strip_prefix("Vk").unwrap_or(type_name), }; let new_name = new_name.replace("FlagBits", "Flags"); - format_ident!("{}", new_name.as_str()) + format_ident!("{}", new_name) } /// Parses and rewrites a C literal into Rust @@ -611,7 +665,7 @@ fn name_to_tokens(type_name: &str) -> Ident { /// Examples: /// - `0x3FFU` -> `0x3ffu32` fn convert_c_literal(lit: Literal) -> Literal { - if let Ok((_, (_, rexpr))) = parse_cexpr::>(&lit.to_string()) { + if let Ok(("", (_, rexpr))) = parse_cexpr(&lit.to_string()) { // lit::SynInt uses the same `.parse` method to create hexadecimal // literals because there is no `Literal` constructor for it. let mut stream = rexpr.parse::().unwrap().into_iter(); @@ -679,7 +733,7 @@ impl FieldExt for vkxml::Field { "type" => "ty", _ => name, }; - format_ident!("{}", name_corrected.to_snake_case().as_str()) + format_ident!("{}", name_corrected.to_snake_case()) } fn inner_type_tokens( @@ -768,11 +822,72 @@ impl FieldExt for vkxml::Field { } } -pub type CommandMap<'a> = HashMap; +impl FieldExt for vk_parse::CommandParam { + fn param_ident(&self) -> Ident { + let name = self.definition.name.as_str(); + let name_corrected = match name { + "type" => "ty", + _ => name, + }; + format_ident!("{}", name_corrected.to_snake_case()) + } + + fn inner_type_tokens( + &self, + _lifetime: Option, + _inner_length: Option, + ) -> TokenStream { + unimplemented!() + } + + fn safe_type_tokens( + &self, + _lifetime: TokenStream, + _inner_length: Option, + ) -> TokenStream { + unimplemented!() + } + + fn type_tokens(&self, is_ffi_param: bool) -> TokenStream { + assert!(!self.is_void(), "{:?}", self); + let (rem, ty) = parse_c_parameter(&self.definition.code).unwrap(); + assert!(rem.is_empty()); + let type_name = name_to_tokens(ty.type_.name); + let inner_ty = match ty.type_.reference_type { + CReferenceType::Value => quote!(#type_name), + CReferenceType::Pointer => { + quote!(*mut #type_name) + } + CReferenceType::PointerToConst => quote!(*const #type_name), + CReferenceType::PointerToPointer => quote!(*mut *mut #type_name), + CReferenceType::PointerToPointerToConst => quote!(*mut *const #type_name), + CReferenceType::PointerToConstPointer => quote!(*const *mut #type_name), + CReferenceType::PointerToConstPointerToConst => quote!(*const *const #type_name), + }; + + match ty.static_array { + None => inner_ty, + Some(len) if is_ffi_param => quote!(*const [#inner_ty; #len]), + Some(len) => quote!([#inner_ty; #len]), + } + } + + fn is_void(&self) -> bool { + self.definition.type_name.as_deref() == Some("void") + && self.len.is_none() + && !self.definition.name.starts_with('p') + } + + fn is_pointer_to_static_sized_array(&self) -> bool { + unimplemented!() + } +} + +pub type CommandMap<'a> = HashMap; fn generate_function_pointers<'a>( ident: Ident, - commands: &[&'a vkxml::Command], + commands: &[&'a vk_parse::CommandDefinition], aliases: &HashMap, fn_cache: &mut HashSet<&'a str>, ) -> TokenStream { @@ -780,7 +895,7 @@ fn generate_function_pointers<'a>( // really want to generate one function pointer. let commands = commands .iter() - .unique_by(|cmd| cmd.name.as_str()) + .unique_by(|cmd| cmd.proto.name.as_str()) .collect::>(); struct Command { @@ -796,28 +911,25 @@ fn generate_function_pointers<'a>( let commands = commands .iter() .map(|cmd| { - let type_name = format_ident!("PFN_{}", cmd.name); + let name = &cmd.proto.name; + let type_name = format_ident!("PFN_{}", name); - let function_name_c = if let Some(alias_name) = aliases.get(&cmd.name) { + let function_name_c = if let Some(alias_name) = aliases.get(name) { alias_name.to_string() } else { - cmd.name.to_string() + name.to_string() }; let function_name_rust = format_ident!( "{}", - function_name_c - .strip_prefix("vk") - .unwrap() - .to_snake_case() - .as_str() + function_name_c.strip_prefix("vk").unwrap().to_snake_case() ); let params: Vec<_> = cmd - .param + .params .iter() - .map(|field| { - let name = field.param_ident(); - let ty = field.type_tokens(true); + .map(|param| { + let name = param.param_ident(); + let ty = param.type_tokens(true); (name, ty) }) .collect(); @@ -833,19 +945,25 @@ fn generate_function_pointers<'a>( }); let parameters_unused = quote!(#(#params_iter,)*); + let ret = cmd + .proto + .type_name + .as_ref() + .expect("Command must have return type"); + Command { // PFN function pointers are global and can not have duplicates. // This can happen because there are aliases to commands - type_needs_defining: fn_cache.insert(cmd.name.as_str()), + type_needs_defining: fn_cache.insert(name), type_name, function_name_c, function_name_rust, parameters, parameters_unused, - returns: if cmd.return_type.is_void() { + returns: if ret == "void" { quote!() } else { - let ret_ty_tokens = cmd.return_type.type_tokens(true); + let ret_ty_tokens = name_to_tokens(ret); quote!(-> #ret_ty_tokens) }, } @@ -1042,11 +1160,9 @@ pub fn generate_extension_commands<'a>( for name in names { if let Some(cmd) = cmd_map.get(name).copied() { commands.push(cmd); - } else if let Some(cmd) = cmd_aliases - .get(name) - .and_then(|alias_name| cmd_map.get(alias_name).copied()) - { - aliases.insert(cmd.name.clone(), name.to_string()); + } else if let Some(cmd) = cmd_aliases.get(name) { + aliases.insert(cmd.clone(), name.to_string()); + let cmd = cmd_map.get(cmd).copied().unwrap(); commands.push(cmd); } } @@ -1098,12 +1214,6 @@ pub fn generate_extension<'a>( cmd_aliases: &HashMap, fn_cache: &mut HashSet<&'a str>, ) -> Option { - // Okay this is a little bit odd. We need to generate all extensions, even disabled ones, - // because otherwise some StructureTypes won't get generated. But we don't generate extensions - // that are reserved - if extension.name.contains("RESERVED") { - return None; - } let extension_tokens = generate_extension_constants( &extension.name, extension.number.unwrap_or(0), @@ -1139,8 +1249,7 @@ pub fn generate_define( if define_name.contains("VERSION") && !spec.code.contains("//#define") { let link = khronos_link(define_name); - let (c_expr, (comment, (_name, parameters))) = - parse_c_define_header::>(&spec.code).unwrap(); + let (c_expr, (comment, (_name, parameters))) = parse_c_define_header(&spec.code).unwrap(); let c_expr = c_expr.trim().trim_start_matches('\\'); let c_expr = c_expr.replace("(uint32_t)", ""); let c_expr = convert_c_expression(&c_expr, identifier_renames); @@ -1349,7 +1458,7 @@ pub fn generate_enum<'a>( let name = enum_.name.as_ref().unwrap(); let clean_name = name.strip_prefix("Vk").unwrap(); let clean_name = clean_name.replace("FlagBits", "Flags"); - let ident = format_ident!("{}", clean_name.as_str()); + let ident = format_ident!("{}", clean_name); let constants = enum_ .children .iter() @@ -1376,7 +1485,7 @@ pub fn generate_enum<'a>( let khronos_link = khronos_link(name); if name.contains("Bit") { - let ident = format_ident!("{}", clean_name.as_str()); + let ident = format_ident!("{}", clean_name); let type_ = if enum_.bitwidth == Some(64u32) { quote!(Flags64) @@ -1431,9 +1540,9 @@ pub fn generate_enum<'a>( pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream { let notation = enum_.children.iter().filter_map(|elem| { - let (variant_name, notation) = match *elem { - vk_parse::EnumsChild::Enum(ref constant) => ( - constant.name.as_str(), + let (variant_name, notation) = match elem { + vk_parse::EnumsChild::Enum(constant) => ( + &constant.name, constant.formatted_notation().unwrap_or(Cow::Borrowed("")), ), _ => { @@ -1769,7 +1878,7 @@ pub fn derive_setters( array_size }; - let array_size_ident = format_ident!("{}", array_size.to_snake_case().as_str()); + let array_size_ident = format_ident!("{}", array_size.to_snake_case()); let size_field = members.clone().find(|m| m.name.as_deref() == Some(array_size)).unwrap(); @@ -2109,7 +2218,7 @@ pub fn generate_handle(handle: &vkxml::Handle) -> Option { Some(tokens) } fn generate_funcptr(fnptr: &vkxml::FunctionPointer) -> TokenStream { - let name = format_ident!("{}", fnptr.name.as_str()); + let name = format_ident!("{}", fnptr.name); let ret_ty_tokens = if fnptr.return_type.is_void() { quote!() } else { @@ -2425,7 +2534,7 @@ pub fn extract_native_types(registry: &vk_parse::Registry) -> (Vec<(String, Stri "Header `{name}` being redefined", ); - let (rem, path) = parse_c_include::>(&code.code) + let (rem, path) = parse_c_include(&code.code) .expect("Failed to parse `#include` from `category=\"include\"` directive"); assert!(rem.is_empty()); header_includes.push((name, path)); @@ -2499,12 +2608,13 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { .map(|(name, alias)| (name.to_string(), alias.to_string())) .collect(); - let commands: HashMap = spec - .elements + let commands: CommandMap<'_> = spec2 + .0 .iter() - .filter_map(get_variant!(vkxml::RegistryElement::Commands)) - .flat_map(|cmds| &cmds.elements) - .map(|cmd| (cmd.name.clone(), cmd)) + .filter_map(get_variant!(vk_parse::RegistryChild::Commands)) + .flat_map(|cmds| &cmds.children) + .filter_map(get_variant!(vk_parse::Command::Definition)) + .map(|cmd| (cmd.proto.name.clone(), cmd)) .collect(); let features: Vec<&vkxml::Feature> = spec