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 
19 // This is not included in vk.xml, so it's added here manually
requires_features(name: &str) -> &'static [&'static str]20 fn requires_features(name: &str) -> &'static [&'static str] {
21     match name {
22         "sparseImageInt64Atomics" => &["shaderImageInt64Atomics"],
23         "sparseImageFloat32Atomics" => &["shaderImageFloat32Atomics"],
24         "sparseImageFloat32AtomicAdd" => &["shaderImageFloat32AtomicAdd"],
25         "sparseImageFloat32AtomicMinMax" => &["shaderImageFloat32AtomicMinMax"],
26         _ => &[],
27     }
28 }
29 
conflicts_features(name: &str) -> &'static [&'static str]30 fn conflicts_features(name: &str) -> &'static [&'static str] {
31     match name {
32         "shadingRateImage" => &[
33             "pipelineFragmentShadingRate",
34             "primitiveFragmentShadingRate",
35             "attachmentFragmentShadingRate",
36         ],
37         "fragmentDensityMap" => &[
38             "pipelineFragmentShadingRate",
39             "primitiveFragmentShadingRate",
40             "attachmentFragmentShadingRate",
41         ],
42         "pipelineFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
43         "primitiveFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
44         "attachmentFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
45         _ => &[],
46     }
47 }
48 
required_by_extensions(name: &str) -> &'static [(&'static str, &'static str)]49 fn required_by_extensions(name: &str) -> &'static [(&'static str, &'static str)] {
50     match name {
51         "shaderDrawParameters" => &[("V1_2", "VK_KHR_shader_draw_parameters")],
52         "drawIndirectCount" => &[("V1_2", "VK_KHR_draw_indirect_count")],
53         "samplerMirrorClampToEdge" => &[("V1_2", "VK_KHR_sampler_mirror_clamp_to_edge")],
54         "descriptorIndexing" => &[("V1_2", "VK_EXT_descriptor_indexing")],
55         "samplerFilterMinmax" => &[("V1_2", "VK_EXT_sampler_filter_minmax")],
56         "shaderOutputViewportIndex" => &[("V1_2", "VK_EXT_shader_viewport_index_layer")],
57         "shaderOutputLayer" => &[("V1_2", "VK_EXT_shader_viewport_index_layer")],
58         _ => &[],
59     }
60 }
61 
write(vk_data: &VkRegistryData)62 pub fn write(vk_data: &VkRegistryData) {
63     let features_output = features_output(&features_members(&vk_data.types));
64     let features_ffi_output =
65         features_ffi_output(&features_ffi_members(&vk_data.types, &vk_data.extensions));
66     write_file(
67         "features.rs",
68         format!(
69             "vk.xml header version {}.{}.{}",
70             vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
71         ),
72         quote! {
73             #features_output
74             #features_ffi_output
75         },
76     );
77 }
78 
79 #[derive(Clone, Debug)]
80 struct FeaturesMember {
81     name: Ident,
82     doc: String,
83     raw: String,
84     ffi_name: Ident,
85     ffi_members: Vec<(Ident, TokenStream)>,
86     requires_features: Vec<Ident>,
87     conflicts_features: Vec<Ident>,
88     required_by_extensions: Vec<(Ident, Ident)>,
89     optional: bool,
90 }
91 
features_output(members: &[FeaturesMember]) -> TokenStream92 fn features_output(members: &[FeaturesMember]) -> TokenStream {
93     let struct_items = members.iter().map(|FeaturesMember { name, doc, .. }| {
94         quote! {
95             #[doc = #doc]
96             pub #name: bool,
97         }
98     });
99 
100     let check_requirements_items = members.iter().map(
101         |FeaturesMember {
102              name,
103              requires_features,
104              conflicts_features,
105              required_by_extensions,
106              ..
107          }| {
108             let name_string = name.to_string();
109             let requires_features_items = requires_features.iter().map(|feature| {
110                 let string = feature.to_string();
111                 quote! {
112                     if !self.#feature {
113                         return Err(crate::device::FeatureRestrictionError {
114                             feature: #name_string,
115                             restriction: crate::device::FeatureRestriction::RequiresFeature(#string),
116                         });
117                     }
118                 }
119             });
120             let conflicts_features_items = conflicts_features.iter().map(|feature| {
121                 let string = feature.to_string();
122                 quote! {
123                     if self.#feature {
124                         return Err(crate::device::FeatureRestrictionError {
125                             feature: #name_string,
126                             restriction: crate::device::FeatureRestriction::ConflictsFeature(#string),
127                         });
128                     }
129                 }
130             });
131             let required_by_extensions_items =
132                 required_by_extensions.iter().map(|(version, extension)| {
133                     let string = extension.to_string();
134                     quote! {
135                         if extensions.#extension && api_version >= crate::Version::#version {
136                             return Err(crate::device::FeatureRestrictionError {
137                                 feature: #name_string,
138                                 restriction: crate::device::FeatureRestriction::RequiredByExtension(#string),
139                             });
140                         }
141                     }
142                 });
143             quote! {
144                 if self.#name {
145                     if !supported.#name {
146                         return Err(crate::device::FeatureRestrictionError {
147                             feature: #name_string,
148                             restriction: crate::device::FeatureRestriction::NotSupported,
149                         });
150                     }
151 
152                     #(#requires_features_items)*
153                     #(#conflicts_features_items)*
154                 } else {
155                     #(#required_by_extensions_items)*
156                 }
157             }
158         },
159     );
160 
161     let empty_items = members.iter().map(|FeaturesMember { name, .. }| {
162         quote! {
163             #name: false,
164         }
165     });
166 
167     let all_items = members.iter().map(|FeaturesMember { name, .. }| {
168         quote! {
169             #name: true,
170         }
171     });
172 
173     let intersects_items = members.iter().map(|FeaturesMember { name, .. }| {
174         quote! {
175             (self.#name && other.#name)
176         }
177     });
178 
179     let contains_items = members.iter().map(|FeaturesMember { name, .. }| {
180         quote! {
181             (self.#name || !other.#name)
182         }
183     });
184 
185     let union_items = members.iter().map(|FeaturesMember { name, .. }| {
186         quote! {
187             #name: self.#name || other.#name,
188         }
189     });
190 
191     let intersection_items = members.iter().map(|FeaturesMember { name, .. }| {
192         quote! {
193             #name: self.#name && other.#name,
194         }
195     });
196 
197     let difference_items = members.iter().map(|FeaturesMember { name, .. }| {
198         quote! {
199             #name: self.#name && !other.#name,
200         }
201     });
202 
203     let symmetric_difference_items = members.iter().map(|FeaturesMember { name, .. }| {
204         quote! {
205             #name: self.#name ^ other.#name,
206         }
207     });
208 
209     let debug_items = members.iter().map(|FeaturesMember { name, raw, .. }| {
210         quote! {
211             if self.#name {
212                 if !first { write!(f, ", ")? }
213                 else { first = false; }
214                 f.write_str(#raw)?;
215             }
216         }
217     });
218 
219     let arr_items = members.iter().map(|FeaturesMember { name, raw, .. }| {
220         quote! {
221             (#raw, self.#name),
222         }
223     });
224     let arr_len = members.len();
225 
226     let write_items = members.iter().map(
227         |FeaturesMember {
228              name,
229              ffi_name,
230              ffi_members,
231              optional,
232              ..
233          }| {
234             if *optional {
235                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
236                     quote! { self.#ffi_member.as_mut().map(|s| &mut s #ffi_member_field .#ffi_name) }
237                 });
238                 quote! {
239                     if let Some(f) = [
240                         #(#ffi_members),*
241                     ].into_iter().flatten().next() {
242                         *f = features.#name as ash::vk::Bool32;
243                     }
244                 }
245             } else {
246                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
247                     quote! { &mut self.#ffi_member #ffi_member_field .#ffi_name }
248                 });
249                 quote! {
250                     if let Some(f) = [
251                         #(#ffi_members),*
252                     ].into_iter().next() {
253                         *f = features.#name as ash::vk::Bool32;
254                     }
255                 }
256             }
257         },
258     );
259 
260     let from_items = members.iter().map(
261         |FeaturesMember {
262              name,
263              ffi_name,
264              ffi_members,
265              optional,
266              ..
267          }| {
268             if *optional {
269                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
270                     quote! { features_ffi.#ffi_member.map(|s| s #ffi_member_field .#ffi_name) }
271                 });
272                 quote! {
273                     #name: [
274                         #(#ffi_members),*
275                     ].into_iter().flatten().next().unwrap_or(0) != 0,
276                 }
277             } else {
278                 let ffi_members = ffi_members.iter().map(|(ffi_member, ffi_member_field)| {
279                     quote! { features_ffi.#ffi_member #ffi_member_field .#ffi_name }
280                 });
281                 quote! {
282                     #name: [
283                         #(#ffi_members),*
284                     ].into_iter().next().unwrap_or(0) != 0,
285                 }
286             }
287         },
288     );
289 
290     quote! {
291         /// Represents all the features that are available on a physical device or enabled on
292         /// a logical device.
293         ///
294         /// Note that the `robust_buffer_access` is guaranteed to be supported by all Vulkan
295         /// implementations.
296         ///
297         /// # Examples
298         ///
299         /// ```
300         /// use vulkano::device::Features;
301         /// # let physical_device: vulkano::device::physical::PhysicalDevice = return;
302         /// let minimal_features = Features {
303         ///     geometry_shader: true,
304         ///     ..Features::empty()
305         /// };
306         ///
307         /// let optimal_features = vulkano::device::Features {
308         ///     geometry_shader: true,
309         ///     tessellation_shader: true,
310         ///     ..Features::empty()
311         /// };
312         ///
313         /// if !physical_device.supported_features().is_superset_of(&minimal_features) {
314         ///     panic!("The physical device is not good enough for this application.");
315         /// }
316         ///
317         /// assert!(optimal_features.is_superset_of(&minimal_features));
318         /// let features_to_request = optimal_features.intersection(physical_device.supported_features());
319         /// ```
320         #[derive(Copy, Clone, PartialEq, Eq, Hash)]
321         pub struct Features {
322             #(#struct_items)*
323             pub _ne: crate::NonExhaustive,
324         }
325 
326         impl Default for Features {
327             #[inline]
328             fn default() -> Self {
329                 Self::empty()
330             }
331         }
332 
333         impl Features {
334             /// Checks enabled features against the device version, device extensions and each
335             /// other.
336             pub(super) fn check_requirements(
337                 &self,
338                 supported: &Features,
339                 api_version: crate::Version,
340                 extensions: &crate::device::DeviceExtensions,
341             ) -> Result<(), crate::device::FeatureRestrictionError> {
342                 #(#check_requirements_items)*
343                 Ok(())
344             }
345 
346             /// Returns an `Features` object with none of the members set.
347             #[inline]
348             pub const fn empty() -> Self {
349                 Self {
350                     #(#empty_items)*
351                     _ne: crate::NonExhaustive(()),
352                 }
353             }
354 
355             /// Returns an `Features` object with none of the members set.
356             #[deprecated(since = "0.31.0", note = "Use `empty` instead.")]
357             #[inline]
358             pub const fn none() -> Self {
359                 Self::empty()
360             }
361 
362             /// Returns a `Features` object with all of the members set.
363             #[cfg(test)]
364             pub(crate) const fn all() -> Features {
365                 Features {
366                     #(#all_items)*
367                     _ne: crate::NonExhaustive(()),
368                 }
369             }
370 
371             /// Returns whether any members are set in both `self` and `other`.
372             #[inline]
373             pub const fn intersects(&self, other: &Self) -> bool {
374                 #(#intersects_items)||*
375             }
376 
377             /// Returns whether all members in `other` are set in `self`.
378             #[inline]
379             pub const fn contains(&self, other: &Self) -> bool {
380                 #(#contains_items)&&*
381             }
382 
383             /// Returns whether all members in `other` are set in `self`.
384             #[deprecated(since = "0.31.0", note = "Use `contains` instead.")]
385             #[inline]
386             pub const fn is_superset_of(&self, other: &Self) -> bool {
387                 self.contains(other)
388             }
389 
390             /// Returns the union of `self` and `other`.
391             #[inline]
392             pub const fn union(&self, other: &Self) -> Self {
393                 Self {
394                     #(#union_items)*
395                     _ne: crate::NonExhaustive(()),
396                 }
397             }
398 
399             /// Returns the intersection of `self` and `other`.
400             #[inline]
401             pub const fn intersection(&self, other: &Self) -> Self {
402                 Self {
403                     #(#intersection_items)*
404                     _ne: crate::NonExhaustive(()),
405                 }
406             }
407 
408             /// Returns `self` without the members set in `other`.
409             #[inline]
410             pub const fn difference(&self, other: &Self) -> Self {
411                 Self {
412                     #(#difference_items)*
413                     _ne: crate::NonExhaustive(()),
414                 }
415             }
416 
417             /// Returns the members set in `self` or `other`, but not both.
418             #[inline]
419             pub const fn symmetric_difference(&self, other: &Self) -> Self {
420                 Self {
421                     #(#symmetric_difference_items)*
422                     _ne: crate::NonExhaustive(()),
423                 }
424             }
425         }
426 
427         impl std::ops::BitAnd for Features {
428             type Output = Features;
429 
430             #[inline]
431             fn bitand(self, rhs: Self) -> Self::Output {
432                 self.intersection(&rhs)
433             }
434         }
435 
436         impl std::ops::BitAndAssign for Features {
437             #[inline]
438             fn bitand_assign(&mut self, rhs: Self) {
439                 *self = self.intersection(&rhs);
440             }
441         }
442 
443         impl std::ops::BitOr for Features {
444             type Output = Features;
445 
446             #[inline]
447             fn bitor(self, rhs: Self) -> Self::Output {
448                 self.union(&rhs)
449             }
450         }
451 
452         impl std::ops::BitOrAssign for Features {
453             #[inline]
454             fn bitor_assign(&mut self, rhs: Self) {
455                 *self = self.union(&rhs);
456             }
457         }
458 
459         impl std::ops::BitXor for Features {
460             type Output = Features;
461 
462             #[inline]
463             fn bitxor(self, rhs: Self) -> Self::Output {
464                 self.symmetric_difference(&rhs)
465             }
466         }
467 
468         impl std::ops::BitXorAssign for Features {
469             #[inline]
470             fn bitxor_assign(&mut self, rhs: Self) {
471                 *self = self.symmetric_difference(&rhs);
472             }
473         }
474 
475         impl std::ops::Sub for Features {
476             type Output = Features;
477 
478             #[inline]
479             fn sub(self, rhs: Self) -> Self::Output {
480                 self.difference(&rhs)
481             }
482         }
483 
484         impl std::ops::SubAssign for Features {
485             #[inline]
486             fn sub_assign(&mut self, rhs: Self) {
487                 *self = self.difference(&rhs);
488             }
489         }
490 
491         impl std::fmt::Debug for Features {
492             #[allow(unused_assignments)]
493             fn fmt(&self, f: &mut std::fmt:: Formatter<'_>) -> Result<(), std::fmt::Error> {
494                 write!(f, "[")?;
495 
496                 let mut first = true;
497                 #(#debug_items)*
498 
499                 write!(f, "]")
500             }
501         }
502 
503         impl FeaturesFfi {
504             pub(crate) fn write(&mut self, features: &Features) {
505                 #(#write_items)*
506             }
507         }
508 
509         impl From<&FeaturesFfi> for Features {
510             fn from(features_ffi: &FeaturesFfi) -> Self {
511                 Features {
512                     #(#from_items)*
513                     _ne: crate::NonExhaustive(()),
514                 }
515             }
516         }
517 
518         impl IntoIterator for Features {
519             type Item = (&'static str, bool);
520             type IntoIter = std::array::IntoIter<Self::Item, #arr_len>;
521 
522             #[inline]
523             fn into_iter(self) -> Self::IntoIter {
524                 [#(#arr_items)*].into_iter()
525             }
526         }
527     }
528 }
529 
features_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<FeaturesMember>530 fn features_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<FeaturesMember> {
531     let mut features = HashMap::default();
532     std::iter::once(&types["VkPhysicalDeviceFeatures"])
533         .chain(sorted_structs(types).into_iter())
534         .filter(|(ty, _)| {
535             ty.name.as_deref() == Some("VkPhysicalDeviceFeatures")
536                 || ty.structextends.as_deref()
537                     == Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
538         })
539         .for_each(|(ty, _)| {
540             let vulkan_ty_name = ty.name.as_ref().unwrap();
541 
542             let (ty_name, optional) = if vulkan_ty_name == "VkPhysicalDeviceFeatures" {
543                 (
544                     (format_ident!("features_vulkan10"), quote! { .features }),
545                     false,
546                 )
547             } else {
548                 (
549                     (format_ident!("{}", ffi_member(vulkan_ty_name)), quote! {}),
550                     true,
551                 )
552             };
553 
554             members(ty).into_iter().for_each(|vulkan_name| {
555                 let name = vulkan_name.to_snake_case();
556                 match features.entry(name.clone()) {
557                     Entry::Vacant(entry) => {
558                         let requires_features = requires_features(vulkan_name);
559                         let conflicts_features = conflicts_features(vulkan_name);
560                         let required_by_extensions = required_by_extensions(vulkan_name);
561 
562                         let mut member = FeaturesMember {
563                             name: format_ident!("{}", name),
564                             doc: String::new(),
565                             ffi_name: format_ident!("{}", name),
566                             raw: vulkan_name.to_owned(),
567                             ffi_members: vec![ty_name.clone()],
568                             requires_features: requires_features
569                                 .iter()
570                                 .map(|&s| format_ident!("{}", s.to_snake_case()))
571                                 .collect(),
572                             conflicts_features: conflicts_features
573                                 .iter()
574                                 .map(|&s| format_ident!("{}", s.to_snake_case()))
575                                 .collect(),
576                             required_by_extensions: required_by_extensions
577                                 .iter()
578                                 .map(|(version, vk_name)| {
579                                     let version = format_ident!("{}", version);
580                                     let name = format_ident!(
581                                         "{}",
582                                         vk_name.strip_prefix("VK_").unwrap().to_snake_case()
583                                     );
584                                     (version, name)
585                                 })
586                                 .collect(),
587                             optional,
588                         };
589                         make_doc(&mut member, vulkan_ty_name);
590                         entry.insert(member);
591                     }
592                     Entry::Occupied(entry) => {
593                         entry.into_mut().ffi_members.push(ty_name.clone());
594                     }
595                 };
596             });
597         });
598 
599     let mut names: Vec<_> = features
600         .values()
601         .map(|feat| feat.name.to_string())
602         .collect();
603     names.sort_unstable();
604     names
605         .into_iter()
606         .map(|name| features.remove(&name).unwrap())
607         .collect()
608 }
609 
make_doc(feat: &mut FeaturesMember, vulkan_ty_name: &str)610 fn make_doc(feat: &mut FeaturesMember, vulkan_ty_name: &str) {
611     let writer = &mut feat.doc;
612     write!(
613         writer,
614         "- [Vulkan documentation](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/{}.html#features-{})",
615         vulkan_ty_name,
616         feat.raw
617     )
618     .unwrap();
619 
620     if !feat.requires_features.is_empty() {
621         let links: Vec<_> = feat
622             .requires_features
623             .iter()
624             .map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
625             .collect();
626         write!(
627             writer,
628             "\n- Requires feature{}: {}",
629             if feat.requires_features.len() > 1 {
630                 "s"
631             } else {
632                 ""
633             },
634             links.join(", ")
635         )
636         .unwrap();
637     }
638 
639     if !feat.required_by_extensions.is_empty() {
640         let links: Vec<_> = feat
641             .required_by_extensions
642             .iter()
643             .map(|(_, ext)| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
644             .collect();
645         write!(
646             writer,
647             "\n- Required by device extension{}: {}",
648             if feat.required_by_extensions.len() > 1 {
649                 "s"
650             } else {
651                 ""
652             },
653             links.join(", ")
654         )
655         .unwrap();
656     }
657 
658     if !feat.conflicts_features.is_empty() {
659         let links: Vec<_> = feat
660             .conflicts_features
661             .iter()
662             .map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
663             .collect();
664         write!(
665             writer,
666             "\n- Conflicts with feature{}: {}",
667             if feat.conflicts_features.len() > 1 {
668                 "s"
669             } else {
670                 ""
671             },
672             links.join(", ")
673         )
674         .unwrap();
675     }
676 }
677 
678 #[derive(Clone, Debug)]
679 struct FeaturesFfiMember {
680     name: Ident,
681     ty: Ident,
682     provided_by: Vec<TokenStream>,
683     conflicts: Vec<Ident>,
684 }
685 
features_ffi_output(members: &[FeaturesFfiMember]) -> TokenStream686 fn features_ffi_output(members: &[FeaturesFfiMember]) -> TokenStream {
687     let struct_items = members.iter().map(|FeaturesFfiMember { name, ty, .. }| {
688         quote! { #name: Option<ash::vk::#ty>, }
689     });
690 
691     let make_chain_items = members.iter().map(
692         |FeaturesFfiMember {
693              name,
694              provided_by,
695              conflicts,
696              ..
697          }| {
698             quote! {
699                 if [#(#provided_by),*].into_iter().any(|x| x) &&
700                     [#(self.#conflicts.is_none()),*].into_iter().all(|x| x) {
701                     self.#name = Some(Default::default());
702                     let member = self.#name.as_mut().unwrap();
703                     member.p_next = head.p_next;
704                     head.p_next = member as *mut _ as _;
705                 }
706             }
707         },
708     );
709 
710     quote! {
711         #[derive(Default)]
712         pub(crate) struct FeaturesFfi {
713             features_vulkan10: ash::vk::PhysicalDeviceFeatures2KHR,
714             #(#struct_items)*
715         }
716 
717         impl FeaturesFfi {
718             pub(crate) fn make_chain(
719                 &mut self,
720                 api_version: crate::Version,
721                 device_extensions: &crate::device::DeviceExtensions,
722                 _instance_extensions: &crate::instance::InstanceExtensions,
723             ) {
724                 self.features_vulkan10 = Default::default();
725                 let head = &mut self.features_vulkan10;
726                 #(#make_chain_items)*
727             }
728 
729             pub(crate) fn head_as_ref(&self) -> &ash::vk::PhysicalDeviceFeatures2KHR {
730                 &self.features_vulkan10
731             }
732 
733             pub(crate) fn head_as_mut(&mut self) -> &mut ash::vk::PhysicalDeviceFeatures2KHR {
734                 &mut self.features_vulkan10
735             }
736         }
737     }
738 }
739 
features_ffi_members<'a>( types: &'a HashMap<&str, (&Type, Vec<&str>)>, extensions: &IndexMap<&'a str, &Extension>, ) -> Vec<FeaturesFfiMember>740 fn features_ffi_members<'a>(
741     types: &'a HashMap<&str, (&Type, Vec<&str>)>,
742     extensions: &IndexMap<&'a str, &Extension>,
743 ) -> Vec<FeaturesFfiMember> {
744     let mut feature_included_in: HashMap<&str, Vec<&str>> = HashMap::default();
745     sorted_structs(types)
746         .into_iter()
747         .map(|(ty, provided_by)| {
748             let ty_name = ty.name.as_ref().unwrap();
749             let provided_by = provided_by
750                 .iter()
751                 .map(|provided_by| {
752                     if let Some(version) = provided_by.strip_prefix("VK_VERSION_") {
753                         let version = format_ident!("V{}", version);
754                         quote! { api_version >= crate::Version::#version }
755                     } else {
756                         let member = format_ident!(
757                             "{}_extensions",
758                             extensions[provided_by].ext_type.as_ref().unwrap().as_str()
759                         );
760                         let name = format_ident!(
761                             "{}",
762                             provided_by
763                                 .strip_prefix("VK_")
764                                 .unwrap()
765                                 .to_ascii_lowercase(),
766                         );
767 
768                         quote! { #member.#name }
769                     }
770                 })
771                 .collect();
772             let mut conflicts = vec![];
773             members(ty)
774                 .into_iter()
775                 .for_each(|member| match feature_included_in.entry(member) {
776                     Entry::Vacant(entry) => {
777                         entry.insert(vec![ty_name]);
778                     }
779                     Entry::Occupied(entry) => {
780                         let conflicters = entry.into_mut();
781                         conflicters.iter().for_each(|conflicter| {
782                             let conflicter = ffi_member(conflicter);
783                             if !conflicts.contains(&conflicter) {
784                                 conflicts.push(conflicter);
785                             }
786                         });
787                         conflicters.push(ty_name);
788                     }
789                 });
790 
791             FeaturesFfiMember {
792                 name: format_ident!("{}", ffi_member(ty_name)),
793                 ty: format_ident!("{}", ty_name.strip_prefix("Vk").unwrap()),
794                 provided_by,
795                 conflicts: conflicts
796                     .into_iter()
797                     .map(|s| format_ident!("{}", s))
798                     .collect(),
799             }
800         })
801         .collect()
802 }
803 
sorted_structs<'a>( types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>, ) -> Vec<&'a (&'a Type, Vec<&'a str>)>804 fn sorted_structs<'a>(
805     types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>,
806 ) -> Vec<&'a (&'a Type, Vec<&'a str>)> {
807     let mut structs: Vec<_> = types
808         .values()
809         .filter(|(ty, _)| {
810             ty.structextends.as_deref() == Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
811         })
812         .collect();
813     let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Features$").unwrap();
814     structs.sort_unstable_by_key(|&(ty, provided_by)| {
815         let name = ty.name.as_ref().unwrap();
816         (
817             !regex.is_match(name),
818             if let Some(version) = provided_by
819                 .iter()
820                 .find_map(|s| s.strip_prefix("VK_VERSION_"))
821             {
822                 let (major, minor) = version.split_once('_').unwrap();
823                 major.parse::<i32>().unwrap() << 22 | minor.parse::<i32>().unwrap() << 12
824             } else if provided_by.iter().any(|s| s.starts_with("VK_KHR_")) {
825                 i32::MAX - 2
826             } else if provided_by.iter().any(|s| s.starts_with("VK_EXT_")) {
827                 i32::MAX - 1
828             } else {
829                 i32::MAX
830             },
831             name,
832         )
833     });
834 
835     structs
836 }
837 
ffi_member(ty_name: &str) -> String838 fn ffi_member(ty_name: &str) -> String {
839     let ty_name = ty_name
840         .strip_prefix("VkPhysicalDevice")
841         .unwrap()
842         .to_snake_case();
843     let (base, suffix) = ty_name.rsplit_once("_features").unwrap();
844     format!("features_{}{}", base, suffix)
845 }
846 
members(ty: &Type) -> Vec<&str>847 fn members(ty: &Type) -> Vec<&str> {
848     if let TypeSpec::Members(members) = &ty.spec {
849         members
850             .iter()
851             .filter_map(|member| {
852                 if let TypeMember::Definition(def) = member {
853                     let name = def.markup.iter().find_map(|markup| match markup {
854                         TypeMemberMarkup::Name(name) => Some(name.as_str()),
855                         _ => None,
856                     });
857                     if name != Some("sType") && name != Some("pNext") {
858                         return name;
859                     }
860                 }
861                 None
862             })
863             .collect()
864     } else {
865         vec![]
866     }
867 }
868