From b18ad63cba195cfb60372f100a8b66017ccdb55b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 6 May 2023 20:30:46 +0200 Subject: [PATCH] extensions/ext: Add VK_EXT_pipeline_properties device extension (#622) * extensions/ext: Add VK_EXT_pipeline_properties device extension * Generate traits and impls for all `validstructs` on command parameters --- .github/workflows/ci.yml | 2 +- Changelog.md | 1 + ash/src/extensions/ext/mod.rs | 2 + ash/src/extensions/ext/pipeline_properties.rs | 49 +++++++++++++ ash/src/vk/extensions.rs | 4 ++ generator/src/lib.rs | 69 ++++++++++++++++--- 6 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 ash/src/extensions/ext/pipeline_properties.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b25958777..a0132f37c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - name: Run generator run: cargo run -p generator - name: Diff autogen result - run: git diff --quiet || (echo "::error::Generated files are different, please regenerate with cargo run -p generator!"; git diff; false) + run: test -z "$(git status --porcelain)" || (echo "::error::Generated files are different, please regenerate with cargo run -p generator!"; git diff; false) test: name: Test Suite diff --git a/Changelog.md b/Changelog.md index 8fda52738..0a07baaca 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `VK_EXT_pipeline_properties` device extension (#622) - Update Vulkan-Headers to 1.3.246 (#697, #723) - Added `VK_KHR_performance_query` device extension (#726) - Added `VK_EXT_shader_object` device extension (#732) diff --git a/ash/src/extensions/ext/mod.rs b/ash/src/extensions/ext/mod.rs index d686f1b8d..8d4c5a2f2 100644 --- a/ash/src/extensions/ext/mod.rs +++ b/ash/src/extensions/ext/mod.rs @@ -17,6 +17,7 @@ pub use self::image_drm_format_modifier::ImageDrmFormatModifier; pub use self::mesh_shader::MeshShader; pub use self::metal_surface::MetalSurface; pub use self::physical_device_drm::PhysicalDeviceDrm; +pub use self::pipeline_properties::PipelineProperties; pub use self::private_data::PrivateData; pub use self::sample_locations::SampleLocations; pub use self::shader_object::ShaderObject; @@ -41,6 +42,7 @@ mod image_drm_format_modifier; mod mesh_shader; mod metal_surface; mod physical_device_drm; +mod pipeline_properties; mod private_data; mod sample_locations; mod shader_object; diff --git a/ash/src/extensions/ext/pipeline_properties.rs b/ash/src/extensions/ext/pipeline_properties.rs new file mode 100644 index 000000000..12ebc6310 --- /dev/null +++ b/ash/src/extensions/ext/pipeline_properties.rs @@ -0,0 +1,49 @@ +use crate::prelude::*; +use crate::vk; +use crate::{Device, Instance}; +use std::ffi::CStr; +use std::mem; + +/// +#[derive(Clone)] +pub struct PipelineProperties { + handle: vk::Device, + fp: vk::ExtPipelinePropertiesFn, +} + +impl PipelineProperties { + pub fn new(instance: &Instance, device: &Device) -> Self { + let handle = device.handle(); + let fp = vk::ExtPipelinePropertiesFn::load(|name| unsafe { + mem::transmute(instance.get_device_proc_addr(handle, name.as_ptr())) + }); + Self { handle, fp } + } + + /// + #[inline] + pub unsafe fn get_pipeline_properties( + &self, + pipeline_info: &vk::PipelineInfoEXT, + pipeline_properties: &mut impl vk::GetPipelinePropertiesEXTParamPipelineProperties, + ) -> VkResult<()> { + (self.fp.get_pipeline_properties_ext)( + self.handle, + pipeline_info, + <*mut _>::cast(pipeline_properties), + ) + .result() + } + + pub const NAME: &'static CStr = vk::ExtPipelinePropertiesFn::NAME; + + #[inline] + pub fn fp(&self) -> &vk::ExtPipelinePropertiesFn { + &self.fp + } + + #[inline] + pub fn device(&self) -> vk::Device { + self.handle + } +} diff --git a/ash/src/vk/extensions.rs b/ash/src/vk/extensions.rs index 78874d543..1dec9d18e 100644 --- a/ash/src/vk/extensions.rs +++ b/ash/src/vk/extensions.rs @@ -20233,6 +20233,10 @@ impl ExtPipelinePropertiesFn { pub const SPEC_VERSION: u32 = 1u32; } #[allow(non_camel_case_types)] +#[doc = "Implemented for all types that can be passed as argument to `pipeline_properties` in [`PFN_vkGetPipelinePropertiesEXT`]"] +pub unsafe trait GetPipelinePropertiesEXTParamPipelineProperties {} +unsafe impl GetPipelinePropertiesEXTParamPipelineProperties for PipelinePropertiesIdentifierEXT {} +#[allow(non_camel_case_types)] pub type PFN_vkGetPipelinePropertiesEXT = unsafe extern "system" fn( device: Device, p_pipeline_info: *const PipelineInfoEXT, diff --git a/generator/src/lib.rs b/generator/src/lib.rs index 188c92faf..25946e7b3 100644 --- a/generator/src/lib.rs +++ b/generator/src/lib.rs @@ -906,32 +906,36 @@ fn generate_function_pointers<'a>( struct Command<'a> { type_needs_defining: bool, type_name: Ident, + pfn_type_name: Ident, function_name_c: &'a str, function_name_rust: Ident, parameters: TokenStream, parameters_unused: TokenStream, returns: TokenStream, + parameter_validstructs: Vec<(Ident, Vec)>, } let commands = commands .iter() .map(|cmd| { let name = &cmd.proto.name; - let type_name = format_ident!("PFN_{}", name); + let pfn_type_name = format_ident!("PFN_{}", name); // We might need to generate a function pointer for an extension, where we are given the original // `cmd` and a rename back to the extension alias (typically with vendor suffix) in `rename_commands`: let function_name_c = rename_commands.get(name.as_str()).cloned().unwrap_or(name); - let function_name_rust = format_ident!( - "{}", - function_name_c.strip_prefix("vk").unwrap().to_snake_case() - ); + let type_name = function_name_c.strip_prefix("vk").unwrap(); + let function_name_rust = format_ident!("{}", type_name.to_snake_case()); + let type_name = format_ident!("{}", type_name); - let params: Vec<_> = cmd + let params = cmd .params .iter() - .filter(|param| matches!(param.api.as_deref(), None | Some(DESIRED_API))) + .filter(|param| matches!(param.api.as_deref(), None | Some(DESIRED_API))); + + let params_tokens: Vec<_> = params + .clone() .map(|param| { let name = param.param_ident(); let ty = param.type_tokens(true); @@ -939,17 +943,22 @@ fn generate_function_pointers<'a>( }) .collect(); - let params_iter = params + let params_iter = params_tokens .iter() .map(|(param_name, param_ty)| quote!(#param_name: #param_ty)); let parameters = quote!(#(#params_iter,)*); - let params_iter = params.iter().map(|(param_name, param_ty)| { + let params_iter = params_tokens.iter().map(|(param_name, param_ty)| { let unused_name = format_ident!("_{}", param_name); quote!(#unused_name: #param_ty) }); let parameters_unused = quote!(#(#params_iter,)*); + let parameter_validstructs: Vec<_> = params + .filter(|param| !param.validstructs.is_empty()) + .map(|param| (param.param_ident(), param.validstructs.clone())) + .collect(); + let ret = cmd .proto .type_name @@ -961,6 +970,7 @@ fn generate_function_pointers<'a>( // This can happen because there are aliases to commands type_needs_defining: fn_cache.insert(name), type_name, + pfn_type_name, function_name_c, function_name_rust, parameters, @@ -971,14 +981,49 @@ fn generate_function_pointers<'a>( let ret_ty_tokens = name_to_tokens(ret); quote!(-> #ret_ty_tokens) }, + parameter_validstructs, } }) .collect::>(); + struct CommandToParamTraits<'a>(&'a Command<'a>); + impl<'a> quote::ToTokens for CommandToParamTraits<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + for (param_ident, validstructs) in &self.0.parameter_validstructs { + let param_ident = param_ident.to_string(); + let param_ident = param_ident + .strip_prefix("pp_") + .or_else(|| param_ident.strip_prefix("p_")) + .unwrap_or(¶m_ident); + + let doc_string = format!( + "Implemented for all types that can be passed as argument to `{}` in [`{}`]", + param_ident, self.0.pfn_type_name + ); + let param_trait_name = format_ident!( + "{}Param{}", + self.0.type_name, + param_ident.to_upper_camel_case() + ); + quote! { + #[allow(non_camel_case_types)] + #[doc = #doc_string] + pub unsafe trait #param_trait_name {} + } + .to_tokens(tokens); + + for validstruct in validstructs { + let structname = name_to_tokens(validstruct); + quote!(unsafe impl #param_trait_name for #structname {}).to_tokens(tokens); + } + } + } + } + struct CommandToType<'a>(&'a Command<'a>); impl<'a> quote::ToTokens for CommandToType<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let type_name = &self.0.type_name; + let type_name = &self.0.pfn_type_name; let parameters = &self.0.parameters; let returns = &self.0.returns; quote!( @@ -992,7 +1037,7 @@ fn generate_function_pointers<'a>( struct CommandToMember<'a>(&'a Command<'a>); impl<'a> quote::ToTokens for CommandToMember<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let type_name = &self.0.type_name; + let type_name = &self.0.pfn_type_name; let type_name = if self.0.type_needs_defining { // Type is defined in local scope quote!(#type_name) @@ -1033,6 +1078,7 @@ fn generate_function_pointers<'a>( } } + let param_traits = commands.iter().map(CommandToParamTraits); let pfn_typedefs = commands .iter() .filter(|pfn| pfn.type_needs_defining) @@ -1041,6 +1087,7 @@ fn generate_function_pointers<'a>( let loaders = commands.iter().map(CommandToLoader); quote! { + #(#param_traits)* #(#pfn_typedefs)* #[derive(Clone)]