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