1 // Copyright (c) 2021 The Vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use super::{write_file, IndexMap, VkRegistryData};
11 use ahash::HashMap;
12 use heck::ToSnakeCase;
13 use proc_macro2::{Ident, TokenStream};
14 use quote::{format_ident, quote};
15 use regex::Regex;
16 use std::{collections::hash_map::Entry, fmt::Write as _};
17 use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
18 
write(vk_data: &VkRegistryData)19 pub fn write(vk_data: &VkRegistryData) {
20     let properties_output = properties_output(&properties_members(&vk_data.types));
21     let properties_ffi_output =
22         properties_ffi_output(&properties_ffi_members(&vk_data.types, &vk_data.extensions));
23     write_file(
24         "properties.rs",
25         format!(
26             "vk.xml header version {}.{}.{}",
27             vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
28         ),
29         quote! {
30             #properties_output
31             #properties_ffi_output
32         },
33     );
34 }
35 
36 #[derive(Clone, Debug)]
37 struct PropertiesMember {
38     name: Ident,
39     ty: TokenStream,
40     doc: String,
41     raw: String,
42     ffi_name: Ident,
43     ffi_members: Vec<(Ident, TokenStream)>,
44     optional: bool,
45 }
46 
properties_output(members: &[PropertiesMember]) -> TokenStream47 fn properties_output(members: &[PropertiesMember]) -> TokenStream {
48     let struct_items = members.iter().map(
49         |PropertiesMember {
50              name,
51              ty,
52              doc,
53              optional,
54              ..
55          }| {
56             if *optional {
57                 quote! {
58                     #[doc = #doc]
59                     pub #name: Option<#ty>,
60                 }
61             } else {
62                 quote! {
63                     #[doc = #doc]
64                     pub #name: #ty,
65                 }
66             }
67         },
68     );
69 
70     let default_items = members.iter().map(|PropertiesMember { name, .. }| {
71         quote! {
72             #name: Default::default(),
73         }
74     });
75 
76     let from_items = members.iter().map(
77         |PropertiesMember {
78              name,
79              ty,
80              ffi_name,
81              ffi_members,
82              optional,
83              ..
84          }| {
85             if *optional {
86                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
87                     quote! { properties_ffi.#ffi_member.map(|s| s #ffi_member_field .#ffi_name) }
88                 });
89 
90                 quote! {
91                     #name: [
92                         #(#ffi_members),*
93                     ].into_iter().flatten().next().and_then(<#ty>::from_vulkan),
94                 }
95             } else {
96                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
97                     quote! { properties_ffi.#ffi_member #ffi_member_field .#ffi_name }
98                 });
99 
100                 quote! {
101                     #name: [
102                         #(#ffi_members),*
103                     ].into_iter().next().and_then(<#ty>::from_vulkan).unwrap(),
104                 }
105             }
106         },
107     );
108 
109     quote! {
110         /// Represents all the properties of a physical device.
111         ///
112         /// Depending on the highest version of Vulkan supported by the physical device, and the
113         /// available extensions, not every property may be available. For that reason, some
114         /// properties are wrapped in an `Option`.
115         #[derive(Clone, Debug)]
116         pub struct Properties {
117             #(#struct_items)*
118             pub _ne: crate::NonExhaustive,
119         }
120 
121         impl Default for Properties {
122             fn default() -> Self {
123                 Properties {
124                     #(#default_items)*
125                     _ne: crate::NonExhaustive(()),
126                 }
127             }
128         }
129 
130         impl From<&PropertiesFfi> for Properties {
131             fn from(properties_ffi: &PropertiesFfi) -> Self {
132                 Properties {
133                     #(#from_items)*
134                     _ne: crate::NonExhaustive(()),
135                 }
136             }
137         }
138     }
139 }
140 
properties_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<PropertiesMember>141 fn properties_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<PropertiesMember> {
142     let mut properties = HashMap::default();
143 
144     [
145         &types["VkPhysicalDeviceProperties"],
146         &types["VkPhysicalDeviceLimits"],
147         &types["VkPhysicalDeviceSparseProperties"],
148     ]
149     .into_iter()
150     .chain(sorted_structs(types).into_iter())
151     .filter(|(ty, _)| {
152         let name = ty.name.as_deref();
153         name == Some("VkPhysicalDeviceProperties")
154             || name == Some("VkPhysicalDeviceLimits")
155             || name == Some("VkPhysicalDeviceSparseProperties")
156             || ty.structextends.as_deref() == Some("VkPhysicalDeviceProperties2")
157     })
158     .for_each(|(ty, _)| {
159         let vulkan_ty_name = ty.name.as_ref().unwrap();
160 
161         let (ty_name, optional) = if vulkan_ty_name == "VkPhysicalDeviceProperties" {
162             (
163                 (format_ident!("properties_vulkan10"), quote! { .properties }),
164                 false,
165             )
166         } else if vulkan_ty_name == "VkPhysicalDeviceLimits" {
167             (
168                 (
169                     format_ident!("properties_vulkan10"),
170                     quote! { .properties.limits },
171                 ),
172                 false,
173             )
174         } else if vulkan_ty_name == "VkPhysicalDeviceSparseProperties" {
175             (
176                 (
177                     format_ident!("properties_vulkan10"),
178                     quote! { .properties.sparse_properties },
179                 ),
180                 false,
181             )
182         } else {
183             (
184                 (format_ident!("{}", ffi_member(vulkan_ty_name)), quote! {}),
185                 true,
186             )
187         };
188 
189         members(ty)
190             .into_iter()
191             .for_each(|Member { name, ty, len }| {
192                 if ty == "VkPhysicalDeviceLimits" || ty == "VkPhysicalDeviceSparseProperties" {
193                     return;
194                 }
195 
196                 let vulkano_member = name.to_snake_case();
197                 let vulkano_ty = match name {
198                     "apiVersion" => quote! { Version },
199                     "bufferImageGranularity"
200                     | "minStorageBufferOffsetAlignment"
201                     | "minTexelBufferOffsetAlignment"
202                     | "minUniformBufferOffsetAlignment"
203                     | "nonCoherentAtomSize"
204                     | "optimalBufferCopyOffsetAlignment"
205                     | "optimalBufferCopyRowPitchAlignment"
206                     | "robustStorageBufferAccessSizeAlignment"
207                     | "robustUniformBufferAccessSizeAlignment"
208                     | "storageTexelBufferOffsetAlignmentBytes"
209                     | "uniformTexelBufferOffsetAlignmentBytes" => {
210                         quote! { DeviceAlignment }
211                     }
212                     _ => vulkano_type(ty, len),
213                 };
214                 match properties.entry(vulkano_member.clone()) {
215                     Entry::Vacant(entry) => {
216                         let mut member = PropertiesMember {
217                             name: format_ident!("{}", vulkano_member),
218                             ty: vulkano_ty,
219                             doc: String::new(),
220                             raw: name.to_owned(),
221                             ffi_name: format_ident!("{}", vulkano_member),
222                             ffi_members: vec![ty_name.clone()],
223                             optional,
224                         };
225                         make_doc(&mut member, vulkan_ty_name);
226                         entry.insert(member);
227                     }
228                     Entry::Occupied(entry) => {
229                         entry.into_mut().ffi_members.push(ty_name.clone());
230                     }
231                 };
232             });
233     });
234 
235     let mut names: Vec<_> = properties
236         .values()
237         .map(|prop| prop.name.to_string())
238         .collect();
239     names.sort_unstable();
240     names
241         .into_iter()
242         .map(|name| properties.remove(&name).unwrap())
243         .collect()
244 }
245 
make_doc(prop: &mut PropertiesMember, vulkan_ty_name: &str)246 fn make_doc(prop: &mut PropertiesMember, vulkan_ty_name: &str) {
247     let writer = &mut prop.doc;
248     write!(
249         writer,
250         "- [Vulkan documentation](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/{}.html#limits-{})",
251         vulkan_ty_name,
252         prop.raw
253     )
254     .unwrap();
255 }
256 
257 #[derive(Clone, Debug)]
258 struct PropertiesFfiMember {
259     name: Ident,
260     ty: Ident,
261     provided_by: Vec<TokenStream>,
262     conflicts: Vec<Ident>,
263 }
264 
properties_ffi_output(members: &[PropertiesFfiMember]) -> TokenStream265 fn properties_ffi_output(members: &[PropertiesFfiMember]) -> TokenStream {
266     let struct_items = members.iter().map(|PropertiesFfiMember { name, ty, .. }| {
267         quote! { #name: Option<ash::vk::#ty>, }
268     });
269 
270     let make_chain_items = members.iter().map(
271         |PropertiesFfiMember {
272              name,
273              provided_by,
274              conflicts,
275              ..
276          }| {
277             quote! {
278                 if [#(#provided_by),*].into_iter().any(|x| x) &&
279                     [#(self.#conflicts.is_none()),*].into_iter().all(|x| x) {
280                     self.#name = Some(Default::default());
281                     let member = self.#name.as_mut().unwrap();
282                     member.p_next = head.p_next;
283                     head.p_next = member as *mut _ as _;
284                 }
285             }
286         },
287     );
288 
289     quote! {
290         #[derive(Default)]
291         pub(crate) struct PropertiesFfi {
292             properties_vulkan10: ash::vk::PhysicalDeviceProperties2KHR,
293             #(#struct_items)*
294         }
295 
296         impl PropertiesFfi {
297             pub(crate) fn make_chain(
298                 &mut self,
299                 api_version: Version,
300                 device_extensions: &DeviceExtensions,
301                 instance_extensions: &InstanceExtensions,
302             ) {
303                 self.properties_vulkan10 = Default::default();
304                 let head = &mut self.properties_vulkan10;
305                 #(#make_chain_items)*
306             }
307 
308             pub(crate) fn head_as_mut(&mut self) -> &mut ash::vk::PhysicalDeviceProperties2KHR {
309                 &mut self.properties_vulkan10
310             }
311         }
312     }
313 }
314 
properties_ffi_members<'a>( types: &'a HashMap<&str, (&Type, Vec<&str>)>, extensions: &IndexMap<&'a str, &Extension>, ) -> Vec<PropertiesFfiMember>315 fn properties_ffi_members<'a>(
316     types: &'a HashMap<&str, (&Type, Vec<&str>)>,
317     extensions: &IndexMap<&'a str, &Extension>,
318 ) -> Vec<PropertiesFfiMember> {
319     let mut property_included_in: HashMap<&str, Vec<&str>> = HashMap::default();
320     sorted_structs(types)
321         .into_iter()
322         .map(|(ty, provided_by)| {
323             let ty_name = ty.name.as_ref().unwrap();
324             let provided_by = provided_by
325                 .iter()
326                 .map(|provided_by| {
327                     if let Some(version) = provided_by.strip_prefix("VK_VERSION_") {
328                         let version = format_ident!("V{}", version);
329                         quote! { api_version >= Version::#version }
330                     } else {
331                         let member = format_ident!(
332                             "{}_extensions",
333                             extensions[provided_by].ext_type.as_ref().unwrap().as_str()
334                         );
335                         let name = format_ident!(
336                             "{}",
337                             provided_by
338                                 .strip_prefix("VK_")
339                                 .unwrap()
340                                 .to_ascii_lowercase(),
341                         );
342 
343                         quote! { #member.#name }
344                     }
345                 })
346                 .collect();
347             let mut conflicts = vec![];
348             members(ty).into_iter().for_each(|Member { name, .. }| {
349                 match property_included_in.entry(name) {
350                     Entry::Vacant(entry) => {
351                         entry.insert(vec![ty_name]);
352                     }
353                     Entry::Occupied(entry) => {
354                         let conflicters = entry.into_mut();
355                         conflicters.iter().for_each(|conflicter| {
356                             let conflicter = ffi_member(conflicter);
357                             if !conflicts.contains(&conflicter) {
358                                 conflicts.push(conflicter);
359                             }
360                         });
361                         conflicters.push(ty_name);
362                     }
363                 }
364             });
365 
366             PropertiesFfiMember {
367                 name: format_ident!("{}", ffi_member(ty_name)),
368                 ty: format_ident!("{}", ty_name.strip_prefix("Vk").unwrap()),
369                 provided_by,
370                 conflicts: conflicts
371                     .into_iter()
372                     .map(|s| format_ident!("{}", s))
373                     .collect(),
374             }
375         })
376         .collect()
377 }
378 
sorted_structs<'a>( types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>, ) -> Vec<&'a (&'a Type, Vec<&'a str>)>379 fn sorted_structs<'a>(
380     types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>,
381 ) -> Vec<&'a (&'a Type, Vec<&'a str>)> {
382     let mut structs: Vec<_> = types
383         .values()
384         .filter(|(ty, _)| ty.structextends.as_deref() == Some("VkPhysicalDeviceProperties2"))
385         .collect();
386     let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Properties$").unwrap();
387     structs.sort_unstable_by_key(|&(ty, provided_by)| {
388         let name = ty.name.as_ref().unwrap();
389         (
390             !regex.is_match(name),
391             if let Some(version) = provided_by
392                 .iter()
393                 .find_map(|s| s.strip_prefix("VK_VERSION_"))
394             {
395                 let (major, minor) = version.split_once('_').unwrap();
396                 major.parse::<i32>().unwrap() << 22 | minor.parse::<i32>().unwrap() << 12
397             } else if provided_by.iter().any(|s| s.starts_with("VK_KHR_")) {
398                 i32::MAX - 2
399             } else if provided_by.iter().any(|s| s.starts_with("VK_EXT_")) {
400                 i32::MAX - 1
401             } else {
402                 i32::MAX
403             },
404             name,
405         )
406     });
407 
408     structs
409 }
410 
ffi_member(ty_name: &str) -> String411 fn ffi_member(ty_name: &str) -> String {
412     let ty_name = ty_name
413         .strip_prefix("VkPhysicalDevice")
414         .unwrap()
415         .to_snake_case();
416     let (base, suffix) = ty_name.rsplit_once("_properties").unwrap();
417     format!("properties_{}{}", base, suffix)
418 }
419 
420 struct Member<'a> {
421     name: &'a str,
422     ty: &'a str,
423     len: Option<&'a str>,
424 }
425 
members(ty: &Type) -> Vec<Member>426 fn members(ty: &Type) -> Vec<Member> {
427     let regex = Regex::new(r"\[([A-Za-z0-9_]+)\]\s*$").unwrap();
428     if let TypeSpec::Members(members) = &ty.spec {
429         members
430             .iter()
431             .filter_map(|member| {
432                 if let TypeMember::Definition(def) = member {
433                     let name = def.markup.iter().find_map(|markup| match markup {
434                         TypeMemberMarkup::Name(name) => Some(name.as_str()),
435                         _ => None,
436                     });
437                     let ty = def.markup.iter().find_map(|markup| match markup {
438                         TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
439                         _ => None,
440                     });
441                     let len = def
442                         .markup
443                         .iter()
444                         .find_map(|markup| match markup {
445                             TypeMemberMarkup::Enum(len) => Some(len.as_str()),
446                             _ => None,
447                         })
448                         .or_else(|| {
449                             regex
450                                 .captures(&def.code)
451                                 .and_then(|cap| cap.get(1))
452                                 .map(|m| m.as_str())
453                         });
454                     if name != Some("sType") && name != Some("pNext") {
455                         return name.map(|name| Member {
456                             name,
457                             ty: ty.unwrap(),
458                             len,
459                         });
460                     }
461                 }
462                 None
463             })
464             .collect()
465     } else {
466         vec![]
467     }
468 }
469 
vulkano_type(ty: &str, len: Option<&str>) -> TokenStream470 fn vulkano_type(ty: &str, len: Option<&str>) -> TokenStream {
471     if let Some(len) = len {
472         match ty {
473             "char" => quote! { String },
474             "uint8_t" if len == "VK_LUID_SIZE" => quote! { [u8; 8] },
475             "uint8_t" if len == "VK_UUID_SIZE" => quote! { [u8; 16] },
476             "uint32_t" if len == "2" => quote! { [u32; 2] },
477             "uint32_t" if len == "3" => quote! { [u32; 3] },
478             "float" if len == "2" => quote! { [f32; 2] },
479             _ => unimplemented!("{}[{}]", ty, len),
480         }
481     } else {
482         match ty {
483             "float" => quote! { f32 },
484             "int32_t" => quote! { i32 },
485             "int64_t" => quote! { i64 },
486             "size_t" => quote! { usize },
487             "uint8_t" => quote! { u8 },
488             "uint32_t" => quote! { u32 },
489             "uint64_t" => quote! { u64 },
490             "VkBool32" => quote! { bool },
491             "VkConformanceVersion" => quote! { ConformanceVersion },
492             "VkDeviceSize" => quote! { DeviceSize },
493             "VkDriverId" => quote! { DriverId },
494             "VkExtent2D" => quote! { [u32; 2] },
495             "VkMemoryDecompressionMethodFlagsNV" => quote! { MemoryDecompressionMethods },
496             "VkOpticalFlowGridSizeFlagsNV" => quote! { OpticalFlowGridSizes },
497             "VkPhysicalDeviceType" => quote! { PhysicalDeviceType },
498             "VkPipelineRobustnessBufferBehaviorEXT" => quote! { PipelineRobustnessBufferBehavior },
499             "VkPipelineRobustnessImageBehaviorEXT" => quote! { PipelineRobustnessImageBehavior },
500             "VkPointClippingBehavior" => quote! { PointClippingBehavior },
501             "VkQueueFlags" => quote! { QueueFlags },
502             "VkRayTracingInvocationReorderModeNV" => quote! { RayTracingInvocationReorderMode },
503             "VkResolveModeFlags" => quote! { ResolveModes },
504             "VkSampleCountFlags" => quote! { SampleCounts },
505             "VkSampleCountFlagBits" => quote! { SampleCount },
506             "VkShaderCorePropertiesFlagsAMD" => quote! { ShaderCoreProperties },
507             "VkShaderFloatControlsIndependence" => quote! { ShaderFloatControlsIndependence },
508             "VkShaderStageFlags" => quote! { ShaderStages },
509             "VkSubgroupFeatureFlags" => quote! { SubgroupFeatures },
510             _ => unimplemented!("{}", ty),
511         }
512     }
513 }
514