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