#!/usr/bin/python3
#
# Copyright (c) 2019-2026 Valve Corporation
# Copyright (c) 2019-2026 LunarG, Inc.
# Copyright (c) 2019-2022 Google Inc.
# Copyright (c) 2023-2024 RasterGrid Kft.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Charles Giessen <charles@lunarg.com>

from base_generator import BaseGenerator

from collections import OrderedDict

LICENSE_HEADER = '''
/*
 * Copyright (c) 2019-2026 The Khronos Group Inc.
 * Copyright (c) 2019-2026 Valve Corporation
 * Copyright (c) 2019-2026 LunarG, Inc.
 * Copyright (c) 2023-2024 RasterGrid Kft.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Charles Giessen <charles@lunarg.com>
 *
 */

/*
 * This file is generated from the Khronos Vulkan XML API Registry.
 */
'''

CUSTOM_FORMATTERS = r'''
template <typename T>
std::string to_hex_str(const T i) {
    std::stringstream stream;
    stream << "0x" << std::setfill('0') << std::setw(sizeof(T)) << std::hex << i;
    return stream.str();
}

template <typename T>
std::string to_hex_str(Printer &p, const T i) {
    if (p.Type() == OutputType::json)
        return std::to_string(i);
    else if (p.Type() == OutputType::vkconfig_output)
        return std::string("\"") + to_hex_str(i) + std::string("\"");
    else
        return to_hex_str(i);
}

'''


# used in the .cpp code
STRUCTURES_TO_GEN = ['VkExtent3D', 'VkExtent2D', 'VkPhysicalDeviceLimits', 'VkPhysicalDeviceFeatures', 'VkPhysicalDeviceSparseProperties',
                     'VkSurfaceCapabilitiesKHR', 'VkSurfaceFormatKHR', 'VkLayerProperties', 'VkPhysicalDeviceToolProperties', 'VkFormatProperties',
                     'VkSurfacePresentScalingCapabilitiesKHR', 'VkSurfacePresentModeCompatibilityKHR', 'VkPhysicalDeviceHostImageCopyProperties',
                     'VkVideoProfileInfoKHR', 'VkVideoCapabilitiesKHR', 'VkVideoFormatPropertiesKHR', 'VkCooperativeMatrixPropertiesKHR',
                     'VkPhysicalDeviceFragmentShadingRateKHR', 'VkMultisamplePropertiesEXT',
                     'VkDisplayPropertiesKHR', 'VkDisplayPlanePropertiesKHR', 'VkDisplayPlaneCapabilitiesKHR', 'VkDisplayModePropertiesKHR',
                     'VkDisplayModeParametersKHR']

ENUMS_TO_GEN = ['VkResult', 'VkFormat', 'VkPresentModeKHR',
                'VkPhysicalDeviceType', 'VkImageTiling', 'VkTimeDomainKHR']
FLAGS_TO_GEN = ['VkSurfaceTransformFlagsKHR', 'VkCompositeAlphaFlagsKHR', 'VkSurfaceCounterFlagsEXT', 'VkQueueFlags',
                'VkDeviceGroupPresentModeFlagsKHR', 'VkFormatFeatureFlags', 'VkFormatFeatureFlags2', 'VkMemoryPropertyFlags', 'VkMemoryHeapFlags']
FLAG_STRINGS_TO_GEN = ['VkQueueFlags']

STRUCT_SHORT_VERSIONS_TO_GEN = ['VkExtent3D', 'VkExtent2D']

STRUCT_COMPARISONS_TO_GEN = ['VkSurfaceFormatKHR', 'VkSurfaceFormat2KHR', 'VkSurfaceCapabilitiesKHR',
                             'VkSurfaceCapabilities2KHR', 'VkSurfaceCapabilities2EXT']
# don't generate these structures
STRUCT_BLACKLIST = ['VkVideoProfileListInfoKHR', 'VkDrmFormatModifierPropertiesListEXT', 'VkDrmFormatModifierPropertiesEXT', 'VkDrmFormatModifierPropertiesList2EXT']
# These structures are only used in version 1.1, otherwise they are included in the promoted structs
STRUCT_1_1_LIST = ['VkPhysicalDeviceProtectedMemoryFeatures', 'VkPhysicalDeviceShaderDrawParametersFeatures', 'VkPhysicalDeviceSubgroupProperties', 'VkPhysicalDeviceProtectedMemoryProperties']

# generate these structures such that they only print when not in json mode (as json wants them separate)
PORTABILITY_STRUCTS = ['VkPhysicalDevicePortabilitySubsetFeaturesKHR', 'VkPhysicalDevicePortabilitySubsetPropertiesKHR']

# iostream or custom outputter handles these types
PREDEFINED_TYPES = ['char', 'VkBool32', 'uint32_t', 'uint8_t', 'int32_t',
                    'float', 'uint64_t', 'size_t', 'VkDeviceSize', 'int64_t']

NAMES_TO_IGNORE = ['sType', 'pNext', 'displayMode', 'display', 'currentDisplay']

EXTENSION_TYPE_INSTANCE = 'instance'
EXTENSION_TYPE_DEVICE = 'device'
EXTENSION_TYPE_BOTH = 'both'

# Types that need pNext Chains built. 'extends' is the xml tag used in the structextends member. 'type' can be device, instance, or both
EXTENSION_CATEGORIES = OrderedDict((
    ('phys_device_props2',
        {'extends': 'VkPhysicalDeviceProperties2',
         'type': EXTENSION_TYPE_BOTH,
         'print_iterator': True,
         'can_show_promoted_structs': True,
         'ignore_vendor_exclusion': False}),
    ('phys_device_mem_props2',
        {'extends': 'VkPhysicalDeviceMemoryProperties2',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': False,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': False}),
    ('phys_device_features2',
        {'extends': 'VkPhysicalDeviceFeatures2',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': True,
         'ignore_vendor_exclusion': False}),
    ('surface_capabilities2',
        {'extends': 'VkSurfaceCapabilities2KHR',
         'type': EXTENSION_TYPE_BOTH,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': False,
         'exclude': ['VkSurfacePresentScalingCapabilitiesKHR', 'VkSurfacePresentModeCompatibilityKHR']}),
    ('format_properties2',
        {'extends': 'VkFormatProperties2',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': False}),
    ('queue_properties2',
        {'extends': 'VkQueueFamilyProperties2',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': False}),
    ('video_profile_info',
        {'extends': 'VkVideoProfileInfoKHR',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': True}),
    ('video_capabilities',
        {'extends': 'VkVideoCapabilitiesKHR',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': True,}),
    ('video_format_properties',
        {'extends': 'VkVideoFormatPropertiesKHR',
         'type': EXTENSION_TYPE_DEVICE,
         'print_iterator': True,
         'can_show_promoted_structs': False,
         'ignore_vendor_exclusion': True})
                                   ))
class VulkanInfoGenerator(BaseGenerator):
    def __init__(self):
        BaseGenerator.__init__(self)
        self.format_ranges = []

    def generate(self):
        self.findFormatRanges()

        # gather the types that are needed to generate
        types_to_gen = set()
        types_to_gen.update(ENUMS_TO_GEN)
        types_to_gen.update(FLAGS_TO_GEN)
        types_to_gen.update(STRUCTURES_TO_GEN)

        extension_types = {}
        for key, ext_info in EXTENSION_CATEGORIES.items():
            extension_types[key] = []

            for extended_struct in self.vk.structs[ext_info.get('extends')].extendedBy:
                if ext_info.get('exclude') is not None and extended_struct in ext_info.get('exclude'):
                    continue
                elif ext_info.get('ignore_vendor_exclusion'):
                    extension_types[key].append(extended_struct)
                    continue
                vendor_tags = []
                for extension in self.vk.structs[extended_struct].extensions:
                    vendor_tags.append(extension.split('_')[1])
                if len(vendor_tags) == 0 or 'KHR' in vendor_tags or 'EXT' in vendor_tags:
                    extension_types[key].append(extended_struct)
            extension_types[key] = sorted(extension_types[key])
            types_to_gen.update(extension_types[key])

        # find all the types that need
        types_to_gen.update(self.findAllTypesToGen(types_to_gen))

        types_to_gen = sorted(types_to_gen)

        comparison_types_to_gen = set()
        comparison_types_to_gen.update(STRUCT_COMPARISONS_TO_GEN)
        comparison_types_to_gen.update(self.findAllTypesToGen(comparison_types_to_gen))
        comparison_types_to_gen = sorted(comparison_types_to_gen)


        # print the types gathered
        out = []
        out.append(LICENSE_HEADER + '\n')
        out.append('#include "vulkaninfo.h"\n')
        out.append('#include "outputprinter.h"\n')
        out.append(CUSTOM_FORMATTERS)

        out.extend(self.genVideoEnums())

        for enum in (e for e in types_to_gen if e in self.vk.enums):
            out.extend(self.PrintEnumToString(self.vk.enums[enum]))
            out.extend(self.PrintEnum(self.vk.enums[enum]))

        # Need to go through all flags to find if they or their associated bitmask needs printing
        # This is because both bitmask and flag types are generated in PrintBitMask
        for name in (x for x in sorted(self.vk.flags.keys()) if x in types_to_gen or self.vk.flags[x].bitmaskName in types_to_gen):
            bitmask = self.vk.bitmasks[self.vk.flags[name].bitmaskName]

            out.extend(self.PrintBitMask(bitmask, bitmask.flagName))

            if bitmask.flagName in FLAG_STRINGS_TO_GEN:
                out.extend(self.PrintBitMaskToString(bitmask, bitmask.flagName))
        # make sure dump functions for nested structures are declared before use
        for s in (x for x in types_to_gen if x in self.vk.structs and x not in STRUCT_BLACKLIST):
            out.extend(self.PrintStructure(self.vk.structs[s], True))
        for s in (x for x in types_to_gen if x in self.vk.structs and x not in STRUCT_BLACKLIST):
            out.extend(self.PrintStructure(self.vk.structs[s], False))

        for key, value in EXTENSION_CATEGORIES.items():
            out.extend(self.PrintChainStruct(key, extension_types[key], value))

        for s in (x for x in comparison_types_to_gen if x in self.vk.structs):
            out.extend(self.PrintStructComparisonForwardDecl(self.vk.structs[s]))
        for s in (x for x in comparison_types_to_gen if x in self.vk.structs):
            out.extend(self.PrintStructComparison(self.vk.structs[s]))
        for s in (x for x in types_to_gen if x in self.vk.structs and x in STRUCT_SHORT_VERSIONS_TO_GEN):
            out.extend(self.PrintStructShort(self.vk.structs[s]))

        out.append('auto format_ranges = std::array{\n')
        for f in self.format_ranges:
            out.append(f'    FormatRange{{{f.minimum_instance_version}, {self.vk.extensions[f.extensions[0]].nameString if len(f.extensions) > 0 else "nullptr"}, ')
            out.append(f'static_cast<VkFormat>({f.first_format}), static_cast<VkFormat>({f.last_format})}},\n')
        out.append('};\n')

        out.extend(self.genVideoProfileUtils())

        self.write(''.join(out))


    def genVideoEnums(self):
        out = []
        for enum in self.vk.videoStd.enums.values():
            out.append(f'std::string {enum.name}String({enum.name} value) {{\n')
            out.append('    switch (value) {\n')
            for field in enum.fields:
                # Ignore aliases
                if field.value is not None:
                    out.append(f'        case {field.name}: return "{field.name}";\n')
            out.append(f'        default: return std::string("UNKNOWN_{enum.name}_value") + std::to_string(value);\n')
            out.append('    }\n}\n')
            out.append(f'void Dump{enum.name}(Printer &p, std::string name, {enum.name} value) {{\n')
            out.append(f'    p.PrintKeyString(name, {enum.name}String(value));\n}}\n')
        return out


    # Utility to get the extension / version precondition of a list of type names
    def GetTypesPrecondition(self, typelist, indent):
        indent = ' ' * indent
        out = []
        extEnables = []
        for typename in typelist:
            extEnables.extend(self.vk.structs[typename].extensions)

        version = None
        for typename in typelist:
            for v in self.vk.versions.values():
                if typename in v.name:
                    if version is not None and (v.major > version.major or (v.major == version.major and v.minor > version.minor)):
                        version = v


        has_version = version is not None
        has_extNameStr = len(extEnables) > 0
        if has_version or has_extNameStr:
            out.append(f'{indent}if (')
            has_printed_condition = False
            if has_extNameStr:
                for ext in extEnables:
                    if has_printed_condition:
                        out.append(f'\n{indent} || ')
                    else:
                        has_printed_condition = True
                        if has_version:
                            out.append('(')
                    if self.vk.extensions[ext].device:
                        out.append(f'gpu.CheckPhysicalDeviceExtensionIncluded({self.vk.extensions[ext].nameString})')
                    else:
                        assert False, 'Should never get here'
            if has_version:
                if has_printed_condition:
                    out.append(f'\n{indent} || (gpu.api_version >= {version.nameApi})')
                else:
                    out.append(f'gpu.api_version >= {version.nameApi}')
            out.append(') {\n')
        else:
            out = f'{indent}{{\n'
        return out

    # Utility to construct a capability prerequisite condition evaluation expression
    def GetRequiredCapsCondition(self, structName, memberName, memberRef, value):
        condition = ''
        requiredCapStructDef = self.vk.structs[structName]
        for member in requiredCapStructDef.members:
            if member.name == memberName:
                if member.type in self.vk.flags:
                    # Check that the flags contain all the required values
                    def genExpressionFromValue(value):
                        return value if value == "" else f"({memberRef} & {value}) != 0"

                    for char in condition:
                        if char in ['(', ')', '+', ',']:
                            condition += genExpressionFromValue(value)
                            value = ""
                            if char == '+':
                                # '+' means AND
                                condition += ' && '
                            elif char == ',':
                                # ',' means OR
                                condition += ' || '
                            else:
                                condition += char
                        else:
                            value += char
                    condition += genExpressionFromValue(value)
                else:
                    condition = f'{memberRef} == {value}'
        if condition == '':
            return 'true'
        else:
            return f'({condition})'

    def genVideoProfileUtils(self):
        out = []

        # Generate video format properties comparator
        out.append('''
bool is_video_format_same(const VkVideoFormatPropertiesKHR &format_a, const VkVideoFormatPropertiesKHR &format_b) {
    auto a = reinterpret_cast<const VkBaseInStructure*>(&format_a);
    auto b = reinterpret_cast<const VkBaseInStructure*>(&format_b);
    bool same = true;
    while (same && a != nullptr && b != nullptr) {
        if (a->sType != b->sType) {
            // Structure type mismatch (extension structures are expected to be chained in the same order)
            same = false;
        } else {
            switch (a->sType) {''')

        if 'VkVideoFormatPropertiesKHR' in self.registry.validextensionstructs:
            for extstruct in ['VkVideoFormatPropertiesKHR'] + self.registry.validextensionstructs['VkVideoFormatPropertiesKHR']:
                extstructDef = self.vk.structs[extstruct]
                out.append(f'''
                case {extstructDef.sType}:
                    same = same && memcmp(reinterpret_cast<const char*>(a) + sizeof(VkBaseInStructure),
                                          reinterpret_cast<const char*>(b) + sizeof(VkBaseInStructure),
                                          sizeof({extstruct}) - sizeof(VkBaseInStructure)) == 0;
                    break;''')

        out.append('''
                default:
                    // Unexpected structure type
                    same = false;
                    break;
            }
        }
        a = a->pNext;
        b = b->pNext;
    }
    return same;
}
''')

        # Generate video profile info capture utilities
        out.append('''
std::vector<std::unique_ptr<AppVideoProfile>> enumerate_supported_video_profiles(AppGpu &gpu) {
    std::vector<std::unique_ptr<AppVideoProfile>> result{};

    struct ChromaSubsamplingInfo {
        VkVideoChromaSubsamplingFlagsKHR value;
        const char* name;
    };
    const std::vector<ChromaSubsamplingInfo> chroma_subsampling_list = {
        {VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, "4:2:0"},
        {VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, "4:2:2"},
        {VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, "4:4:4"},
        {VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR, "monochrome"}
    };

    struct BitDepthInfo {
        VkVideoComponentBitDepthFlagsKHR value;
        const char* name;
    };
    const std::vector<BitDepthInfo> bit_depth_list = {
        {VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, "8"},
        {VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, "10"},
        {VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, "12"}
    };

    auto find_caps_struct = [](const VkVideoCapabilitiesKHR &capabilities, VkStructureType stype) -> const VkBaseInStructure* {
        auto p = reinterpret_cast<const VkBaseInStructure*>(&capabilities);
        while (p != nullptr) {
            if (p->sType == stype) {
                return p;
            }
            p = p->pNext;
        }
        return nullptr;
    };

    auto base_format = []
        (const ChromaSubsamplingInfo &chroma_subsampling, const BitDepthInfo &luma_bit_depth, const BitDepthInfo &chroma_bit_depth) {
            std::string result{};
            result += " (";
            result += chroma_subsampling.name;
            result += " ";
            result += luma_bit_depth.name;
            if (luma_bit_depth.value != chroma_bit_depth.value) {
                result += ":";
                result += chroma_bit_depth.name;
            }
            result += "-bit)";
            return result;
        };

    auto add_profile = [&](
        const std::string &name,
        const VkVideoProfileInfoKHR &profile_info,
        AppVideoProfile::CreateProfileInfoChainCb create_profile_info_chain,
        AppVideoProfile::CreateCapabilitiesChainCb create_capabilities_chain,
        const AppVideoProfile::CreateFormatPropertiesChainCbList &create_format_properties_chain_list,
        AppVideoProfile::InitProfileCb init_profile) {
            auto profile = std::make_unique<AppVideoProfile>(gpu, gpu.phys_device,
                                                             name, profile_info,
                                                             create_profile_info_chain,
                                                             create_capabilities_chain,
                                                             create_format_properties_chain_list,
                                                             init_profile);
            if (profile->supported) {
                result.push_back(std::move(profile));
            }
        };
''')

        # Generate individual video profiles from the video codec metadata
        for videoCodec in self.vk.videoCodecs.values():
            # Ignore video codec categories
            if videoCodec.value is None:
                continue

            out.append('\n')
            out.extend(self.GetTypesPrecondition(videoCodec.profiles.keys(), 4))
            out.append(f'{" " * 8}const std::string codec_name = "{videoCodec.name}";\n')

            out.append('''
        for (auto chroma_subsampling : chroma_subsampling_list) {
            for (auto luma_bit_depth : bit_depth_list) {
                for (auto chroma_bit_depth : bit_depth_list) {
                    if (chroma_subsampling.value == VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR && luma_bit_depth.value != chroma_bit_depth.value) {
                        // Ignore the chroma bit depth dimension for monochrome
                        continue;
                    }

                    std::string profile_base_name = codec_name + base_format(chroma_subsampling, luma_bit_depth, chroma_bit_depth);
''')

            # Setup video profile info
            out.append(f'{" " * 20}VkVideoProfileInfoKHR profile_info{{\n')
            out.append(f'{" " * 20}    VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,\n')
            out.append(f'{" " * 20}    nullptr,\n')
            out.append(f'{" " * 20}    {videoCodec.value},\n')
            out.append(f'{" " * 20}    chroma_subsampling.value,\n')
            out.append(f'{" " * 20}    luma_bit_depth.value,\n')
            out.append(f'{" " * 20}    chroma_bit_depth.value\n')
            out.append(f'{" " * 20}}};\n\n')

            # Setup video profile info chain creation callback
            out.append(f'{" " * 20}auto create_profile_info_chain = [&](const void **ppnext) -> std::unique_ptr<video_profile_info_chain> {{\n')
            out.append(f'{" " * 20}    auto profile_info_chain = std::make_unique<video_profile_info_chain>();\n')
            for profileStruct in videoCodec.profiles:
                structDef = self.vk.structs[profileStruct]
                out.append(self.AddGuardHeader(structDef))
                out.append(f'{" " * 24}if (profile_info_chain != nullptr) {{\n')
                out.append(f'{" " * 28}profile_info_chain->{profileStruct[2:]}.sType = {structDef.sType};\n')
                out.append(f'{" " * 28}profile_info_chain->{profileStruct[2:]}.pNext = nullptr;\n')
                out.append(f'{" " * 28}*ppnext = &profile_info_chain->{profileStruct[2:]};\n')
                out.append(f'{" " * 28}ppnext = &profile_info_chain->{profileStruct[2:]}.pNext;\n')
                out.append(f'{" " * 24}}}\n')
                if structDef.protect:
                    out.append(f'#else\n{" " * 20}profile_info_chain = nullptr;\n')
                out.append(self.AddGuardFooter(structDef))
            out.append(f'{" " * 20}    return profile_info_chain;\n')
            out.append(f'{" " * 20}}};\n\n')

            # Setup video capabilities chain creation callback
            out.append(f'{" " * 20}auto create_capabilities_chain = [&](void **ppnext) -> std::unique_ptr<video_capabilities_chain> {{\n')
            out.append(f'{" " * 20}    auto capabilities_chain = std::make_unique<video_capabilities_chain>();\n')
            for capabilities in videoCodec.capabilities:
                structDef = self.vk.structs[capabilities]
                out.append(self.AddGuardHeader(structDef))
                out.append(f'{" " * 24}if (capabilities_chain != nullptr) {{\n')
                out.extend(self.GetTypesPrecondition([capabilities], 28))
                out.append(f'{" " * 32}capabilities_chain->{capabilities[2:]}.sType = {structDef.sType};\n')
                out.append(f'{" " * 32}capabilities_chain->{capabilities[2:]}.pNext = nullptr;\n')
                out.append(f'{" " * 32}*ppnext = &capabilities_chain->{capabilities[2:]};\n')
                out.append(f'{" " * 32}ppnext = &capabilities_chain->{capabilities[2:]}.pNext;\n')
                out.append(f'{" " * 28}}}\n')
                out.append(f'{" " * 24}}}\n')
                out.append(self.AddGuardFooter(structDef))
            out.append(f'{" " * 20}    return capabilities_chain;\n')
            out.append(f'{" " * 20}}};\n\n')

            # Setup video format properties chain creation callbacks
            out.append(f'{" " * 20}const AppVideoProfile::CreateFormatPropertiesChainCbList create_format_properties_chain_list = {{\n')
            for format in videoCodec.formats.values():
                out.append(f'{" " * 24}AppVideoProfile::CreateFormatPropertiesChainCb {{\n')
                out.append(f'{" " * 28}"{format.name}",\n')
                out.append(f'{" " * 28}{format.usage.replace("+", " | ")},\n')

                # Callback to check required capabilities
                out.append(f'{" " * 28}[&](const VkVideoCapabilitiesKHR &capabilities) -> bool {{\n')
                out.append(f'{" " * 28}    bool supported = true;\n')
                for requiredCap in format.requiredCaps:
                    structDef = self.vk.structs[requiredCap.struct]
                    out.append(self.AddGuardHeader(structDef))
                    out.extend(self.GetTypesPrecondition([requiredCap.struct], 32))
                    out.append(f'{" " * 32}    auto caps = reinterpret_cast<const {requiredCap.struct}*>(find_caps_struct(capabilities, {structDef.sType}));\n')
                    out.append(f'{" " * 32}    if (caps != nullptr) {{\n')
                    out.append(f'{" " * 32}        supported = supported && {self.GetRequiredCapsCondition(requiredCap.struct, requiredCap.member, f"caps->{requiredCap.member}", requiredCap.value)};\n')
                    out.append(f'{" " * 32}    }} else {{\n')
                    out.append(f'{" " * 32}        supported = false;\n')
                    out.append(f'{" " * 32}    }}\n')
                    out.append(f'{" " * 32}}} else {{\n')
                    out.append(f'{" " * 32}    supported = false;\n')
                    out.append(f'{" " * 32}}}\n')
                    if structDef.protect:
                        out.append(f'#else\n{" " * 32}supported = false;\n')
                    out.append(self.AddGuardFooter(structDef))
                out.append(f'{" " * 28}    return supported;\n')
                out.append(f'{" " * 28}}},\n')

                # Callback to create video format properties chain
                out.append(f'{" " * 28}[&](void **ppnext) -> std::unique_ptr<video_format_properties_chain> {{\n')
                out.append(f'{" " * 28}    auto format_properties_chain = std::make_unique<video_format_properties_chain>();\n')
                for formatProps in format.properties:
                    structDef = self.vk.structs[formatProps]
                    out.append(self.AddGuardHeader(structDef))
                    out.append(f'{" " * 32}if (format_properties_chain != nullptr) {{\n')
                    out.extend(self.GetTypesPrecondition([formatProps], 36))
                    out.append(f'{" " * 40}format_properties_chain->{formatProps[2:]}.sType = {structDef.sType};\n')
                    out.append(f'{" " * 40}format_properties_chain->{formatProps[2:]}.pNext = nullptr;\n')
                    out.append(f'{" " * 40}*ppnext = &format_properties_chain->{formatProps[2:]};\n')
                    out.append(f'{" " * 40}ppnext = &format_properties_chain->{formatProps[2:]}.pNext;\n')
                    out.append(f'{" " * 36}}}\n')
                    out.append(f'{" " * 32}}}\n')
                    out.append(self.AddGuardFooter(structDef))
                out.append(f'{" " * 28}    return format_properties_chain;\n')
                out.append(f'{" " * 28}}},\n')

                out.append(f'{" " * 24}}},\n')
            out.append(f'{" " * 20}}};\n\n')

            # Permute profiles for each profile struct member value
            profiles = {'': []}
            for profileStruct in videoCodec.profiles.values():
                for profileStructMember in profileStruct.members.values():
                    newProfiles = {}
                    for profileStructMemberValue, profileStructMemberName in profileStructMember.values.items():
                        for profileName, profile in profiles.items():
                            # Only add video profile name suffix to the full descriptive name if not empty to avoid excess whitespace
                            newProfileName = profileName if profileStructMemberName == '' else f'{profileName} {profileStructMemberName}'
                            newProfiles[newProfileName] = profile + [{
                                "struct": profileStruct.name,
                                "member": profileStructMember.name,
                                "value": profileStructMemberValue
                            }]
                    profiles = newProfiles

            for profileName, profile in profiles.items():
                out.append(f'{" " * 20}add_profile(profile_base_name + "{profileName}", profile_info,\n')
                out.append(f'{" " * 20}            create_profile_info_chain, create_capabilities_chain,\n')
                out.append(f'{" " * 20}            create_format_properties_chain_list,\n')
                out.append(f'{" " * 20}            [](AppVideoProfile& profile) {{\n')
                for profileStruct in videoCodec.profiles:
                    structDef = self.vk.structs[profileStruct]
                    out.append(self.AddGuardHeader(structDef))
                    for elem in profile:
                        if elem['struct'] == profileStruct:
                            out.append(f'{" " * 24}profile.profile_info_chain->{elem["struct"][2:]}.{elem["member"]} = {elem["value"]};\n')
                    out.append(self.AddGuardFooter(structDef))
                out.append(f'{" " * 20}}});\n')

            out.append(f'{" " * 16}}}\n')
            out.append(f'{" " * 12}}}\n')
            out.append(f'{" " * 8}}}\n')
            out.append(f'{" " * 4}}}\n')

        out.append('    return result;\n')
        out.append('}\n\n')

        return out


    # finds all the ranges of formats from core (1.0), core versions (1.1+), and extensions
    def findFormatRanges(self):
        min_val = 2**32
        prev_field = None
        max_val = 0
        for f in self.vk.enums['VkFormat'].fields:
            if f.value is None:
                continue
            if prev_field is not None and f.value != prev_field.value + 1:
                for ext in prev_field.extensions:
                    if self.vk.extensions[ext].promotedTo is not None:
                        self.format_ranges.append(VulkanFormatRange(self.vk.extensions[ext].promotedTo.replace("VK_", "VK_API_"), [], min_val, max_val))
                        break
                # only bother with the first extension
                self.format_ranges.append(VulkanFormatRange(0, prev_field.extensions, min_val, max_val))
                min_val = 2**32
                max_val = 0
            min_val = min(min_val, f.value)
            max_val = max(max_val, f.value)

            prev_field = f

        for ext in prev_field.extensions:
            if self.vk.extensions[ext].promotedTo is not None:
                self.format_ranges.append(VulkanFormatRange(self.vk.extensions[ext].promotedTo.replace("VK_", "VK_API_"), [], min_val, max_val))
                break

        self.format_ranges.append(VulkanFormatRange(0, prev_field.extensions, min_val, max_val))

    def findAllTypesToGen(self, initial_type_set):
        out_set = set()
        current_set = initial_type_set
        while len(current_set) > 0:
            out_set.update(current_set)
            next_set = set()

            for current_item in current_set:
                if current_item in self.vk.structs:
                    for member in self.vk.structs[current_item].members:
                        if member.type not in out_set and member.name not in NAMES_TO_IGNORE:
                            next_set.add(member.type)

            current_set = next_set
        return out_set

    def AddGuardHeader(self,obj):
        if obj is not None and obj.protect is not None:
            return f'#ifdef {obj.protect}\n'
        else:
            return ''


    def AddGuardFooter(self,obj):
        if obj is not None and obj.protect is not None:
            return f'#endif  // {obj.protect}\n'
        else:
            return ''

    def PrintEnumToString(self,enum):
        out = []
        out.append(self.AddGuardHeader(enum))
        out.append(f'std::string {enum.name}String({enum.name} value) {{\n')
        out.append('    switch (value) {\n')
        for v in enum.fields:
            out.append(f'        case ({v.name}): return "{v.name[3:]}";\n')
        out.append(f'        default: return std::string("UNKNOWN_{enum.name}_value") + std::to_string(value);\n')
        out.append('    }\n}\n')
        out.append(self.AddGuardFooter(enum))
        return out


    def PrintEnum(self,enum):
        out = []
        out.append(self.AddGuardHeader(enum))
        out.append(f'''void Dump{enum.name}(Printer &p, std::string name, {enum.name} value) {{
    if (p.Type() == OutputType::json)
        p.PrintKeyString(name, std::string("VK_") + {enum.name}String(value));
    else
        p.PrintKeyString(name, {enum.name}String(value));
}}
''')
        out.append(self.AddGuardFooter(enum))
        return out


    def PrintGetFlagStrings(self,name, bitmask):
        out = []
        out.append(f'std::vector<const char *> {name}GetStrings({name} value) {{\n')
        out.append('    std::vector<const char *> strings;\n')
        # If a bitmask contains a field whose value is zero, we want to support printing the correct bitflag
        # Otherwise, use "None" for when there are not bits set in the bitmask
        if bitmask.flags[0].value != 0:
            out.append('    if (value == 0) { strings.push_back("None"); return strings; }\n')
        else:
            out.append(f'    if (value == 0) {{ strings.push_back("{bitmask.flags[0].name[3:]}"); return strings; }}\n')
        for v in bitmask.flags:
            # only check single-bit flags
            if v.value != 0 and (v.value & (v.value - 1)) == 0:
                out.append(f'    if ({v.name} & value) strings.push_back("{v.name[3:]}");\n')
        out.append('    return strings;\n}\n')
        return out


    def PrintFlags(self, bitmask, name):
        out = []
        out.append(f'void Dump{name}(Printer &p, std::string name, {name} value) {{\n')
        out.append(f'''    if (static_cast<{bitmask.name}>(value) == 0) {{
        ArrayWrapper arr(p, name, 0);
        if (p.Type() != OutputType::json && p.Type() != OutputType::vkconfig_output)
            p.SetAsType().PrintString("None");
        return;
    }}
    auto strings = {bitmask.name}GetStrings(static_cast<{bitmask.name}>(value));
    ArrayWrapper arr(p, name, strings.size());
    for(auto& str : strings){{
        if (p.Type() == OutputType::json)
            p.SetAsType().PrintString(std::string("VK_") + str);
        else
            p.SetAsType().PrintString(str);
    }}
}}
''')
        return out


    def PrintFlagBits(self, bitmask):
        return [f'''void Dump{bitmask.name}(Printer &p, std::string name, {bitmask.name} value) {{
    auto strings = {bitmask.name}GetStrings(value);
    if (strings.size() > 0) {{
        if (p.Type() == OutputType::json)
            p.PrintKeyString(name, std::string("VK_") + strings.at(0));
        else
            p.PrintKeyString(name, strings.at(0));
    }}
}}
''']


    def PrintBitMask(self,bitmask, name):
        out = []
        out.extend(self.PrintGetFlagStrings(bitmask.name, bitmask))
        out.append(self.AddGuardHeader(bitmask))
        out.extend(self.PrintFlags(bitmask, name))
        out.extend(self.PrintFlagBits(bitmask))
        out.append(self.AddGuardFooter(bitmask))
        out.append('\n')
        return out


    def PrintBitMaskToString(self, bitmask, name):
        out = []
        out.append(self.AddGuardHeader(bitmask))
        out.append(f'std::string {name}String({name} value) {{\n')
        out.append('    std::string out;\n')
        out.append('    bool is_first = true;\n')
        for v in bitmask.flags:
            out.append(f'    if ({v.name} & value) {{\n')
            out.append('        if (is_first) { is_first = false; } else { out += " | "; }\n')
            out.append(f'        out += "{str(v.name)[3:]}";\n')
            out.append('    }\n')
        out.append('    return out;\n')
        out.append('}\n')
        out.append(self.AddGuardFooter(bitmask))
        return out


    def PrintStructure(self,struct, declare_only):
        if len(struct.members) == 0:
            return []
        out = []
        out.append(self.AddGuardHeader(struct))
        max_key_len = 0
        for v in struct.members:
            if (v.type in PREDEFINED_TYPES or v.type in STRUCT_BLACKLIST) and (v.length is None or v.type in ['char'] or v.fixedSizeArray[0] in ['VK_UUID_SIZE', 'VK_LUID_SIZE']):
                max_key_len = max(max_key_len, len(v.name))
        out.append(f'void Dump{struct.name}(Printer &p, std::string name, const {struct.name} &obj)')
        if declare_only:
            out.append(';\n')
            out.append(self.AddGuardFooter(struct))
            return out
        out.append(' {\n')
        if struct.name == 'VkPhysicalDeviceLimits':
            out.append('    if (p.Type() == OutputType::json)\n')
            out.append('        p.ObjectStart("limits");\n')
            out.append('    else\n')
            out.append('        p.SetSubHeader().ObjectStart(name);\n')
        elif struct.name == 'VkPhysicalDeviceSparseProperties':
            out.append('    if (p.Type() == OutputType::json)\n')
            out.append('        p.ObjectStart("sparseProperties");\n')
            out.append('    else\n')
            out.append('        p.SetSubHeader().ObjectStart(name);\n')
        else:
            out.append('    ObjectWrapper object{p, name};\n')
        if max_key_len > 0:
            out.append(f'    p.SetMinKeyWidth({max_key_len});\n')
        for v in struct.members:
            # strings
            if v.type == 'char':
                if v.pointer == True:
                    out.append(f'    if (obj.{v.name} == nullptr) {{')
                    out.append(f'        p.PrintKeyString("{v.name}", "NULL");\n')
                    out.append('    } else {')
                out.append(f'    p.PrintKeyString("{v.name}", obj.{v.name});\n')
                if v.pointer == True:
                        out.append('    }')
            # arrays
            elif v.length is not None:
                # uuid's
                if v.type == 'uint8_t' and (v.fixedSizeArray[0] == 'VK_LUID_SIZE' or v.fixedSizeArray[0] == 'VK_UUID_SIZE'):  # VK_UUID_SIZE
                    if v.fixedSizeArray[0] == 'VK_LUID_SIZE':
                        out.append('    if (obj.deviceLUIDValid) { // special case\n')
                    out.append(f'    p.PrintKeyValue("{v.name}", obj.{v.name});\n')
                    if v.fixedSizeArray[0] == 'VK_LUID_SIZE':
                        out.append('    }\n')
                elif struct.name == 'VkQueueFamilyGlobalPriorityProperties' and v.name == 'priorities':
                    out.append(f'    ArrayWrapper arr(p,"{v.name}", obj.priorityCount);\n')
                    out.append('    for (uint32_t i = 0; i < obj.priorityCount; i++) {\n')
                    out.append('       if (p.Type() == OutputType::json)\n')
                    out.append('           p.PrintString(std::string("VK_") + VkQueueGlobalPriorityString(obj.priorities[i]));\n')
                    out.append('       else\n')
                    out.append('           p.PrintString(VkQueueGlobalPriorityString(obj.priorities[i]));\n')
                    out.append('    }\n')
                elif len(v.fixedSizeArray) == 2:
                    out.append(f'    {{\n        ArrayWrapper arr(p,"{v.name}", ' + v.fixedSizeArray[0] + ');\n')
                    out.append(f'        for (uint32_t i = 0; i < {v.fixedSizeArray[0]}; i++) {{\n')
                    out.append(f'           for (uint32_t j = 0; j < {v.fixedSizeArray[1]}; j++) {{\n')
                    out.append(f'                p.PrintElement(obj.{v.name}[i][j]); }} }}\n')
                    out.append('    }\n')
                elif len(v.fixedSizeArray) == 1:
                    out.append(f'    {{\n        ArrayWrapper arr(p,"{v.name}", ' + v.fixedSizeArray[0] + ');\n')
                    out.append(f'        for (uint32_t i = 0; i < {v.fixedSizeArray[0]}; i++) {{ p.PrintElement(obj.{v.name}[i]); }}\n')
                    out.append('    }\n')
                else:  # dynamic array length based on other member
                    out.append(f'    if (obj.{v.length} == 0 || obj.{v.name} == nullptr) {{\n')
                    out.append(f'        p.PrintKeyString("{v.name}", "NULL");\n')
                    out.append('    } else {\n')
                    out.append(f'        ArrayWrapper arr(p,"{v.name}", obj.{v.length});\n')
                    out.append(f'        for (uint32_t i = 0; i < obj.{v.length}; i++) {{\n')
                    out.append(f'            Dump{v.type}(p, std::to_string(i), obj.{v.name}[i]);\n')
                    out.append('        }\n')
                    out.append('    }\n')
            elif v.type == 'VkBool32':
                out.append(f'    p.PrintKeyBool("{v.name}", static_cast<bool>(obj.{v.name}));\n')
            elif v.type == 'uint8_t':
                out.append(f'    p.PrintKeyValue("{v.name}", static_cast<uint32_t>(obj.{v.name}));\n')
            elif v.type == 'VkDeviceSize' or (v.type == 'uint32_t' and v.name in ['vendorID', 'deviceID']):
                out.append(f'    p.PrintKeyValue("{v.name}", to_hex_str(p, obj.{v.name}));\n')
            elif v.type in PREDEFINED_TYPES:
                out.append(f'    p.PrintKeyValue("{v.name}", obj.{v.name});\n')
            elif v.name not in NAMES_TO_IGNORE:
                # if it is an enum/flag/bitmask
                if v.type in ['VkFormatFeatureFlags', 'VkFormatFeatureFlags2']:
                    out.append('    p.SetOpenDetails();\n') # special case so that feature flags are open in html output
                out.append(f'    Dump{v.type}(p, "{v.name}", obj.{v.name});\n')

        if struct.name in ['VkPhysicalDeviceLimits', 'VkPhysicalDeviceSparseProperties']:
            out.append('    p.ObjectEnd();\n')
        out.append('}\n')

        out.append(self.AddGuardFooter(struct))
        return out


    def PrintStructShort(self,struct):
        out = []
        out.append(self.AddGuardHeader(struct))
        out.append(f'std::ostream &operator<<(std::ostream &o, {struct.name} &obj) {{\n')
        out.append('    return o << "(" << ')

        first = True
        for v in struct.members:
            if first:
                first = False
                out.append(f'obj.{v.name} << ')
            else:
                out.append(f'\',\' << obj.{v.name} << ')
        out.append('")";\n')
        out.append('}\n')
        out.append(self.AddGuardFooter(struct))
        return out

    def PrintChainStruct(self, listName, structs_to_print, chain_details):
        version_desc = ''
        if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]:
            version_desc = 'gpu.api_version'
        else:
            version_desc = 'inst.instance_version'

        out = []

        # use default constructor and delete copy & move operators
        out.append(f'''struct {listName}_chain {{
    {listName}_chain() = default;
    {listName}_chain(const {listName}_chain &) = delete;
    {listName}_chain& operator=(const {listName}_chain &) = delete;
    {listName}_chain({listName}_chain &&) = delete;
    {listName}_chain& operator=({listName}_chain &&) = delete;
''')

        out.append('    void* start_of_chain = nullptr;\n')
        for s in structs_to_print:
            if s in STRUCT_BLACKLIST:
                continue
            struct = self.vk.structs[s]
            out.append(self.AddGuardHeader(struct))
            if struct.sType is not None:
                out.append(f'    {struct.name} {struct.name[2:]}{{}};\n')
                # Specific versions of drivers have an incorrect definition of the size of these structs.
                # We need to artificially pad the structure it just so the driver doesn't write out of bounds and
                # into other structures that are adjacent. This bug comes from the in-development version of
                # the extension having a larger size than the final version, so older drivers try to write to
                # members which don't exist.
                if struct.name in ['VkPhysicalDeviceShaderIntegerDotProductFeatures', 'VkPhysicalDeviceHostImageCopyFeaturesEXT']:
                    out.append(f'    char {struct.name}_padding[64];\n')
                for member in struct.members:
                    if member.length is not None and len(member.fixedSizeArray) == 0:
                        out.append(f'    std::vector<{member.type}> {struct.name}_{member.name};\n')
            out.append(self.AddGuardFooter(struct))
        out.append('    void initialize_chain(')
        args = []
        if chain_details.get('type') in [EXTENSION_TYPE_INSTANCE, EXTENSION_TYPE_BOTH]:
            args.append('AppInstance &inst')
        if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]:
            args.append('AppGpu &gpu')
        if chain_details.get('can_show_promoted_structs'):
            args.append('bool show_promoted_structs')
        out.append(f'{", ".join(args)}) noexcept {{\n')
        for s in structs_to_print:
            if s in STRUCT_BLACKLIST:
                continue
            struct = self.vk.structs[s]

            out.append(self.AddGuardHeader(struct))
            out.append(f'        {struct.name[2:]}.sType = {struct.sType};\n')
            out.append(self.AddGuardFooter(struct))

        out.append('        std::vector<VkBaseOutStructure*> chain_members{};\n')
        for s in structs_to_print:
            if s in STRUCT_BLACKLIST:
                continue
            struct = self.vk.structs[s]
            out.append(self.AddGuardHeader(struct))

            has_version = struct.version is not None
            has_extNameStr = len(struct.extensions) > 0 or len(struct.aliases) > 0
            if has_version or has_extNameStr:
                out.append('        if (')
                has_printed_condition = False
                if has_extNameStr:
                    for ext in struct.extensions:
                        if has_printed_condition:
                            out.append('\n         || ')
                        else:
                            has_printed_condition = True
                            if has_version:
                                out.append('(')
                        if self.vk.extensions[ext].device:
                            out.append(f'gpu.CheckPhysicalDeviceExtensionIncluded({self.vk.extensions[ext].nameString})')
                        elif self.vk.extensions[ext].instance:
                            out.append(f'inst.CheckExtensionEnabled({self.vk.extensions[ext].nameString})')
                        else:
                            assert False, 'Should never get here'
                if has_version:
                    str_show_promoted_structs = '|| show_promoted_structs' if chain_details.get('can_show_promoted_structs') else ''
                    if struct.name in STRUCT_1_1_LIST:
                        out.append(f'{version_desc} == {struct.version.nameApi} {str_show_promoted_structs}')
                    elif has_printed_condition:
                        out.append(f')\n            && ({version_desc} < {struct.version.nameApi} {str_show_promoted_structs})')
                    else:
                        out.append(f'({version_desc} >= {struct.version.nameApi})')
                out.append(')\n            ')
            else:
                out.append('        ')
            out.append(f'chain_members.push_back(reinterpret_cast<VkBaseOutStructure*>(&{struct.name[2:]}));\n')
            out.append(self.AddGuardFooter(struct))
        chain_param_list = []
        chain_arg_list = []
        if chain_details.get('type') in [EXTENSION_TYPE_INSTANCE, EXTENSION_TYPE_BOTH]:
            chain_param_list.append('AppInstance &inst')
            chain_arg_list.append('inst')
        if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]:
            chain_param_list.append('AppGpu &gpu')
            chain_arg_list.append('gpu')
        if chain_details.get('can_show_promoted_structs'):
            chain_param_list.append('bool show_promoted_structs')
            chain_arg_list.append('show_promoted_structs')

        out.append(f'''
        if (!chain_members.empty()) {{
            for(size_t i = 0; i < chain_members.size() - 1; i++){{
                chain_members[i]->pNext = chain_members[i + 1];
            }}
            start_of_chain = chain_members[0];
        }}
    }}
}};
void setup_{listName}_chain({chain_details['extends']}& start, std::unique_ptr<{listName}_chain>& chain, {','.join(chain_param_list)}){{
    chain = std::unique_ptr<{listName}_chain>(new {listName}_chain());
    chain->initialize_chain({','.join(chain_arg_list)});
    start.pNext = chain->start_of_chain;
}};
''')
        if chain_details.get('print_iterator'):
            out.append('\n')
            out.append(f'void chain_iterator_{listName}(')
            args = ['Printer &p']
            if chain_details.get('type') in [EXTENSION_TYPE_INSTANCE, EXTENSION_TYPE_BOTH]:
                args.append('AppInstance &inst')
            if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]:
                args.append('AppGpu &gpu')
            if chain_details.get('can_show_promoted_structs'):
                args.append('bool show_promoted_structs')
            args.append('const void * place')
            out.append(f'{", ".join(args)}) {{\n')
            out.append('    while (place) {\n')
            out.append('        const VkBaseOutStructure *structure = (const VkBaseOutStructure *)place;\n')
            out.append('        p.SetSubHeader();\n')

            for s in structs_to_print:
                if s in STRUCT_BLACKLIST:
                    continue
                struct = self.vk.structs[s]

                out.append(self.AddGuardHeader(struct))
                out.append(f'        if (structure->sType == {struct.sType}')
                if struct.name in PORTABILITY_STRUCTS:
                    out.append(' && p.Type() != OutputType::json')
                out.append(') {\n')
                out.append(f'            const {struct.name}* props = (const {struct.name}*)structure;\n')
                out.extend(self.PrintStructNameDecisionLogic(struct, version_desc, chain_details.get('can_show_promoted_structs')))
                out.append('            p.AddNewline();\n')
                out.append('        }\n')
                out.append(self.AddGuardFooter(struct))
            out.append('        place = structure->pNext;\n')
            out.append('    }\n')
            out.append('}\n')

        out.append('\n')
        out.append(f'bool prepare_{listName}_twocall_chain_vectors(std::unique_ptr<{listName}_chain>& chain) {{\n')
        out.append('    (void)chain;\n')
        is_twocall = False
        for s in structs_to_print:
            if s in STRUCT_BLACKLIST:
                continue
            struct = self.vk.structs[s]
            has_length = False
            for member in struct.members:
                if member.length is not None and len(member.fixedSizeArray) == 0:
                    has_length = True
            if not has_length:
                continue
            out.append(self.AddGuardHeader(struct))
            for member in struct.members:
                if member.length is not None and len(member.fixedSizeArray) == 0:
                    out.append(f'    chain->{struct.name}_{member.name}.resize(chain->{struct.name[2:]}.{member.length});\n')
                    out.append(f'    chain->{struct.name[2:]}.{member.name} = chain->{struct.name}_{member.name}.data();\n')
            out.append(self.AddGuardFooter(struct))
            is_twocall = True
        out.append(f'    return {"true" if is_twocall else "false"};\n')
        out.append('}\n')

        return out

    def GetStructCheckStringForMatchingExtension(self, struct, structName):
        for ext_name in struct.extensions:
            ext = self.vk.extensions[ext_name]
            vendor = ext.name.split('_')[1]
            if structName.endswith(vendor):
                if ext.device:
                    return f'gpu.CheckPhysicalDeviceExtensionIncluded({ext.nameString})'
                elif ext.instance:
                    return f'inst.CheckExtensionEnabled({ext.nameString})'
        return None

    # Function is complex because it has to do the following:
    # Always print the struct with the most appropriate name given the gpu api version & enabled instance/device extensions
    # Print struct aliases when --show-promoted-structs is set
    # Not let alias printing duplicate the most appropriate name
    def PrintStructNameDecisionLogic(self, struct, version_desc, can_show_promoted_structs):
        out = []
        out.append(f'{" " * 12}const char* name = ')
        # Get a list of all the conditions to check and the type name to use
        check_list = []
        if struct.version is not None:
            check_list.append([f'{version_desc} >= {struct.version.nameApi}', struct.name])
        else:
            check_list.append([f'{self.GetStructCheckStringForMatchingExtension(struct, struct.name)}', struct.name])

        for alias in struct.aliases:
            ext_str = self.GetStructCheckStringForMatchingExtension(struct, alias)
            if ext_str is not None:
                check_list.append([f'{self.GetStructCheckStringForMatchingExtension(struct, alias)}', alias])
        end_parens = ''
        # Turn the conditions into a nested ternary condition -
        for check in check_list:
            if check == check_list[-1]:
                out.append( f'"{check[1]}"')
            else:
                out.append( f'{check[0]} ? "{check[1]}" : (')
                end_parens += ')'
        out.append(f'{end_parens};\n')
        out.append(f'{" " * 12}Dump{struct.name}(p, name, *props);\n')
        if not can_show_promoted_structs:
            return out
        for alias in struct.aliases:
            ext_str = self.GetStructCheckStringForMatchingExtension(struct, alias)
            if ext_str is not None:
                out.append(f'{" " * 12}if (show_promoted_structs && strcmp(name, "{alias}") != 0 && {ext_str}) {{\n')
                out.append(f'{" " * 16}p.AddNewline();\n')
                out.append(f'{" " * 16}p.SetSubHeader();\n')
                out.append(f'{" " * 16}Dump{struct.name}(p, "{alias}", *props);\n')
                out.append(f'{" " * 12}}}\n')
        return out

    def PrintStructComparisonForwardDecl(self,structure):
        out = []
        out.append(f'bool operator==(const {structure.name} & a, const {structure.name} b);\n')
        return out


    def PrintStructComparison(self,structure):
        out = []
        out.append(f'bool operator==(const {structure.name} & a, const {structure.name} b) {{\n')
        out.append('    return ')
        is_first = True
        for m in structure.members:
            if m.name not in NAMES_TO_IGNORE:
                if not is_first:
                    out.append('\n        && ')
                else:
                    is_first = False
                out.append(f'a.{m.name} == b.{m.name}')
        out.append(';\n')
        out.append('}\n')
        return out

class VulkanFormatRange:
    def __init__(self, min_inst_version, extensions, first, last):
        self.minimum_instance_version = min_inst_version
        self.extensions = extensions
        self.first_format = first
        self.last_format = last
