File: format_utils_generator.py

package info (click to toggle)
vulkan-utility-libraries 1.4.335.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,476 kB
  • sloc: cpp: 106,816; ansic: 16,788; python: 2,218; sh: 23; makefile: 6
file content (683 lines) | stat: -rw-r--r-- 33,286 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
#!/usr/bin/python3 -i
#
# Copyright 2023-2025 The Khronos Group Inc.
# Copyright 2023-2025 Valve Corporation
# Copyright 2023-2025 LunarG, Inc.
#
# SPDX-License-Identifier: Apache-2.0

import os
from collections import namedtuple
from vulkan_object import (Format)
from base_generator import BaseGenerator

# Make C name friendly class name
def getClassName(className: str) -> str:
    return className.replace('-', '').replace(' ', '_').upper()

def formatHasDepth(format: Format) -> bool:
    return any(x.type == 'D' for x in format.components)

def formatHasStencil(format: Format) -> bool:
    return any(x.type == 'S' for x in format.components)

def formatHasEqualBitsize(format: Format, bitsize: str) -> bool:
    return all(x.bits == bitsize for x in format.components)

# True if all components are same numericFormat
def formatHasNumericFormat(format: Format, numericFormat: str) -> bool:
    if numericFormat == 'SRGB':
        # For SRGB, the Alpha will be UNORM, but it is still considered an SRGB format
        if format.name == 'VK_FORMAT_A8_UNORM':
            return False
        return all(x.type == 'A' or x.numericFormat == numericFormat for x in format.components)
    else:
        return all(x.numericFormat == numericFormat for x in format.components)

class FormatUtilsOutputGenerator(BaseGenerator):
    def __init__(self):
        BaseGenerator.__init__(self)

        self.maxPlaneCount = 1
        self.maxComponentCount = 1

        self.compressedFormats = dict()
        self.depthFormats = dict()
        self.stencilFormats = dict()
        self.numericFormats = set()

        # Lots of switch statements share same ending
        self.commonBoolSwitch  = '''            return true;
        default:
            return false;
    }
}
'''

    #
    # Called at beginning of processing as file is opened
    def generate(self):
        self.maxPlaneCount = max([len(format.planes) for format in self.vk.formats.values()])
        self.maxComponentCount = max([len(format.components) for format in self.vk.formats.values()])

        for format in [x for x in self.vk.formats.values() if x.compressed]:
            compressed = format.compressed.replace(' ', '_')
            if compressed not in self.compressedFormats:
                # create list if first time
                self.compressedFormats[compressed] = []
            self.compressedFormats[compressed].append(format.name)

        for format in self.vk.formats.values():
            for component in format.components:
                if component.type == 'D':
                    self.depthFormats[format.name] = component
                elif component.type == 'S':
                    self.stencilFormats[format.name] = component
                self.numericFormats.add(component.numericFormat)

        out = []
        out.append(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications
// Copyright 2023-2025 The Khronos Group Inc.
// Copyright 2023-2025 Valve Corporation
// Copyright 2023-2025 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0
''')

        out.append('''
#pragma once

// clang-format off

#ifdef __cplusplus
extern "C" {
#endif

#include <vulkan/vulkan.h>

#include <stdbool.h>

''')
        out.append('#define VKU_FORMAT_INVALID_INDEX 0xFFFFFFFF\n')
        out.append(f'#define VKU_FORMAT_MAX_PLANES {self.maxPlaneCount}\n')
        out.append(f'#define VKU_FORMAT_MAX_COMPONENTS {self.maxComponentCount}\n')
        out.append('\n')
        out.append('enum VKU_FORMAT_NUMERICAL_TYPE {\n')
        out.append('    VKU_FORMAT_NUMERICAL_TYPE_NONE = 0,\n')
        for index, numericFormat in enumerate(sorted(self.numericFormats), start=1):
            out.append(f'    VKU_FORMAT_NUMERICAL_TYPE_{numericFormat}')
            out.append(',\n')
        out.append('};\n')
        out.append('\n')
        out.append('enum VKU_FORMAT_COMPATIBILITY_CLASS {\n')
        out.append('    VKU_FORMAT_COMPATIBILITY_CLASS_NONE = 0,\n')

        classNames = set()
        for f in self.vk.formats.values():
            classNames.add(getClassName(f.className))

        for className in sorted(classNames):
            out.append(f'    VKU_FORMAT_COMPATIBILITY_CLASS_{className}')
            out.append(',\n')
        out.append('};\n')

        out.append('// Return the plane index of a given VkImageAspectFlagBits.\n')
        out.append('//     VK_IMAGE_ASPECT_PLANE_0_BIT -> 0\n')
        out.append('//     VK_IMAGE_ASPECT_PLANE_1_BIT -> 1\n')
        out.append('//     VK_IMAGE_ASPECT_PLANE_2_BIT -> 2\n')
        out.append('//     <any other value> -> VKU_FORMAT_INVALID_INDEX\n')
        out.append('static inline uint32_t vkuGetPlaneIndex(VkImageAspectFlagBits aspect);\n\n')

        for numericFormat in sorted(self.numericFormats):
            out.append(f'// Returns whether a VkFormat is of the numerical format {numericFormat}\n')
            out.append('// Format must only contain one numerical format, so formats like D16_UNORM_S8_UINT always return false\n')
            out.append(f'static inline bool vkuFormatIs{numericFormat}(VkFormat format);\n\n')

        out.append('''// Returns whether the type of a VkFormat is a OpTypeInt (SPIR-V) from "Interpretation of Numeric Format" table
static inline bool vkuFormatIsSampledInt(VkFormat format);

// Returns whether the type of a VkFormat is a OpTypeFloat (SPIR-V) from "Interpretation of Numeric Format" table
static inline bool vkuFormatIsSampledFloat(VkFormat format);

''')

        for key in sorted(self.compressedFormats.keys()):
            out.append(f'// Returns whether a VkFormat is a compressed format of type {key}\n')
            out.append(f'static inline bool vkuFormatIsCompressed_{key}(VkFormat format);\n\n')
        out.append('// Returns whether a VkFormat is of any compressed format type\n')
        out.append('static inline bool vkuFormatIsCompressed(VkFormat format);\n')

        out.append('''
// Returns whether a VkFormat is either a depth or stencil format
static inline bool vkuFormatIsDepthOrStencil(VkFormat format);

// Returns whether a VkFormat is a depth and stencil format
static inline bool vkuFormatIsDepthAndStencil(VkFormat format);

// Returns whether a VkFormat is a depth only format
static inline bool vkuFormatIsDepthOnly(VkFormat format);

// Returns whether a VkFormat is a stencil only format
static inline bool vkuFormatIsStencilOnly(VkFormat format);

// Returns whether a VkFormat has a depth component
static inline bool vkuFormatHasDepth(VkFormat format) { return (vkuFormatIsDepthOnly(format) || vkuFormatIsDepthAndStencil(format)); }

// Returns whether a VkFormat has a stencil component
static inline bool vkuFormatHasStencil(VkFormat format) { return (vkuFormatIsStencilOnly(format) || vkuFormatIsDepthAndStencil(format)); }

// Returns the size of the depth component in bits if it has one. Otherwise it returns 0
static inline uint32_t vkuFormatDepthSize(VkFormat format);

// Returns the size of the stencil component in bits if it has one. Otherwise it returns 0
static inline uint32_t vkuFormatStencilSize(VkFormat format);

// Returns the numerical type of the depth component if it has one.  Otherwise it returns VKU_FORMAT_NUMERICAL_TYPE_NONE
static inline enum VKU_FORMAT_NUMERICAL_TYPE vkuFormatDepthNumericalType(VkFormat format);

// Returns the numerical type of the stencil component if it has one.  Otherwise it returns VKU_FORMAT_NUMERICAL_TYPE_NONE
static inline enum VKU_FORMAT_NUMERICAL_TYPE vkuFormatStencilNumericalType(VkFormat format);

// Returns whether a VkFormat is packed
static inline bool vkuFormatIsPacked(VkFormat format);

// Returns whether a VkFormat is YCbCr
// This corresponds to formats with _444, _422, or _420 in their name
static inline bool vkuFormatRequiresYcbcrConversion(VkFormat format);

// Returns whether a VkFormat is XChromaSubsampled
// This corresponds to formats with _422 or 420 in their name
static inline bool vkuFormatIsXChromaSubsampled(VkFormat format);

// Returns whether a VkFormat is YChromaSubsampled
// This corresponds to formats with _420 in their name
static inline bool vkuFormatIsYChromaSubsampled(VkFormat format);

// Returns whether a VkFormat is Multiplane
// Single-plane "_422" formats are treated as 2x1 compressed (for copies)
static inline bool vkuFormatIsSinglePlane_422(VkFormat format);

// Returns number of planes in format (which is 1 by default)
static inline uint32_t vkuFormatPlaneCount(VkFormat format);

// Returns whether a VkFormat is multiplane
// Note - Formats like VK_FORMAT_G8B8G8R8_422_UNORM are NOT multi-planar, they require a
//        VkSamplerYcbcrConversion and you should use vkuFormatRequiresYcbcrConversion instead
static inline bool vkuFormatIsMultiplane(VkFormat format) { return ((vkuFormatPlaneCount(format)) > 1u); }

// Returns a VkFormat that is compatible with a given plane of a multiplane format
// Will return VK_FORMAT_UNDEFINED if given a plane aspect that doesn't exist for the format
static inline VkFormat vkuFindMultiplaneCompatibleFormat(VkFormat mp_fmt, VkImageAspectFlagBits plane_aspect);

// Returns the extent divisors of a multiplane format given a plane
// Will return {1, 1} if given a plane aspect that doesn't exist for the VkFormat
static inline VkExtent2D vkuFindMultiplaneExtentDivisors(VkFormat mp_fmt, VkImageAspectFlagBits plane_aspect);

// From table in spec vkspec.html#formats-compatible-zs-color
// Introduced in VK_KHR_maintenance8 to allow copying between color and depth/stencil formats
static inline bool vkuFormatIsDepthStencilWithColorSizeCompatible(VkFormat color_format, VkFormat ds_format, VkImageAspectFlags aspect_mask);

// Returns the count of components in a VkFormat
static inline uint32_t vkuFormatComponentCount(VkFormat format);

// Returns the texel block extent of a VkFormat
static inline VkExtent3D vkuFormatTexelBlockExtent(VkFormat format);

// Returns the Compatibility Class of a VkFormat as defined by the spec
static inline enum VKU_FORMAT_COMPATIBILITY_CLASS vkuFormatCompatibilityClass(VkFormat format);

// Returns the number of texels inside a texel block
// Will always be 1 when not using compressed block formats
static inline uint32_t vkuFormatTexelsPerBlock(VkFormat format);

// Returns the number of bytes in a single Texel Block.
// When dealing with a depth/stencil format, need to consider using vkuFormatStencilSize or vkuFormatDepthSize.
// When dealing with mulit-planar formats, need to consider using vkuGetPlaneIndex.
static inline uint32_t vkuFormatTexelBlockSize(VkFormat format);

''')
        for bits in ['8', '16', '32', '64']:
            out.append(f'// Returns whether a VkFormat contains only {bits}-bit sized components\n')
            out.append(f'static inline bool vkuFormatIs{bits}bit(VkFormat format);\n\n')

        out.append('''// Returns whether a VkFormat has a component of a given size
static inline bool vkuFormatHasComponentSize(VkFormat format, uint32_t size);

// Returns whether a VkFormat has a Red color component
static inline bool vkuFormatHasRed(VkFormat format);

// Returns whether a VkFormat has a Green color component
static inline bool vkuFormatHasGreen(VkFormat format);

// Returns whether a VkFormat has a Blue color component
static inline bool vkuFormatHasBlue(VkFormat format);

// Returns whether a VkFormat has a Alpha color component
static inline bool vkuFormatHasAlpha(VkFormat format);

// Returns whether a VkFormat is equal to VK_FORMAT_UNDEFINED
static inline bool vkuFormatIsUndefined(VkFormat format) { return (format == VK_FORMAT_UNDEFINED); }

// Returns whether a VkFormat is a "blocked image" as defined in the spec (vkspec.html#blocked-image)
static inline bool vkuFormatIsBlockedImage(VkFormat format) {
    return (vkuFormatIsCompressed(format) || vkuFormatIsSinglePlane_422(format));
}

// Returns whether a VkFormat is a "color format'. Because there is no official specification definition of
// "color format", it is defined here as anything that isn't a depth/stencil format, multiplane format, or the undefined format.
static inline bool vkuFormatIsColor(VkFormat format) {
    return !(vkuFormatIsUndefined(format) || vkuFormatIsDepthOrStencil(format) || vkuFormatIsMultiplane(format));
}

enum VKU_FORMAT_COMPONENT_TYPE {
    VKU_FORMAT_COMPONENT_TYPE_NONE,
    VKU_FORMAT_COMPONENT_TYPE_R,
    VKU_FORMAT_COMPONENT_TYPE_G,
    VKU_FORMAT_COMPONENT_TYPE_B,
    VKU_FORMAT_COMPONENT_TYPE_A,
    VKU_FORMAT_COMPONENT_TYPE_D,
    VKU_FORMAT_COMPONENT_TYPE_S,
};

// Compressed formats don't have a defined component size
#define VKU_FORMAT_COMPRESSED_COMPONENT 0xFFFFFFFF

struct VKU_FORMAT_COMPONENT_INFO {
    enum VKU_FORMAT_COMPONENT_TYPE type;
    uint32_t size;  // bits
};

// Generic information for all formats
struct VKU_FORMAT_INFO {
    enum VKU_FORMAT_COMPATIBILITY_CLASS compatibility;
    uint32_t texel_block_size;  // bytes
    uint32_t texels_per_block;
    VkExtent3D block_extent;
    uint32_t component_count;
    struct VKU_FORMAT_COMPONENT_INFO components[VKU_FORMAT_MAX_COMPONENTS];
};
''')
        formats_in_order = {}
        # Manually add in the entry for VK_FORMAT_UNDEFINED because it is missing from the self.vk.formats dict
        formats_in_order[0] = Format('VK_FORMAT_UNDEFINED', 'NONE', 0,0, ['0','0','0'], None, None, None, [], [], None)
        for e in self.vk.enums['VkFormat'].fields:
            if e.name != 'VK_FORMAT_UNDEFINED':
                formats_in_order[e.value] = self.vk.formats[e.name]
        # Number of VkFormats should equal the fields of the VkFormat enum
        assert(len(formats_in_order) == len(self.vk.enums['VkFormat'].fields))
        formats_in_order = dict(sorted(formats_in_order.items()))

        out.append(f'const struct VKU_FORMAT_INFO vku_formats[{len(self.vk.formats) + 1}] = {{\n')
        for f in formats_in_order.values():
            className = getClassName(f.className)
            blockExtent = ', '.join(f.blockExtent) if len(f.blockExtent) > 0 else '1, 1, 1'
            out.extend(f'    {{ VKU_FORMAT_COMPATIBILITY_CLASS_{className}, {f.blockSize}, {f.texelsPerBlock}, {{{blockExtent}}}, {len(f.components)}, {{')
            for index, component in enumerate(f.components):
                bits = 'VKU_FORMAT_COMPRESSED_COMPONENT' if component.bits == 'compressed' else component.bits
                out.append(f'{{VKU_FORMAT_COMPONENT_TYPE_{component.type}, {bits}}}')
                if index + 1 != len(f.components):
                    out.append(', ')
            out.append('} },\n')
        out.append('};\n')

        # Find the "format groups", eg formats whose value are consecutive, as that is the way they are written into the 'formats' array.
        # Value refers to the enum value. These are discontinuous.
        # Index refers to the index of a format in the vku_formats array. These are from 0 to the len(formats_in_order).
        format_values = list(formats_in_order.keys())
        FormatGroup = namedtuple('FormatGroup', ['begin_format', 'end_format','array_index'])
        format_groups = []
        index = 0
        while index < len(format_values):
            start_value = format_values[index]
            end_value = format_values[-1] # use last format as sentinel so the last group can get the right end value
            previous_value = start_value - 1
            # Find the end value for the current group
            for format_value in format_values[index:]:
                if previous_value + 1 != format_value:
                    end_value = previous_value
                    break
                previous_value = format_value
            format_groups.append(FormatGroup(formats_in_order[start_value].name, formats_in_order[end_value].name, index))
            index += (end_value - start_value) + 1

        out.append('static inline struct VKU_FORMAT_INFO vkuGetFormatInfo(VkFormat format) {\n')
        for group in format_groups:
            out.append(f'    {"else " if group.array_index != 0 else ""}if ({group.begin_format} <= format && format <= {group.end_format} )')
            out.append(f' {{ return vku_formats[format - {group.begin_format} + {group.array_index}]; }}\n')
        out.append('''    // Default case - return VK_FORMAT_UNDEFINED
    else {
        return vku_formats[0];
    }
}

struct VKU_FORMAT_PER_PLANE_COMPATIBILITY {
    uint32_t width_divisor;
    uint32_t height_divisor;
    VkFormat compatible_format;
};

// Information for multiplanar formats
struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY {
    struct VKU_FORMAT_PER_PLANE_COMPATIBILITY per_plane[VKU_FORMAT_MAX_PLANES];
};

''')
        out.append('// Source: Vulkan spec Table 47. Plane Format Compatibility Table\n')
        out.append('static inline struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY vkuGetFormatCompatibility(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for format in [x for x in self.vk.formats.values() if x.planes]:
            out.extend(f'        case {format.name}: {{\n')
            out.append('            struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY out = {{')
            for index, plane in enumerate(format.planes):
                if (index != plane.index):
                    self.logMsg('error', 'index of planes were not added in order')
                out.append(f'{{{plane.widthDivisor}, {plane.heightDivisor}, {plane.compatible} }}')
                if index + 1 != self.maxPlaneCount:
                    out.append(', ')
            # pad any 'empty' elements
            if len(format.planes) < self.maxPlaneCount:
                for index in range(len(format.planes), self.maxPlaneCount):
                    out.append('{1, 1, VK_FORMAT_UNDEFINED }')
                    if index + 1 != self.maxPlaneCount:
                        out.append(', ')
            out.append('}};\n')
            out.append('            return out; }\n')
        out.append('        default: {\n')
        out.append('            struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY out = {{{1, 1, VK_FORMAT_UNDEFINED}, {1, 1, VK_FORMAT_UNDEFINED}, {1, 1, VK_FORMAT_UNDEFINED}}};\n')
        out.append('            return out; }\n')
        out.append('    };\n')
        out.append('}\n')

        for numericFormat in sorted(self.numericFormats):
            out.append(f'\n// Return true if all components in a format are an {numericFormat}\n')
            out.append(f'bool vkuFormatIs{numericFormat}(VkFormat format) {{\n')
            out.append('    switch (format) {\n')
            out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasNumericFormat(f, numericFormat)])
            out.append(self.commonBoolSwitch)

        out.append('''
static inline bool vkuFormatIsSampledInt(VkFormat format) { return (vkuFormatIsSINT(format) || vkuFormatIsUINT(format)); }
static inline bool vkuFormatIsSampledFloat(VkFormat format) {
    return (vkuFormatIsUNORM(format) || vkuFormatIsSNORM(format) ||
            vkuFormatIsUSCALED(format) || vkuFormatIsSSCALED(format) ||
            vkuFormatIsUFLOAT(format) || vkuFormatIsSFLOAT(format) ||
            vkuFormatIsSRGB(format));
}
''')

        for key in sorted(self.compressedFormats.keys()):
            out.append(f'\n// Return true if a format is a {key} compressed image format\n')
            out.append(f'bool vkuFormatIsCompressed_{key}(VkFormat format) {{\n')
            out.append('    switch (format) {\n')
            for f in sorted(self.compressedFormats[key]):
                out.append(f'        case {f}:\n')
            out.append(self.commonBoolSwitch)

        out.append('// Return true if a format is any compressed image format\n')
        out.append('bool vkuFormatIsCompressed(VkFormat format) {\n')
        out.append('    return\n')
        for index, key in enumerate(sorted(self.compressedFormats.keys())):
            out.append(f'        vkuFormatIsCompressed_{key}(format)')
            if (index !=len(self.compressedFormats.keys()) - 1):
                out.append(' ||\n')
        out.append(';\n')
        out.append('}\n')

        out.append('\n// Return true if format is a depth OR stencil format\n')
        out.append('bool vkuFormatIsDepthOrStencil(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasDepth(f) or formatHasStencil(f)])
        out.append(self.commonBoolSwitch)

        out.append('\n// Return true if format is a depth AND stencil format\n')
        out.append('bool vkuFormatIsDepthAndStencil(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasDepth(f) and formatHasStencil(f)])
        out.append(self.commonBoolSwitch)

        out.append('\n// Return true if format is a depth ONLY format\n')
        out.append('bool vkuFormatIsDepthOnly(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasDepth(f) and not formatHasStencil(f)])
        out.append(self.commonBoolSwitch)

        out.append('\n// Return true if format is a stencil ONLY format\n')
        out.append('bool vkuFormatIsStencilOnly(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasStencil(f) and not formatHasDepth(f)])
        out.append(self.commonBoolSwitch)

        out.append('\n// Returns size of depth component in bits')
        out.append('\n// Returns zero if no depth component\n')
        out.append('uint32_t vkuFormatDepthSize(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        # sorts case statments together with same return value
        used = []
        for key, value in sorted(self.depthFormats.items()):
            if key not in used:
                for key_dup, value_dup in sorted(self.depthFormats.items()):
                    if value_dup.bits == value.bits:
                        used.append(key_dup)
                        out.append(f'        case {key_dup}:\n')
                out.append(f'            return {value.bits};\n')
        out.append('        default:\n')
        out.append('            return 0;\n')
        out.append('    }\n')
        out.append('}\n')

        out.append('\n// Returns size of stencil component in bits')
        out.append('\n// Returns zero if no stencil component\n')
        out.append('uint32_t vkuFormatStencilSize(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        # sorts case statments together with same return value
        used = []
        for key, value in sorted(self.stencilFormats.items()):
            if key not in used:
                for key_dup, value_dup in sorted(self.stencilFormats.items()):
                    if value_dup.bits == value.bits:
                        used.append(key_dup)
                        out.append(f'        case {key_dup}:\n')
                out.append(f'            return {value.bits};\n')
        out.append('        default:\n')
        out.append('            return 0;\n')
        out.append('    }\n')
        out.append('}\n')

        out.append('\n// Returns NONE if no depth component\n')
        out.append('enum VKU_FORMAT_NUMERICAL_TYPE vkuFormatDepthNumericalType(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        # sorts case statments together with same return value
        used = []
        for key, value in sorted(self.depthFormats.items()):
            if key not in used:
                for key_dup, value_dup in sorted(self.depthFormats.items()):
                    if value_dup.numericFormat == value.numericFormat:
                        used.append(key_dup)
                        out.append(f'        case {key_dup}:\n')
                out.append(f'            return VKU_FORMAT_NUMERICAL_TYPE_{value.numericFormat};\n')
        out.append('        default:\n')
        out.append('            return VKU_FORMAT_NUMERICAL_TYPE_NONE;\n')
        out.append('    }\n')
        out.append('}\n')

        out.append('\n// Returns NONE if no stencil component\n')
        out.append('enum VKU_FORMAT_NUMERICAL_TYPE vkuFormatStencilNumericalType(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        # sorts case statments together with same return value
        used = []
        for key, value in sorted(self.stencilFormats.items()):
            if key not in used:
                for key_dup, value_dup in sorted(self.stencilFormats.items()):
                    if value_dup.numericFormat == value.numericFormat:
                        used.append(key_dup)
                        out.append(f'        case {key_dup}:\n')
                out.append(f'            return VKU_FORMAT_NUMERICAL_TYPE_{value.numericFormat};\n')
        out.append('        default:\n')
        out.append('            return VKU_FORMAT_NUMERICAL_TYPE_NONE;\n')
        out.append('    }\n')
        out.append('}\n')

        out.append('\n// Return true if format is a packed format\n')
        out.append('bool vkuFormatIsPacked(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for name in [x.name for x in self.vk.formats.values() if x.packed]:
            out.append(f'        case {name}:\n')
        out.append(self.commonBoolSwitch)

        out.append('\n// Return true if format requires sampler YCBCR conversion\n')
        out.append('// for VK_IMAGE_ASPECT_COLOR_BIT image views\n')
        out.append('// Table found in spec\n')
        out.append('bool vkuFormatRequiresYcbcrConversion(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for name in [x.name for x in self.vk.formats.values() if x.chroma]:
            out.append(f'        case {name}:\n')
        out.append(self.commonBoolSwitch)

        out.append('\nbool vkuFormatIsXChromaSubsampled(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for name in [x.name for x in self.vk.formats.values() if x.chroma == '420' or x.chroma == '422']:
            out.append(f'        case {name}:\n')
        out.append(self.commonBoolSwitch)

        out.append('\nbool vkuFormatIsYChromaSubsampled(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for name in [x.name for x in self.vk.formats.values() if x.chroma == '420']:
                out.append(f'        case {name}:\n')
        out.append(self.commonBoolSwitch)

        out.append('\nbool vkuFormatIsSinglePlane_422(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        for name in [x.name for x in self.vk.formats.values() if x.chroma == '422' and not x.planes]:
            out.append(f'        case {name}:\n')
        out.append(self.commonBoolSwitch)

        out.append('\n// Returns number of planes in format (which is 1 by default)\n')
        out.append('uint32_t vkuFormatPlaneCount(VkFormat format) {\n')
        out.append('    switch (format) {\n')
        # Use range to sort formats together
        for i in range(2, self.maxPlaneCount + 1):
            out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if len(f.planes) == i])
            out.append(f'            return {i};\n')
        out.append('        default:\n')
        out.append('            return 1;\n')
        out.append('    }\n')
        out.append('}\n')
        out.append('''
// Will return VK_FORMAT_UNDEFINED if given a plane aspect that doesn't exist for the format
static inline VkFormat vkuFindMultiplaneCompatibleFormat(VkFormat mp_fmt, VkImageAspectFlagBits plane_aspect) {
    const uint32_t plane_idx = vkuGetPlaneIndex(plane_aspect);
    const struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY multiplane_compatibility = vkuGetFormatCompatibility(mp_fmt);
    if ((multiplane_compatibility.per_plane[0].compatible_format == VK_FORMAT_UNDEFINED) || (plane_idx >= VKU_FORMAT_MAX_PLANES)) {
        return VK_FORMAT_UNDEFINED;
    }

    return multiplane_compatibility.per_plane[plane_idx].compatible_format;
}

static inline VkExtent2D vkuFindMultiplaneExtentDivisors(VkFormat mp_fmt, VkImageAspectFlagBits plane_aspect) {
    VkExtent2D divisors = {1, 1};
    const uint32_t plane_idx = vkuGetPlaneIndex(plane_aspect);
    const struct VKU_FORMAT_MULTIPLANE_COMPATIBILITY multiplane_compatibility = vkuGetFormatCompatibility(mp_fmt);
    if ((multiplane_compatibility.per_plane[0].compatible_format == VK_FORMAT_UNDEFINED) || (plane_idx >= VKU_FORMAT_MAX_PLANES)) {
        return divisors;
    }

    divisors.width = multiplane_compatibility.per_plane[plane_idx].width_divisor;
    divisors.height = multiplane_compatibility.per_plane[plane_idx].height_divisor;
    return divisors;
}

// TODO - This should be generated, but will need updating the spec XML and table
// Some few case don't have an aspect mask, so might need to check both the Depth and Stencil possiblity
static inline bool vkuFormatIsDepthStencilWithColorSizeCompatible(VkFormat color_format, VkFormat ds_format, VkImageAspectFlags aspect_mask) {
    bool valid = false;

    if (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
        if (ds_format == VK_FORMAT_S8_UINT || ds_format == VK_FORMAT_D16_UNORM_S8_UINT ||
            ds_format == VK_FORMAT_D24_UNORM_S8_UINT || ds_format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
            valid |= (color_format == VK_FORMAT_R8_UINT || color_format == VK_FORMAT_R8_SINT ||
                      color_format == VK_FORMAT_R8_UNORM || color_format == VK_FORMAT_R8_SNORM);
        }
    }

    if (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
        if (ds_format == VK_FORMAT_D32_SFLOAT || ds_format == VK_FORMAT_D32_SFLOAT_S8_UINT ||
            ds_format == VK_FORMAT_X8_D24_UNORM_PACK32 || ds_format == VK_FORMAT_D24_UNORM_S8_UINT) {
            valid |= (color_format == VK_FORMAT_R32_SFLOAT || color_format == VK_FORMAT_R32_SINT || color_format == VK_FORMAT_R32_UINT);
        }
        if (ds_format == VK_FORMAT_D16_UNORM || ds_format == VK_FORMAT_D16_UNORM_S8_UINT) {
            valid |= (color_format == VK_FORMAT_R16_SFLOAT || color_format == VK_FORMAT_R16_UNORM ||
                      color_format == VK_FORMAT_R16_SNORM  || color_format == VK_FORMAT_R16_UINT || color_format == VK_FORMAT_R16_SINT);
        }
    }

    return valid;
}

static inline uint32_t vkuFormatComponentCount(VkFormat format) { return vkuGetFormatInfo(format).component_count; }

static inline VkExtent3D vkuFormatTexelBlockExtent(VkFormat format) { return vkuGetFormatInfo(format).block_extent; }

static inline enum VKU_FORMAT_COMPATIBILITY_CLASS vkuFormatCompatibilityClass(VkFormat format) { return vkuGetFormatInfo(format).compatibility; }

static inline uint32_t vkuFormatTexelsPerBlock(VkFormat format) { return vkuGetFormatInfo(format).texels_per_block; }

static inline uint32_t vkuFormatTexelBlockSize(VkFormat format) { return vkuGetFormatInfo(format).texel_block_size; }

''')
        # Could loop the components, but faster to just list these
        for bits in ['8', '16', '32', '64']:
            out.append(f'static inline bool vkuFormatIs{bits}bit(VkFormat format) {{\n')
            out.append('    switch (format) {\n')
            out.extend([f'        case {f.name}:\n' for f in self.vk.formats.values() if formatHasEqualBitsize(f, bits)])
            out.append(self.commonBoolSwitch)

        out.append('''
static inline bool vkuFormatHasComponentSize(VkFormat format, uint32_t size) {
    const struct VKU_FORMAT_INFO format_info = vkuGetFormatInfo(format);
    bool equal_component_size = false;
    for (size_t i = 0; i < VKU_FORMAT_MAX_COMPONENTS; i++) {
        equal_component_size |= format_info.components[i].size == size;
    }
    return equal_component_size;
}

static inline bool vkuFormatHasComponentType(VkFormat format, enum VKU_FORMAT_COMPONENT_TYPE component) {
    const struct VKU_FORMAT_INFO format_info = vkuGetFormatInfo(format);
    bool equal_component_type = false;
    for (size_t i = 0; i < VKU_FORMAT_MAX_COMPONENTS; i++) {
        equal_component_type |= format_info.components[i].type == component;
    }
    return equal_component_type;
}

static inline bool vkuFormatHasRed(VkFormat format) { return vkuFormatHasComponentType(format, VKU_FORMAT_COMPONENT_TYPE_R); }

static inline bool vkuFormatHasGreen(VkFormat format) { return vkuFormatHasComponentType(format, VKU_FORMAT_COMPONENT_TYPE_G); }

static inline bool vkuFormatHasBlue(VkFormat format) { return vkuFormatHasComponentType(format, VKU_FORMAT_COMPONENT_TYPE_B); }

static inline bool vkuFormatHasAlpha(VkFormat format) { return vkuFormatHasComponentType(format, VKU_FORMAT_COMPONENT_TYPE_A); }

static inline uint32_t vkuGetPlaneIndex(VkImageAspectFlagBits aspect) {
    switch (aspect) {
        case VK_IMAGE_ASPECT_PLANE_0_BIT:
            return 0;
        case VK_IMAGE_ASPECT_PLANE_1_BIT:
            return 1;
        case VK_IMAGE_ASPECT_PLANE_2_BIT:
            return 2;
        default:
            return VKU_FORMAT_INVALID_INDEX;
    }
}

#ifdef __cplusplus
}
#endif

// clang-format off''')

        self.write("".join(out))