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