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::{extensions::RequiresOneOf, write_file, IndexMap, VkRegistryData};
11 use heck::ToSnakeCase;
12 use once_cell::sync::Lazy;
13 use proc_macro2::{Ident, Literal, TokenStream};
14 use quote::{format_ident, quote};
15 use regex::Regex;
16 use vk_parse::{
17     Enum, EnumSpec, Extension, ExtensionChild, Feature, Format, FormatChild, InterfaceItem,
18 };
19 
write(vk_data: &VkRegistryData)20 pub fn write(vk_data: &VkRegistryData) {
21     write_file(
22         "formats.rs",
23         format!(
24             "vk.xml header version {}.{}.{}",
25             vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
26         ),
27         formats_output(&formats_members(
28             &vk_data.formats,
29             &vk_data.features,
30             &vk_data.extensions,
31         )),
32     );
33 }
34 
35 #[derive(Clone, Debug)]
36 struct FormatMember {
37     name: Ident,
38     ffi_name: Ident,
39     requires: Vec<RequiresOneOf>,
40 
41     aspect_color: bool,
42     aspect_depth: bool,
43     aspect_stencil: bool,
44     aspect_plane0: bool,
45     aspect_plane1: bool,
46     aspect_plane2: bool,
47 
48     block_extent: [u32; 3],
49     block_size: Option<u64>,
50     compatibility: Ident,
51     components: [u8; 4],
52     compression: Option<Ident>,
53     planes: Vec<Ident>,
54     texels_per_block: u8,
55     type_color: Option<Ident>,
56     type_depth: Option<Ident>,
57     type_stencil: Option<Ident>,
58     ycbcr_chroma_sampling: Option<Ident>,
59 
60     type_std_array: Option<TokenStream>,
61     type_cgmath: Option<TokenStream>,
62     type_nalgebra: Option<TokenStream>,
63 }
64 
formats_output(members: &[FormatMember]) -> TokenStream65 fn formats_output(members: &[FormatMember]) -> TokenStream {
66     let enum_items = members.iter().map(|FormatMember { name, ffi_name, .. }| {
67         quote! { #name = ash::vk::Format::#ffi_name.as_raw(), }
68     });
69     let aspects_items = members.iter().map(
70         |FormatMember {
71              name,
72              aspect_color,
73              aspect_depth,
74              aspect_stencil,
75              aspect_plane0,
76              aspect_plane1,
77              aspect_plane2,
78              ..
79          }| {
80             let aspect_items = [
81                 aspect_color.then(|| quote! { crate::image::ImageAspects::COLOR }),
82                 aspect_depth.then(|| quote! { crate::image::ImageAspects::DEPTH }),
83                 aspect_stencil.then(|| quote! { crate::image::ImageAspects::STENCIL }),
84                 aspect_plane0.then(|| quote! { crate::image::ImageAspects::PLANE_0 }),
85                 aspect_plane1.then(|| quote! { crate::image::ImageAspects::PLANE_1 }),
86                 aspect_plane2.then(|| quote! { crate::image::ImageAspects::PLANE_2 }),
87             ]
88             .into_iter()
89             .flatten();
90 
91             quote! {
92                 Self::#name => #(#aspect_items)|*,
93             }
94         },
95     );
96     let block_extent_items = members.iter().filter_map(
97         |FormatMember {
98              name,
99              block_extent: [x, y, z],
100              ..
101          }| {
102             if *x == 1 && *y == 1 && *z == 1 {
103                 None
104             } else {
105                 let x = Literal::u32_unsuffixed(*x);
106                 let y = Literal::u32_unsuffixed(*y);
107                 let z = Literal::u32_unsuffixed(*z);
108                 Some(quote! { Self::#name => [#x, #y, #z], })
109             }
110         },
111     );
112     let block_size_items = members.iter().filter_map(
113         |FormatMember {
114              name, block_size, ..
115          }| {
116             block_size.as_ref().map(|size| {
117                 let size = Literal::u64_unsuffixed(*size);
118                 quote! { Self::#name => Some(#size), }
119             })
120         },
121     );
122     let compatibility_items = members.iter().map(
123         |FormatMember {
124              name,
125              compatibility,
126              ..
127          }| {
128             quote! {
129                 Self::#name => &FormatCompatibilityInner::#compatibility,
130             }
131         },
132     );
133     let components_items = members.iter().map(
134         |FormatMember {
135              name, components, ..
136          }| {
137             let components = components.iter().map(|c| Literal::u8_unsuffixed(*c));
138             quote! {
139                 Self::#name => [#(#components),*],
140             }
141         },
142     );
143     let compression_items = members.iter().filter_map(
144         |FormatMember {
145              name, compression, ..
146          }| {
147             compression
148                 .as_ref()
149                 .map(|x| quote! { Self::#name => Some(CompressionType::#x), })
150         },
151     );
152     let planes_items = members
153         .iter()
154         .filter_map(|FormatMember { name, planes, .. }| {
155             if planes.is_empty() {
156                 None
157             } else {
158                 Some(quote! { Self::#name => &[#(Self::#planes),*], })
159             }
160         });
161     let texels_per_block_items = members.iter().filter_map(
162         |FormatMember {
163              name,
164              texels_per_block,
165              ..
166          }| {
167             (*texels_per_block != 1).then(|| {
168                 let texels_per_block = Literal::u8_unsuffixed(*texels_per_block);
169                 quote! { Self::#name => #texels_per_block, }
170             })
171         },
172     );
173     let type_color_items = members.iter().filter_map(
174         |FormatMember {
175              name, type_color, ..
176          }| {
177             type_color
178                 .as_ref()
179                 .map(|ty| quote! { Self::#name => Some(NumericType::#ty), })
180         },
181     );
182     let type_depth_items = members.iter().filter_map(
183         |FormatMember {
184              name, type_depth, ..
185          }| {
186             type_depth
187                 .as_ref()
188                 .map(|ty| quote! { Self::#name => Some(NumericType::#ty), })
189         },
190     );
191     let type_stencil_items = members.iter().filter_map(
192         |FormatMember {
193              name, type_stencil, ..
194          }| {
195             type_stencil
196                 .as_ref()
197                 .map(|ty| quote! { Self::#name => Some(NumericType::#ty), })
198         },
199     );
200     let ycbcr_chroma_sampling_items = members.iter().filter_map(
201         |FormatMember {
202              name,
203              ycbcr_chroma_sampling,
204              ..
205          }| {
206             ycbcr_chroma_sampling
207                 .as_ref()
208                 .map(|ty| quote! { Self::#name => Some(ChromaSampling::#ty), })
209         },
210     );
211     let try_from_items = members.iter().map(|FormatMember { name, ffi_name, .. }| {
212         quote! { ash::vk::Format::#ffi_name => Ok(Self::#name), }
213     });
214 
215     let type_for_format_items = members.iter().filter_map(
216         |FormatMember {
217              name,
218              type_std_array,
219              ..
220          }| {
221             type_std_array.as_ref().map(|ty| {
222                 quote! { (#name) => { #ty }; }
223             })
224         },
225     );
226     let type_for_format_cgmath_items = members.iter().filter_map(
227         |FormatMember {
228              name,
229              type_std_array,
230              type_cgmath,
231              ..
232          }| {
233             (type_cgmath.as_ref().or(type_std_array.as_ref())).map(|ty| {
234                 quote! { (cgmath, #name) => { #ty }; }
235             })
236         },
237     );
238     let type_for_format_nalgebra_items = members.iter().filter_map(
239         |FormatMember {
240              name,
241              type_std_array,
242              type_nalgebra,
243              ..
244          }| {
245             (type_nalgebra.as_ref().or(type_std_array.as_ref())).map(|ty| {
246                 quote! { (nalgebra, #name) => { #ty }; }
247             })
248         },
249     );
250 
251     let validate_device_items = members.iter().map(|FormatMember { name, requires, .. }| {
252         let requires_items = requires.iter().map(
253             |RequiresOneOf {
254                  api_version,
255                  device_extensions,
256                  instance_extensions,
257              }| {
258                 let condition_items = (api_version.iter().map(|(major, minor)| {
259                     let version = format_ident!("V{}_{}", major, minor);
260                     quote! { device.api_version() >= crate::Version::#version }
261                 }))
262                 .chain(device_extensions.iter().map(|ext| {
263                     quote! { device.enabled_extensions().#ext }
264                 }))
265                 .chain(instance_extensions.iter().map(|ext| {
266                     quote! { device.instance().enabled_extensions().#ext }
267                 }));
268                 let required_for = format!("`Format::{}`", name);
269                 let requires_one_of_items = (api_version.iter().map(|(major, minor)| {
270                     let version = format_ident!("V{}_{}", major, minor);
271                     quote! { api_version: Some(crate::Version::#version), }
272                 }))
273                 .chain((!device_extensions.is_empty()).then(|| {
274                     let items = device_extensions.iter().map(|ext| ext.to_string());
275                     quote! { device_extensions: &[#(#items),*], }
276                 }))
277                 .chain((!instance_extensions.is_empty()).then(|| {
278                     let items = instance_extensions.iter().map(|ext| ext.to_string());
279                     quote! { instance_extensions: &[#(#items),*], }
280                 }));
281 
282                 quote! {
283                     if !(#(#condition_items)||*) {
284                         return Err(crate::RequirementNotMet {
285                             required_for: #required_for,
286                             requires_one_of: crate::RequiresOneOf {
287                                 #(#requires_one_of_items)*
288                                 ..Default::default()
289                             },
290                         });
291                     }
292                 }
293             },
294         );
295 
296         quote! {
297             Self::#name => {
298                 #(#requires_items)*
299             }
300         }
301     });
302     let validate_physical_device_items =
303         members.iter().map(|FormatMember { name, requires, .. }| {
304             let requires_items = requires.iter().map(
305                 |RequiresOneOf {
306                      api_version,
307                      device_extensions,
308                      instance_extensions,
309                  }| {
310                     let condition_items = (api_version.iter().map(|(major, minor)| {
311                         let version = format_ident!("V{}_{}", major, minor);
312                         quote! { physical_device.api_version() >= crate::Version::#version }
313                     }))
314                     .chain(device_extensions.iter().map(|ext| {
315                         quote! { physical_device.supported_extensions().#ext }
316                     }))
317                     .chain(instance_extensions.iter().map(|ext| {
318                         quote! { physical_device.instance().enabled_extensions().#ext }
319                     }));
320                     let required_for = format!("`Format::{}`", name);
321                     let requires_one_of_items = (api_version.iter().map(|(major, minor)| {
322                         let version = format_ident!("V{}_{}", major, minor);
323                         quote! { api_version: Some(crate::Version::#version), }
324                     }))
325                     .chain((!device_extensions.is_empty()).then(|| {
326                         let items = device_extensions.iter().map(|ext| ext.to_string());
327                         quote! { device_extensions: &[#(#items),*], }
328                     }))
329                     .chain((!instance_extensions.is_empty()).then(|| {
330                         let items = instance_extensions.iter().map(|ext| ext.to_string());
331                         quote! { instance_extensions: &[#(#items),*], }
332                     }));
333 
334                     quote! {
335                         if !(#(#condition_items)||*) {
336                             return Err(crate::RequirementNotMet {
337                                 required_for: #required_for,
338                                 requires_one_of: crate::RequiresOneOf {
339                                     #(#requires_one_of_items)*
340                                     ..Default::default()
341                                 },
342                             });
343                         }
344                     }
345                 },
346             );
347 
348             quote! {
349                 Self::#name => {
350                     #(#requires_items)*
351                 }
352             }
353         });
354 
355     quote! {
356         /// An enumeration of all the possible formats.
357         #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
358         #[repr(i32)]
359         #[allow(non_camel_case_types)]
360         #[non_exhaustive]
361         pub enum Format {
362             #(#enum_items)*
363         }
364 
365         impl Format {
366             /// Returns the aspects that images of this format have.
367             pub fn aspects(self) -> ImageAspects {
368                 match self {
369                     #(#aspects_items)*
370                 }
371             }
372 
373             /// Returns the extent in texels (horizontally and vertically) of a single texel
374             /// block of this format. A texel block is a rectangle of pixels that is represented by
375             /// a single element of this format. It is also the minimum granularity of the extent of
376             /// an image; images must always have an extent that's a multiple of the block extent.
377             ///
378             /// For normal formats, the block extent is [1, 1, 1], meaning that each element of the
379             /// format represents one texel. Block-compressed formats encode multiple texels into
380             /// a single element. The 422 and 420 YCbCr formats have a block extent of [2, 1, 1] and
381             /// [2, 2, 1] respectively, as the red and blue components are shared across multiple
382             /// texels.
383             pub fn block_extent(self) -> [u32; 3] {
384                 match self {
385                     #(#block_extent_items)*
386                     _ => [1, 1, 1],
387                 }
388             }
389 
390             /// Returns the size in bytes of a single texel block of this format. Returns `None`
391             /// if the texel block size is not well-defined for this format.
392             ///
393             /// For regular formats, this is the size of a single texel, but for more specialized
394             /// formats this may be the size of multiple texels.
395             ///
396             /// Depth/stencil formats are considered to have an opaque memory representation, and do
397             /// not have a well-defined size. Multi-planar formats store the color components
398             /// disjointly in memory, and therefore do not have a well-defined size for all
399             /// components as a whole. The individual planes do have a well-defined size.
400             pub fn block_size(self) -> Option<DeviceSize> {
401                 match self {
402                     #(#block_size_items)*
403                     _ => None,
404                 }
405             }
406 
407             /// Returns the an opaque object representing the compatibility class of the format.
408             /// This can be used to determine whether two formats are compatible for the purposes
409             /// of certain Vulkan operations, such as image copying.
410             pub fn compatibility(self) -> FormatCompatibility {
411                 FormatCompatibility(match self {
412                     #(#compatibility_items)*
413                 })
414             }
415 
416             /// Returns the number of bits per texel block that each component (R, G, B, A) is
417             /// represented with. Components that are not present in the format have 0 bits.
418             ///
419             /// For depth/stencil formats, the depth component is the first, stencil the second. For
420             /// multi-planar formats, this is the number of bits across all planes.
421             ///
422             /// For block-compressed formats, the number of bits in individual components is not
423             /// well-defined, and the return value is merely binary: 1 indicates a component
424             /// that is present in the format, 0 indicates one that is absent.
425             pub fn components(self) -> [u8; 4] {
426                 match self {
427                     #(#components_items)*
428                 }
429             }
430 
431             /// Returns the block compression scheme used for this format, if any. Returns `None` if
432             /// the format does not use compression.
433             pub fn compression(self) -> Option<CompressionType> {
434                 match self {
435                     #(#compression_items)*
436                     _ => None,
437                 }
438             }
439 
440             /// For multi-planar formats, returns a slice of length 2 or 3, containing the
441             /// equivalent regular format of each plane.
442             ///
443             /// For non-planar formats, returns the empty slice.
444             pub fn planes(self) -> &'static [Self] {
445                 match self {
446                     #(#planes_items)*
447                     _ => &[],
448                 }
449             }
450 
451             /// Returns the number of texels for a single texel block. For most formats, this is
452             /// the product of the `block_extent` elements, but for some it differs.
453             pub fn texels_per_block(self) -> u8 {
454                 match self {
455                     #(#texels_per_block_items)*
456                     _ => 1,
457                 }
458             }
459 
460             /// Returns the numeric data type of the color aspect of this format. Returns `None`
461             /// for depth/stencil formats.
462             pub fn type_color(self) -> Option<NumericType> {
463                 match self {
464                     #(#type_color_items)*
465                     _ => None,
466                 }
467             }
468 
469             /// Returns the numeric data type of the depth aspect of this format. Returns `None`
470             /// color and stencil-only formats.
471             pub fn type_depth(self) -> Option<NumericType> {
472                 match self {
473                     #(#type_depth_items)*
474                     _ => None,
475                 }
476             }
477 
478             /// Returns the numeric data type of the stencil aspect of this format. Returns `None`
479             /// for color and depth-only formats.
480             pub fn type_stencil(self) -> Option<NumericType> {
481                 match self {
482                     #(#type_stencil_items)*
483                     _ => None,
484                 }
485             }
486 
487             /// For YCbCr (YUV) formats, returns the way in which the chroma components are
488             /// represented. Returns `None` for non-YCbCr formats.
489             ///
490             /// If an image view is created for one of the formats for which this function returns
491             /// `Some`, with the `color` aspect selected, then the view and any samplers that sample
492             /// it must be created with an attached sampler YCbCr conversion object.
493             pub fn ycbcr_chroma_sampling(self) -> Option<ChromaSampling> {
494                 match self {
495                     #(#ycbcr_chroma_sampling_items)*
496                     _ => None,
497                 }
498             }
499 
500             #[allow(dead_code)]
501             pub(crate) fn validate_device(
502                 self,
503                 #[allow(unused_variables)] device: &crate::device::Device,
504             ) -> Result<(), crate::RequirementNotMet> {
505                 match self {
506                     #(#validate_device_items)*
507                 }
508 
509                 Ok(())
510             }
511 
512             #[allow(dead_code)]
513             pub(crate) fn validate_physical_device(
514                 self,
515                 #[allow(unused_variables)] physical_device: &crate::device::physical::PhysicalDevice,
516             ) -> Result<(), crate::RequirementNotMet> {
517                 match self {
518                     #(#validate_physical_device_items)*
519                 }
520 
521                 Ok(())
522             }
523         }
524 
525         impl TryFrom<ash::vk::Format> for Format {
526             type Error = ();
527 
528             fn try_from(val: ash::vk::Format) -> Result<Format, ()> {
529                 match val {
530                     #(#try_from_items)*
531                     _ => Err(()),
532                 }
533             }
534         }
535 
536         /// Converts a format enum identifier to a standard Rust type that is suitable for
537         /// representing the format in a buffer or image.
538         ///
539         /// This macro returns one possible suitable representation, but there are usually other
540         /// possibilities for a given format. A compile error occurs for formats that have no
541         /// well-defined size (the `size` method returns `None`).
542         ///
543         /// - For regular unpacked formats with one component, this returns a single floating point,
544         ///   signed or unsigned integer with the appropriate number of bits. For formats with
545         ///   multiple components, an array is returned.
546         /// - For packed formats, this returns an unsigned integer with the size of the packed
547         ///   element. For multi-packed formats (such as `2PACK16`), an array is returned.
548         /// - For compressed formats, this returns `[u8; N]` where N is the size of a block.
549         ///
550         /// Note: for 16-bit floating point values, you need to import the [`half::f16`] type.
551         ///
552         /// # Examples
553         ///
554         /// For arrays:
555         ///
556         /// ```
557         /// # use vulkano::type_for_format;
558         /// let pixel: type_for_format!(R32G32B32A32_SFLOAT);
559         /// pixel = [1.0f32, 0.0, 0.0, 1.0];
560         /// ```
561         ///
562         /// For [`cgmath`]:
563         ///
564         /// ```
565         /// # use vulkano::type_for_format;
566         /// let pixel: type_for_format!(cgmath, R32G32B32A32_SFLOAT);
567         /// pixel = cgmath::Vector4::new(1.0f32, 0.0, 0.0, 1.0);
568         /// ```
569         ///
570         /// For [`nalgebra`]:
571         ///
572         /// ```
573         /// # use vulkano::type_for_format;
574         /// let pixel: type_for_format!(nalgebra, R32G32B32A32_SFLOAT);
575         /// pixel = nalgebra::vector![1.0f32, 0.0, 0.0, 1.0];
576         /// ```
577         ///
578         /// [`cgmath`]: https://crates.io/crates/cgmath
579         /// [`nalgebra`]: https://crates.io/crates/nalgebra
580         #[macro_export]
581         macro_rules! type_for_format {
582             #(#type_for_format_items)*
583             #(#type_for_format_cgmath_items)*
584             #(#type_for_format_nalgebra_items)*
585         }
586     }
587 }
588 
formats_members( formats: &[&Format], features: &IndexMap<&str, &Feature>, extensions: &IndexMap<&str, &Extension>, ) -> Vec<FormatMember>589 fn formats_members(
590     formats: &[&Format],
591     features: &IndexMap<&str, &Feature>,
592     extensions: &IndexMap<&str, &Extension>,
593 ) -> Vec<FormatMember> {
594     static BLOCK_EXTENT_REGEX: Lazy<Regex> =
595         Lazy::new(|| Regex::new(r"^(\d+),(\d+),(\d+)$").unwrap());
596 
597     formats
598         .iter()
599         .map(|format| {
600             let vulkan_name = format.name.strip_prefix("VK_FORMAT_").unwrap();
601             let ffi_name = format_ident!("{}", vulkan_name.to_ascii_uppercase());
602 
603             let mut parts = vulkan_name.split('_').collect::<Vec<_>>();
604 
605             if ["EXT", "IMG"].contains(parts.last().unwrap()) {
606                 parts.pop();
607             }
608 
609             let name = format_ident!("{}", parts.join("_"));
610 
611             let mut member = FormatMember {
612                 name,
613                 ffi_name,
614                 requires: Vec::new(),
615 
616                 aspect_color: false,
617                 aspect_depth: false,
618                 aspect_stencil: false,
619                 aspect_plane0: false,
620                 aspect_plane1: false,
621                 aspect_plane2: false,
622 
623                 block_extent: [1, 1, 1],
624                 block_size: None,
625                 compatibility: format_ident!(
626                     "Class_{}",
627                     format.class.replace('-', "").replace(' ', "_")
628                 ),
629                 components: [0u8; 4],
630                 compression: format
631                     .compressed
632                     .as_ref()
633                     .map(|c| format_ident!("{}", c.replace(' ', "_"))),
634                 planes: vec![],
635                 texels_per_block: format.texelsPerBlock,
636                 type_color: None,
637                 type_depth: None,
638                 type_stencil: None,
639                 ycbcr_chroma_sampling: None,
640 
641                 type_std_array: None,
642                 type_cgmath: None,
643                 type_nalgebra: None,
644             };
645 
646             for child in &format.children {
647                 match child {
648                     FormatChild::Component {
649                         name,
650                         bits,
651                         numericFormat,
652                         ..
653                     } => {
654                         let bits = if bits == "compressed" {
655                             1u8
656                         } else {
657                             bits.parse().unwrap()
658                         };
659                         let ty = format_ident!("{}", numericFormat);
660 
661                         match name.as_str() {
662                             "R" => {
663                                 member.aspect_color = true;
664                                 member.components[0] += bits;
665                                 member.type_color = Some(ty);
666                             }
667                             "G" => {
668                                 member.aspect_color = true;
669                                 member.components[1] += bits;
670                                 member.type_color = Some(ty);
671                             }
672                             "B" => {
673                                 member.aspect_color = true;
674                                 member.components[2] += bits;
675                                 member.type_color = Some(ty);
676                             }
677                             "A" => {
678                                 member.aspect_color = true;
679                                 member.components[3] += bits;
680                                 member.type_color = Some(ty);
681                             }
682                             "D" => {
683                                 member.aspect_depth = true;
684                                 member.components[0] += bits;
685                                 member.type_depth = Some(ty);
686                             }
687                             "S" => {
688                                 member.aspect_stencil = true;
689                                 member.components[1] += bits;
690                                 member.type_stencil = Some(ty);
691                             }
692                             _ => {
693                                 panic!("Unknown component type {} on format {}", name, format.name)
694                             }
695                         }
696                     }
697                     FormatChild::Plane {
698                         index, compatible, ..
699                     } => {
700                         match *index {
701                             0 => member.aspect_plane0 = true,
702                             1 => member.aspect_plane1 = true,
703                             2 => member.aspect_plane2 = true,
704                             _ => (),
705                         }
706 
707                         assert_eq!(*index as usize, member.planes.len());
708                         member.planes.push(format_ident!(
709                             "{}",
710                             compatible.strip_prefix("VK_FORMAT_").unwrap()
711                         ));
712                     }
713                     //FormatChild::SpirvImageFormat { name, .. } => (),
714                     _ => (),
715                 }
716             }
717 
718             if let Some(block_extent) = format.blockExtent.as_ref() {
719                 let captures = BLOCK_EXTENT_REGEX.captures(block_extent).unwrap();
720                 member.block_extent = [
721                     captures.get(1).unwrap().as_str().parse().unwrap(),
722                     captures.get(2).unwrap().as_str().parse().unwrap(),
723                     captures.get(3).unwrap().as_str().parse().unwrap(),
724                 ];
725             } else {
726                 match format.chroma.as_deref() {
727                     Some("420") => member.block_extent = [2, 2, 1],
728                     Some("422") => member.block_extent = [2, 1, 1],
729                     _ => (),
730                 }
731             };
732 
733             // Depth-stencil and multi-planar formats don't have well-defined block sizes.
734             if let (Some(numeric_type), true) = (&member.type_color, member.planes.is_empty()) {
735                 member.block_size = Some(format.blockSize as u64);
736 
737                 if format.compressed.is_some() {
738                     member.type_std_array = Some({
739                         let block_size = Literal::usize_unsuffixed(format.blockSize as usize);
740                         quote! { [u8; #block_size] }
741                     });
742                 } else if let Some(pack_bits) = format.packed {
743                     let pack_elements = format.blockSize * 8 / pack_bits;
744                     let element_type = format_ident!("u{}", pack_bits);
745 
746                     member.type_std_array = Some(if pack_elements > 1 {
747                         let elements = Literal::usize_unsuffixed(pack_elements as usize);
748                         quote! { [#element_type; #elements] }
749                     } else {
750                         quote! { #element_type }
751                     });
752                 } else {
753                     let prefix = match numeric_type.to_string().as_str() {
754                         "SFLOAT" => "f",
755                         "SINT" | "SNORM" | "SSCALED" => "i",
756                         "UINT" | "UNORM" | "USCALED" | "SRGB" => "u",
757                         _ => unreachable!(),
758                     };
759                     let bits = member.components[0];
760                     let component_type = format_ident!("{}{}", prefix, bits);
761 
762                     let component_count = if member.components[1] == 2 * bits {
763                         // 422 format with repeated G component
764                         4
765                     } else {
766                         // Normal format
767                         member
768                             .components
769                             .into_iter()
770                             .filter(|&c| {
771                                 if c != 0 {
772                                     debug_assert!(c == bits);
773                                     true
774                                 } else {
775                                     false
776                                 }
777                             })
778                             .count()
779                     };
780 
781                     if component_count > 1 {
782                         let elements = Literal::usize_unsuffixed(component_count);
783                         member.type_std_array = Some(quote! { [#component_type; #elements] });
784 
785                         // cgmath only has 1, 2, 3 and 4-component vector types.
786                         // Fall back to arrays for anything else.
787                         if matches!(component_count, 1 | 2 | 3 | 4) {
788                             let ty = format_ident!("{}", format!("Vector{}", component_count));
789                             member.type_cgmath = Some(quote! { cgmath::#ty<#component_type> });
790                         }
791 
792                         member.type_nalgebra = Some(quote! {
793                             nalgebra::base::SVector<#component_type, #component_count>
794                         });
795                     } else {
796                         member.type_std_array = Some(quote! { #component_type });
797                     }
798                 }
799             }
800 
801             if let Some(chroma) = format.chroma.as_ref() {
802                 member.ycbcr_chroma_sampling = Some(format_ident!("Mode{}", chroma));
803             }
804 
805             debug_assert!(
806                 !member.components.iter().all(|x| *x == 0),
807                 "format {} has 0 components",
808                 vulkan_name
809             );
810 
811             for &feature in features.values() {
812                 for child in &feature.children {
813                     if let ExtensionChild::Require { items, .. } = child {
814                         for item in items {
815                             match item {
816                                 InterfaceItem::Enum(Enum {
817                                     name,
818                                     spec: EnumSpec::Offset { extends, .. },
819                                     ..
820                                 }) if name == &format.name && extends == "VkFormat" => {
821                                     if let Some(version) = feature.name.strip_prefix("VK_VERSION_")
822                                     {
823                                         let (major, minor) = version.split_once('_').unwrap();
824                                         member.requires.push(RequiresOneOf {
825                                             api_version: Some((
826                                                 major.to_string(),
827                                                 minor.to_string(),
828                                             )),
829                                             ..Default::default()
830                                         });
831                                     }
832                                 }
833                                 _ => (),
834                             }
835                         }
836                     }
837                 }
838             }
839 
840             for &extension in extensions.values() {
841                 for child in &extension.children {
842                     if let ExtensionChild::Require { items, .. } = child {
843                         for item in items {
844                             if let InterfaceItem::Enum(en) = item {
845                                 if matches!(
846                                     en,
847                                     Enum {
848                                         name,
849                                         spec: EnumSpec::Offset { extends, .. },
850                                         ..
851                                     } if name == &format.name && extends == "VkFormat")
852                                     || matches!(
853                                         en,
854                                         Enum {
855                                             spec: EnumSpec::Alias { alias, extends, .. },
856                                             ..
857                                         } if alias == &format.name && extends.as_deref() == Some("VkFormat"))
858                                 {
859                                     let extension_name =
860                                         extension.name.strip_prefix("VK_").unwrap().to_snake_case();
861 
862                                     if member.requires.is_empty() {
863                                         member.requires.push(Default::default());
864                                     };
865 
866                                     let requires = member.requires.first_mut().unwrap();
867 
868                                     match extension.ext_type.as_deref() {
869                                         Some("device") => {
870                                             requires
871                                                 .device_extensions
872                                                 .push(format_ident!("{}", extension_name));
873                                         }
874                                         Some("instance") => {
875                                             requires
876                                                 .instance_extensions
877                                                 .push(format_ident!("{}", extension_name));
878                                         }
879                                         _ => (),
880                                     }
881                                 }
882                             }
883                         }
884                     }
885                 }
886             }
887 
888             member
889         })
890         .collect()
891 }
892