| #!/usr/bin/python3 |
| # |
| # Copyright (c) 2019-2022 Valve Corporation |
| # Copyright (c) 2019-2022 LunarG, Inc. |
| # Copyright (c) 2019-2022 Google Inc. |
| # Copyright (c) 2023-2023 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> |
| |
| import re |
| import sys |
| import operator |
| from collections import OrderedDict |
| import generator as gen |
| from common_codegen import GetFeatureProtect |
| from generator import GeneratorOptions, OutputGenerator |
| |
| LICENSE_HEADER = ''' |
| /* |
| * Copyright (c) 2019-2022 The Khronos Group Inc. |
| * Copyright (c) 2019-2022 Valve Corporation |
| * Copyright (c) 2019-2022 LunarG, Inc. |
| * Copyright (c) 2023-2023 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', |
| 'VkSurfacePresentScalingCapabilitiesEXT', 'VkSurfacePresentModeCompatibilityEXT', 'VkPhysicalDeviceHostImageCopyProperties'] |
| ENUMS_TO_GEN = ['VkResult', 'VkFormat', 'VkPresentModeKHR', |
| 'VkPhysicalDeviceType', 'VkImageTiling'] |
| FLAGS_TO_GEN = ['VkSurfaceTransformFlagsKHR', 'VkCompositeAlphaFlagsKHR', 'VkSurfaceCounterFlagsEXT', 'VkQueueFlags', |
| 'VkDeviceGroupPresentModeFlagsKHR', 'VkFormatFeatureFlags', 'VkFormatFeatureFlags2', 'VkMemoryPropertyFlags', 'VkMemoryHeapFlags'] |
| FLAG_STRINGS_TO_GEN = ['VkQueueFlags'] |
| |
| STRUCT_SHORT_VERSIONS_TO_GEN = ['VkExtent3D'] |
| |
| STRUCT_COMPARISONS_TO_GEN = ['VkSurfaceFormatKHR', 'VkSurfaceFormat2KHR', 'VkSurfaceCapabilitiesKHR', |
| 'VkSurfaceCapabilities2KHR', 'VkSurfaceCapabilities2EXT'] |
| # don't generate these structures |
| STRUCT_BLACKLIST = ['VkVideoProfileListInfoKHR', 'VkVideoProfileInfoKHR', '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', 'stdProfileIdc'] |
| |
| 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, |
| 'holder_type': 'VkPhysicalDeviceProperties2', |
| 'print_iterator': True, |
| 'can_show_promoted_structs': True}), |
| ('phys_device_mem_props2', |
| {'extends': 'VkPhysicalDeviceMemoryProperties2', |
| 'type': EXTENSION_TYPE_DEVICE, |
| 'holder_type':'VkPhysicalDeviceMemoryProperties2', |
| 'print_iterator': False, |
| 'can_show_promoted_structs': False}), |
| ('phys_device_features2', |
| {'extends': 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo', |
| 'type': EXTENSION_TYPE_DEVICE, |
| 'holder_type': 'VkPhysicalDeviceFeatures2', |
| 'print_iterator': True, |
| 'can_show_promoted_structs': True}), |
| ('surface_capabilities2', |
| {'extends': 'VkSurfaceCapabilities2KHR', |
| 'type': EXTENSION_TYPE_BOTH, |
| 'holder_type': 'VkSurfaceCapabilities2KHR', |
| 'print_iterator': True, |
| 'can_show_promoted_structs': False, |
| 'exclude': ['VkSurfacePresentScalingCapabilitiesEXT', 'VkSurfacePresentModeCompatibilityEXT']}), |
| ('format_properties2', |
| {'extends': 'VkFormatProperties2', |
| 'type': EXTENSION_TYPE_DEVICE, |
| 'holder_type':'VkFormatProperties2', |
| 'print_iterator': True, |
| 'can_show_promoted_structs': False}), |
| ('queue_properties2', |
| {'extends': 'VkQueueFamilyProperties2', |
| 'type': EXTENSION_TYPE_DEVICE, |
| 'holder_type': 'VkQueueFamilyProperties2', |
| 'print_iterator': True, |
| 'can_show_promoted_structs': False}) |
| )) |
| class VulkanInfoGeneratorOptions(GeneratorOptions): |
| def __init__(self, |
| conventions=None, |
| input=None, |
| filename=None, |
| directory='.', |
| genpath = None, |
| apiname=None, |
| profile=None, |
| versions='.*', |
| emitversions='.*', |
| defaultExtensions=None, |
| addExtensions=None, |
| removeExtensions=None, |
| emitExtensions=None, |
| sortProcedure=None, |
| prefixText='', |
| genFuncPointers=True, |
| protectFile=True, |
| protectFeature=True, |
| protectProto=None, |
| protectProtoStr=None, |
| apicall='', |
| apientry='', |
| apientryp='', |
| indentFuncProto=True, |
| indentFuncPointer=False, |
| alignFuncParam=0, |
| expandEnumerants=True, |
| ): |
| GeneratorOptions.__init__(self, |
| conventions = conventions, |
| filename = filename, |
| directory = directory, |
| genpath = genpath, |
| apiname = apiname, |
| profile = profile, |
| versions = versions, |
| emitversions = emitversions, |
| defaultExtensions = defaultExtensions, |
| addExtensions = addExtensions, |
| removeExtensions = removeExtensions, |
| emitExtensions = emitExtensions, |
| sortProcedure = sortProcedure) |
| self.input = input |
| self.prefixText = prefixText |
| self.genFuncPointers = genFuncPointers |
| self.protectFile = protectFile |
| self.protectFeature = protectFeature |
| self.protectProto = protectProto |
| self.protectProtoStr = protectProtoStr |
| self.apicall = apicall |
| self.apientry = apientry |
| self.apientryp = apientryp |
| self.indentFuncProto = indentFuncProto |
| self.indentFuncPointer = indentFuncPointer |
| self.alignFuncParam = alignFuncParam |
| |
| # VulkanInfoGenerator - subclass of OutputGenerator. |
| # Generates a vulkan info output helper function |
| |
| |
| class VulkanInfoGenerator(OutputGenerator): |
| |
| def __init__(self, |
| errFile=sys.stderr, |
| warnFile=sys.stderr, |
| diagFile=sys.stdout): |
| OutputGenerator.__init__(self, errFile, warnFile, diagFile) |
| |
| self.constants = OrderedDict() |
| |
| self.types_to_gen = set() |
| |
| self.extension_sets = OrderedDict() |
| for ext_cat in EXTENSION_CATEGORIES.keys(): |
| self.extension_sets[ext_cat] = set() |
| |
| self.enums = [] |
| self.flags = [] |
| self.bitmasks = [] |
| self.format_ranges = [] |
| self.all_structures = [] |
| self.aliases = OrderedDict() |
| |
| self.extFuncs = OrderedDict() |
| self.extTypes = OrderedDict() |
| |
| self.vendor_abbreviations = [] |
| self.vulkan_versions = [] |
| |
| def beginFile(self, genOpts): |
| gen.OutputGenerator.beginFile(self, genOpts) |
| |
| for node in self.registry.reg.findall('enums'): |
| if node.get('name') == 'API Constants': |
| for item in node.findall('enum'): |
| self.constants[item.get('name')] = item.get('value') |
| |
| for node in self.registry.reg.find('extensions').findall('extension'): |
| ext = VulkanExtension(node) |
| for item in ext.vktypes: |
| if item not in self.extTypes: |
| self.extTypes[item] = [] |
| self.extTypes[item].append(ext) |
| for item in ext.vkfuncs: |
| self.extFuncs[item] = ext |
| |
| # need list of venders to blacklist vendor extensions |
| for tag in self.registry.reg.find('tags'): |
| if tag.get('name') not in ['KHR', 'EXT']: |
| self.vendor_abbreviations.append('_' + tag.get('name')) |
| |
| for ver in self.registry.reg.findall('feature'): |
| self.vulkan_versions.append(VulkanVersion(ver)) |
| |
| def endFile(self): |
| self.findFormatRanges() |
| |
| # gather the types that are needed to generate |
| types_to_gen = set() |
| for s in ENUMS_TO_GEN: |
| types_to_gen.add(s) |
| |
| for f in FLAGS_TO_GEN: |
| types_to_gen.add(f) |
| |
| types_to_gen.update( |
| GatherTypesToGen(self.all_structures, STRUCTURES_TO_GEN)) |
| for key, info in EXTENSION_CATEGORIES.items(): |
| types_to_gen.update( |
| GatherTypesToGen(self.all_structures, self.extension_sets[key], info.get('exclude'))) |
| types_to_gen = sorted(types_to_gen) |
| |
| names_of_structures_to_gen = set() |
| for s in self.all_structures: |
| if s.name in types_to_gen: |
| names_of_structures_to_gen.add(s.name) |
| names_of_structures_to_gen = sorted(names_of_structures_to_gen) |
| |
| structs_to_comp = set() |
| for s in STRUCT_COMPARISONS_TO_GEN: |
| structs_to_comp.add(s) |
| structs_to_comp.update( |
| GatherTypesToGen(self.all_structures, STRUCT_COMPARISONS_TO_GEN)) |
| |
| for key, value in self.extension_sets.items(): |
| self.extension_sets[key] = sorted(value) |
| |
| self.enums = sorted(self.enums, key=operator.attrgetter('name')) |
| self.flags = sorted(self.flags, key=operator.attrgetter('name')) |
| self.bitmasks = sorted(self.bitmasks, key=operator.attrgetter('name')) |
| self.all_structures = sorted(self.all_structures, key=operator.attrgetter('name')) |
| |
| # print the types gathered |
| out = '' |
| out += LICENSE_HEADER + '\n' |
| out += '#include "vulkaninfo.h"\n' |
| out += '#include "outputprinter.h"\n' |
| out += CUSTOM_FORMATTERS |
| |
| for enum in (e for e in self.enums if e.name in types_to_gen): |
| out += PrintEnumToString(enum, self) |
| out += PrintEnum(enum, self) |
| |
| for flag in self.flags: |
| if flag.name in types_to_gen: |
| for bitmask in (b for b in self.bitmasks if b.name == flag.enum): |
| out += PrintBitMask(bitmask, flag.name, self) |
| |
| if flag.name in FLAG_STRINGS_TO_GEN: |
| for bitmask in (b for b in self.bitmasks if b.name == flag.enum): |
| out += PrintBitMaskToString(bitmask, flag.name, self) |
| |
| for s in (x for x in self.all_structures if x.name in types_to_gen and x.name not in STRUCT_BLACKLIST): |
| out += PrintStructure(s) |
| |
| for key, value in EXTENSION_CATEGORIES.items(): |
| out += PrintChainStruct(key, self.extension_sets[key], self.all_structures, value, self.extTypes, self.aliases, self.vulkan_versions) |
| |
| for s in (x for x in self.all_structures if x.name in structs_to_comp): |
| out += PrintStructComparisonForwardDecl(s) |
| for s in (x for x in self.all_structures if x.name in structs_to_comp): |
| out += PrintStructComparison(s) |
| for s in (x for x in self.all_structures if x.name in STRUCT_SHORT_VERSIONS_TO_GEN): |
| out += PrintStructShort(s) |
| |
| out += 'auto format_ranges = std::array{\n' |
| for f in self.format_ranges: |
| out += f' FormatRange{{{f.minimum_instance_version}, {f.extension_name if f.extension_name is not None else "nullptr"}, ' |
| out += f'static_cast<VkFormat>({f.first_format}), static_cast<VkFormat>({f.last_format})}},\n' |
| out += '};\n' |
| |
| |
| gen.write(out, file=self.outFile) |
| |
| gen.OutputGenerator.endFile(self) |
| |
| def genCmd(self, cmd, name, alias): |
| gen.OutputGenerator.genCmd(self, cmd, name, alias) |
| |
| # These are actually constants |
| def genEnum(self, enuminfo, name, alias): |
| gen.OutputGenerator.genEnum(self, enuminfo, name, alias) |
| |
| # These are actually enums |
| def genGroup(self, groupinfo, groupName, alias): |
| gen.OutputGenerator.genGroup(self, groupinfo, groupName, alias) |
| |
| if alias is not None: |
| if alias in self.aliases.keys(): |
| self.aliases[alias].append(groupName) |
| else: |
| self.aliases[alias] = [groupName, ] |
| return |
| |
| if groupinfo.elem.get('type') == 'bitmask': |
| self.bitmasks.append(VulkanBitmask(groupinfo.elem)) |
| elif groupinfo.elem.get('type') == 'enum': |
| self.enums.append(VulkanEnum(groupinfo.elem)) |
| |
| def genType(self, typeinfo, name, alias): |
| gen.OutputGenerator.genType(self, typeinfo, name, alias) |
| |
| if alias is not None: |
| if alias in self.aliases.keys(): |
| self.aliases[alias].append(name) |
| else: |
| self.aliases[alias] = [name, ] |
| return |
| |
| if typeinfo.elem.get('category') == 'bitmask': |
| self.flags.append(VulkanFlags(typeinfo.elem)) |
| |
| if typeinfo.elem.get('category') == 'struct': |
| self.all_structures.append(VulkanStructure( |
| name, typeinfo.elem, self.constants, self.extTypes)) |
| |
| for vendor in self.vendor_abbreviations: |
| for node in typeinfo.elem.findall('member'): |
| if node.get('values') is not None: |
| if node.get('values').find(vendor) != -1: |
| return |
| |
| for key, value in EXTENSION_CATEGORIES.items(): |
| if str(typeinfo.elem.get('structextends')).find(value.get('extends')) != -1: |
| if value.get('exclude') is None or name not in value.get('exclude'): |
| self.extension_sets[key].add(name) |
| |
| # finds all the ranges of formats from core (1.0), core versions (1.1+), and extensions |
| def findFormatRanges(self): |
| for enums in self.registry.reg.findall('enums'): |
| if enums.get('name') == 'VkFormat': |
| min_val = 2**32 |
| max_val = 0 |
| for enum in enums.findall('enum'): |
| if enum.get('value') is None: |
| continue |
| value = int(enum.get('value')) |
| min_val = min(min_val, value) |
| max_val = max(max_val, value) |
| if min_val < 2**32 and max_val > 0: |
| self.format_ranges.append(VulkanFormatRange(0, None, min_val, max_val)) |
| |
| for feature in self.registry.reg.findall('feature'): |
| for require in feature.findall('require'): |
| comment = require.get('comment') |
| original_ext = None |
| if comment is not None and comment.find('Promoted from') >= 0: |
| # may need tweaking in the future - some ext names aren't just the upper case version |
| original_ext = comment.split(' ')[2].upper() + '_EXTENSION_NAME' |
| # insert an underscore before numbers in the name define |
| original_ext = re.sub(r'([A-Z])(\d+)', r'\1_\2', original_ext) |
| min_val = 2**32 |
| max_val = 0 |
| for enum in require.findall('enum'): |
| if enum.get('extends') == 'VkFormat': |
| value = CalcEnumValue(int(enum.get('extnumber')), int(enum.get('offset'))) |
| min_val = min(min_val, value) |
| max_val = max(max_val, value) |
| if min_val < 2**32 and max_val > 0: |
| self.format_ranges.append(VulkanFormatRange(feature.get('name').replace('_VERSION_', '_API_VERSION_'), None, min_val, max_val)) |
| # If the formats came from an extension, add a format range for that extension so it'll be printed if the ext is supported but not the core version |
| if original_ext is not None: |
| self.format_ranges.append(VulkanFormatRange(0, original_ext, min_val, max_val)) |
| |
| for extension in self.registry.reg.find('extensions').findall('extension'): |
| if not self.genOpts.apiname in extension.get('supported').split(','): |
| continue |
| |
| min_val = 2**32 |
| max_val = 0 |
| enum_name_string = '' |
| for require in extension.findall('require'): |
| for enum in require.findall('enum'): |
| if enum.get('value') is not None and enum.get('value').find(extension.get('name')): |
| enum_name_string = enum.get('name') |
| if enum.get('extends') == 'VkFormat': |
| if enum.get('offset') is None: |
| continue |
| value = CalcEnumValue(int(extension.get('number')), int(enum.get('offset'))) |
| min_val = min(min_val, value) |
| max_val = max(max_val, value) |
| if min_val < 2**32 and max_val > 0: |
| self.format_ranges.append(VulkanFormatRange(0, enum_name_string, min_val, max_val)) |
| |
| |
| |
| def GatherTypesToGen(structure_list, structures, exclude = None): |
| if exclude is None: |
| exclude = [] |
| types = set() |
| for s in structures: |
| types.add(s) |
| added_stuff = True # repeat until no new types are added |
| while added_stuff is True: |
| added_stuff = False |
| for s in structure_list: |
| if s.name in types: |
| for m in s.members: |
| if m.typeID not in PREDEFINED_TYPES and m.name not in NAMES_TO_IGNORE: |
| if m.typeID not in types: |
| if s.name not in exclude: |
| types.add(m.typeID) |
| added_stuff = True |
| return types |
| |
| |
| def GetExtension(name, generator): |
| if name in generator.extFuncs: |
| return generator.extFuncs[name] |
| elif name in generator.extTypes: |
| return generator.extTypes[name][0] |
| else: |
| return None |
| |
| |
| def AddGuardHeader(obj): |
| if obj is not None and obj.guard is not None: |
| return f'#ifdef {obj.guard}\n' |
| else: |
| return '' |
| |
| |
| def AddGuardFooter(obj): |
| if obj is not None and obj.guard is not None: |
| return f'#endif // {obj.guard}\n' |
| else: |
| return '' |
| |
| def CalcEnumValue(num, offset): |
| base = 1000000000 |
| block_size = 1000 |
| return base + (num - 1) * block_size + offset |
| |
| def PrintEnumToString(enum, generator): |
| out = '' |
| out += AddGuardHeader(GetExtension(enum.name, generator)) |
| out += f'std::string {enum.name}String({enum.name} value) {{\n' |
| out += ' switch (value) {\n' |
| for v in enum.options: |
| out += f' case ({v.name}): return "{v.name[3:]}";\n' |
| out += f' default: return std::string("UNKNOWN_{enum.name}_value") + std::to_string(value);\n' |
| out += ' }\n}\n' |
| out += AddGuardFooter(GetExtension(enum.name, generator)) |
| return out |
| |
| |
| def PrintEnum(enum, generator): |
| out = '' |
| out += AddGuardHeader(GetExtension(enum.name, generator)) |
| out += 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 += AddGuardFooter(GetExtension(enum.name, generator)) |
| return out |
| |
| |
| def PrintGetFlagStrings(name, bitmask): |
| out = '' |
| out += f'std::vector<const char *> {name}GetStrings({name} value) {{\n' |
| out += ' 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.options[0].value != 0: |
| out += ' if (value == 0) { strings.push_back("None"); return strings; }\n' |
| for v in bitmask.options: |
| # only check single-bit flags |
| if (v.value & (v.value - 1)) == 0: |
| out += f' if ({v.name} & value) strings.push_back("{v.name[3:]}");\n' |
| out += ' return strings;\n}\n' |
| return out |
| |
| |
| def PrintFlags(bitmask, name): |
| out = f'void Dump{name}(Printer &p, std::string name, {name} value) {{\n' |
| out += 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(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(bitmask, name, generator): |
| out = PrintGetFlagStrings(bitmask.name, bitmask) |
| out += AddGuardHeader(GetExtension(bitmask.name, generator)) |
| out += PrintFlags(bitmask, name) |
| out += PrintFlagBits(bitmask) |
| out += AddGuardFooter(GetExtension(bitmask.name, generator)) |
| out += '\n' |
| return out |
| |
| |
| def PrintBitMaskToString(bitmask, name, generator): |
| out = AddGuardHeader(GetExtension(bitmask.name, generator)) |
| out += f'std::string {name}String({name} value) {{\n' |
| out += ' std::string out;\n' |
| out += ' bool is_first = true;\n' |
| for v in bitmask.options: |
| out += f' if ({v.name} & value) {{\n' |
| out += ' if (is_first) { is_first = false; } else { out += " | "; }\n' |
| out += f' out += "{str(v.name)[3:]}";\n' |
| out += ' }\n' |
| out += ' return out;\n' |
| out += '}\n' |
| out += AddGuardFooter(GetExtension(bitmask.name, generator)) |
| return out |
| |
| |
| def PrintStructure(struct): |
| if len(struct.members) == 0: |
| return '' |
| out = '' |
| out += AddGuardHeader(struct) |
| max_key_len = 0 |
| for v in struct.members: |
| if v.arrayLength is not None: |
| if len(v.name) + len(v.arrayLength) + 2 > max_key_len: |
| max_key_len = len(v.name) + len(v.arrayLength) + 2 |
| elif v.typeID in PREDEFINED_TYPES or v.typeID in STRUCT_BLACKLIST: |
| if len(v.name) > max_key_len: |
| max_key_len = len(v.name) |
| |
| out += f'void Dump{struct.name}(Printer &p, std::string name, const {struct.name} &obj) {{\n' |
| if struct.name == 'VkPhysicalDeviceLimits': |
| out += ' if (p.Type() == OutputType::json)\n' |
| out += ' p.ObjectStart("limits");\n' |
| out += ' else\n' |
| out += ' p.SetSubHeader().ObjectStart(name);\n' |
| elif struct.name == 'VkPhysicalDeviceSparseProperties': |
| out += ' if (p.Type() == OutputType::json)\n' |
| out += ' p.ObjectStart("sparseProperties");\n' |
| out += ' else\n' |
| out += ' p.SetSubHeader().ObjectStart(name);\n' |
| else: |
| out += ' ObjectWrapper object{p, name};\n' |
| if max_key_len > 0: |
| out += f' p.SetMinKeyWidth({max_key_len});\n' |
| for v in struct.members: |
| # arrays |
| if v.arrayLength is not None: |
| # strings |
| if v.typeID == 'char': |
| out += f' p.PrintKeyString("{v.name}", obj.{v.name});\n' |
| # uuid's |
| elif v.typeID == 'uint8_t' and (v.arrayLength == '8' or v.arrayLength == '16'): # VK_UUID_SIZE |
| if v.arrayLength == '8': |
| out += ' if (obj.deviceLUIDValid) { // special case\n' |
| out += f' p.PrintKeyValue("{v.name}", obj.{v.name});\n' |
| if v.arrayLength == '8': |
| out += ' }\n' |
| elif struct.name == 'VkQueueFamilyGlobalPriorityProperties' and v.name == 'priorities': |
| out += f' ArrayWrapper arr(p,"{v.name}", obj.priorityCount);\n' |
| out += ' for (uint32_t i = 0; i < obj.priorityCount; i++) {\n' |
| out += ' if (p.Type() == OutputType::json)\n' |
| out += ' p.PrintString(std::string("VK_") + VkQueueGlobalPriorityString(obj.priorities[i]));\n' |
| out += ' else\n' |
| out += ' p.PrintString(VkQueueGlobalPriorityString(obj.priorities[i]));\n' |
| out += ' }\n' |
| elif v.arrayLength.isdigit(): |
| out += f' {{\n ArrayWrapper arr(p,"{v.name}", ' + v.arrayLength + ');\n' |
| out += f' for (uint32_t i = 0; i < {v.arrayLength}; i++) {{ p.PrintElement(obj.{v.name}[i]); }}\n' |
| out += ' }\n' |
| else: # dynamic array length based on other member |
| out += f' if (obj.{v.arrayLength} == 0 || obj.{v.name} == nullptr) {{\n' |
| out += f' p.PrintKeyString("{v.name}", "NULL");\n' |
| out += ' } else {\n' |
| out += f' ArrayWrapper arr(p,"{v.name}", obj.{v.arrayLength});\n' |
| out += f' for (uint32_t i = 0; i < obj.{v.arrayLength}; i++) {{\n' |
| out += f' Dump{v.typeID}(p, std::to_string(i), obj.{v.name}[i]);\n' |
| out += ' }\n' |
| out += ' }\n' |
| elif v.typeID == 'VkBool32': |
| out += f' p.PrintKeyBool("{v.name}", static_cast<bool>(obj.{v.name}));\n' |
| elif v.typeID == 'uint8_t': |
| out += f' p.PrintKeyValue("{v.name}", static_cast<uint32_t>(obj.{v.name}));\n' |
| elif v.typeID == 'VkDeviceSize': |
| out += f' p.PrintKeyValue("{v.name}", to_hex_str(p, obj.{v.name}));\n' |
| elif v.typeID in PREDEFINED_TYPES: |
| out += 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.typeID in ['VkFormatFeatureFlags', 'VkFormatFeatureFlags2']: |
| out += ' p.SetOpenDetails();\n' # special case so that feature flags are open in html output |
| out += f' Dump{v.typeID}(p, "{v.name}", obj.{v.name});\n' |
| |
| if struct.name in ['VkPhysicalDeviceLimits', 'VkPhysicalDeviceSparseProperties']: |
| out += ' p.ObjectEnd();\n' |
| out += '}\n' |
| |
| out += AddGuardFooter(struct) |
| return out |
| |
| |
| def PrintStructShort(struct): |
| out = '' |
| out += AddGuardHeader(struct) |
| out += f'std::ostream &operator<<(std::ostream &o, {struct.name} &obj) {{\n' |
| out += ' return o << "(" << ' |
| |
| first = True |
| for v in struct.members: |
| if first: |
| first = False |
| out += f'obj.{v.name} << ' |
| else: |
| out += f'\',\' << obj.{v.name} << ' |
| out += '")";\n' |
| out += '}\n' |
| out += AddGuardFooter(struct) |
| return out |
| |
| def PrintChainStruct(listName, structures, all_structures, chain_details, extTypes, aliases, vulkan_versions): |
| sorted_structures = sorted( |
| all_structures, key=operator.attrgetter('name')) |
| |
| 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 = '' |
| structs_to_print = [] |
| for s in sorted_structures: |
| if s.name in structures: |
| structs_to_print.append(s) |
| # use default constructor and delete copy & move operators |
| out += 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 += ' void* start_of_chain = nullptr;\n' |
| for s in structs_to_print: |
| if s.name in STRUCT_BLACKLIST: |
| continue |
| out += AddGuardHeader(s) |
| if s.sTypeName is not None: |
| out += f' {s.name} {s.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 s.name in ['VkPhysicalDeviceShaderIntegerDotProductFeatures', 'VkPhysicalDeviceHostImageCopyFeaturesEXT']: |
| out += f' char {s.name}_padding[64];\n' |
| if s.hasLengthmember: |
| for member in s.members: |
| if member.lengthMember: |
| out += f' std::vector<{member.typeID}> {s.name}_{member.name};\n' |
| out += AddGuardFooter(s) |
| out += ' void initialize_chain(' |
| if chain_details.get('type') in [EXTENSION_TYPE_INSTANCE, EXTENSION_TYPE_BOTH]: |
| out += 'AppInstance &inst, ' |
| if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]: |
| out += 'AppGpu &gpu ' |
| if chain_details.get('can_show_promoted_structs'): |
| out += ', bool show_promoted_structs' |
| out += ') noexcept {\n' |
| for s in structs_to_print: |
| if s.name in STRUCT_BLACKLIST: |
| continue |
| out += AddGuardHeader(s) |
| out += f' {s.name[2:]}.sType = {s.sTypeName};\n' |
| out += AddGuardFooter(s) |
| |
| |
| out += ' std::vector<VkBaseOutStructure*> chain_members{};\n' |
| for s in structs_to_print: |
| if s.name in STRUCT_BLACKLIST: |
| continue |
| out += AddGuardHeader(s) |
| extEnables = {} |
| for k, elem in extTypes.items(): |
| if k == s.name or (s.name in aliases.keys() and k in aliases[s.name]): |
| for e in elem: |
| extEnables[e.extNameStr] = e.type |
| |
| version = None |
| oldVersionName = None |
| for v in vulkan_versions: |
| if s.name in v.names: |
| version = v |
| if s.name in aliases.keys(): |
| for alias in aliases[s.name]: |
| oldVersionName = alias |
| |
| has_version = version is not None |
| has_extNameStr = len(extEnables) > 0 or s.name in aliases.keys() |
| if has_version or has_extNameStr: |
| out += ' if (' |
| has_printed_condition = False |
| if has_extNameStr: |
| for key, value in extEnables.items(): |
| if has_printed_condition: |
| out += '\n || ' |
| else: |
| has_printed_condition = True |
| if has_version: |
| out += '(' |
| if value == EXTENSION_TYPE_DEVICE: |
| out += f'gpu.CheckPhysicalDeviceExtensionIncluded({key})' |
| elif value == EXTENSION_TYPE_INSTANCE: |
| out += f'inst.CheckExtensionEnabled({key})' |
| 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 s.name in STRUCT_1_1_LIST: |
| out += f'{version_desc} == {version.constant} {str_show_promoted_structs}' |
| elif has_printed_condition: |
| out += f')\n && ({version_desc} < {version.constant} {str_show_promoted_structs})' |
| else: |
| out += f'({version_desc} >= {version.constant})' |
| out += ')\n ' |
| else: |
| out += ' ' |
| out += f'chain_members.push_back(reinterpret_cast<VkBaseOutStructure*>(&{s.name[2:]}));\n' |
| out += AddGuardFooter(s) |
| 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 += 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['holder_type']}& 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 += '\n' |
| out += f'void chain_iterator_{listName}(Printer &p, ' |
| if chain_details.get('type') in [EXTENSION_TYPE_INSTANCE, EXTENSION_TYPE_BOTH]: |
| out += 'AppInstance &inst, ' |
| if chain_details.get('type') in [EXTENSION_TYPE_DEVICE, EXTENSION_TYPE_BOTH]: |
| out += 'AppGpu &gpu, ' |
| if chain_details.get('can_show_promoted_structs'): |
| out += 'bool show_promoted_structs, ' |
| out += 'void * place) {\n' |
| out += ' while (place) {\n' |
| out += ' struct VkBaseOutStructure *structure = (struct VkBaseOutStructure *)place;\n' |
| out += ' p.SetSubHeader();\n' |
| |
| for s in sorted_structures: |
| if s.sTypeName is None or s.name in STRUCT_BLACKLIST: |
| continue |
| |
| extEnables = {} |
| for k, elem in extTypes.items(): |
| if k == s.name or (s.name in aliases.keys() and k in aliases[s.name]): |
| for e in elem: |
| extEnables[e.extNameStr] = e.type |
| |
| version = None |
| oldVersionName = None |
| for v in vulkan_versions: |
| if s.name in v.names: |
| version = v |
| if s.name in aliases.keys(): |
| for alias in aliases[s.name]: |
| oldVersionName = alias |
| |
| if s.name in structures: |
| out += AddGuardHeader(s) |
| out += f' if (structure->sType == {s.sTypeName}' |
| if s.name in PORTABILITY_STRUCTS: |
| out += ' && p.Type() != OutputType::json' |
| has_version = version is not None |
| has_extNameStr = len(extEnables) > 0 or s.name in aliases.keys() |
| out += ') {\n' |
| out += f' {s.name}* props = ({s.name}*)structure;\n' |
| out += f' Dump{s.name}(p, ' |
| if s.name in aliases.keys() and version is not None: |
| out += f'{version_desc} >= {version.constant} ?"{s.name}":"{oldVersionName}"' |
| else: |
| out += f'"{s.name}"' |
| out += ', *props);\n' |
| out += ' p.AddNewline();\n' |
| out += ' }\n' |
| out += AddGuardFooter(s) |
| out += ' place = structure->pNext;\n' |
| out += ' }\n' |
| out += '}\n' |
| |
| out += '\n' |
| out += f'bool prepare_{listName}_twocall_chain_vectors(std::unique_ptr<{listName}_chain>& chain) {{\n' |
| out += ' (void)chain;\n' |
| is_twocall = False |
| for s in structs_to_print: |
| if not s.hasLengthmember: |
| continue |
| if s.name in STRUCT_BLACKLIST: |
| continue |
| out += AddGuardHeader(s) |
| for member in s.members: |
| if member.lengthMember: |
| out += f' chain->{s.name}_{member.name}.resize(chain->{s.name[2:]}.{member.arrayLength});\n' |
| out += f' chain->{s.name[2:]}.{member.name} = chain->{s.name}_{member.name}.data();\n' |
| out += AddGuardFooter(s) |
| is_twocall = True |
| out += f' return {"true" if is_twocall else "false"};\n' |
| out += '}\n' |
| |
| return out |
| |
| |
| def PrintStructComparisonForwardDecl(structure): |
| out = '' |
| out += f'bool operator==(const {structure.name} & a, const {structure.name} b);\n' |
| return out |
| |
| |
| def PrintStructComparison(structure): |
| out = '' |
| out += f'bool operator==(const {structure.name} & a, const {structure.name} b) {{\n' |
| out += ' return ' |
| is_first = True |
| for m in structure.members: |
| if m.name not in NAMES_TO_IGNORE: |
| if not is_first: |
| out += '\n && ' |
| else: |
| is_first = False |
| out += f'a.{m.name} == b.{m.name}' |
| out += ';\n' |
| out += '}\n' |
| return out |
| |
| |
| class VulkanEnum: |
| class Option: |
| |
| def __init__(self, name, value, bitpos, comment): |
| self.name = name |
| self.comment = comment |
| |
| if bitpos is not None: |
| value = 1 << int(bitpos) |
| elif isinstance(value, str): |
| if value.lower().startswith('0x'): |
| value = int(value, 16) |
| else: |
| value = int(value) |
| |
| self.value = value |
| |
| def values(self): |
| return { |
| 'optName': self.name, |
| 'optValue': self.value, |
| 'optComment': self.comment, |
| } |
| |
| def __init__(self, rootNode): |
| self.name = rootNode.get('name') |
| self.type = rootNode.get('type') |
| self.options = [] |
| |
| for child in rootNode: |
| childName = child.get('name') |
| childValue = child.get('value') |
| childBitpos = child.get('bitpos') |
| childComment = child.get('comment') |
| childExtends = child.get('extends') |
| childOffset = child.get('offset') |
| childExtNum = child.get('extnumber') |
| support = child.get('supported') |
| if support == 'disabled': |
| continue |
| |
| if childName is None: |
| continue |
| if childValue is None and childBitpos is None and childOffset is None: |
| continue |
| |
| if childExtends is not None and childExtNum is not None and childOffset is not None: |
| childValue = CalcEnumValue(int(childExtNum), int(childOffset)) |
| if 'dir' in child.keys(): |
| childValue = -childValue |
| duplicate = False |
| for o in self.options: |
| if o.values()['optName'] == childName: |
| duplicate = True |
| if duplicate: |
| continue |
| |
| self.options.append(VulkanEnum.Option( |
| childName, childValue, childBitpos, childComment)) |
| |
| |
| class VulkanBitmask: |
| |
| def __init__(self, rootNode): |
| self.name = rootNode.get('name') |
| self.type = rootNode.get('type') |
| |
| # Read each value that the enum contains |
| self.options = [] |
| for child in rootNode: |
| childName = child.get('name') |
| childValue = child.get('value') |
| childBitpos = child.get('bitpos') |
| childComment = child.get('comment') |
| support = child.get('supported') |
| if childName is None or (childValue is None and childBitpos is None): |
| continue |
| if support == 'disabled': |
| continue |
| |
| duplicate = False |
| for option in self.options: |
| if option.name == childName: |
| duplicate = True |
| if duplicate: |
| continue |
| |
| self.options.append(VulkanEnum.Option( |
| childName, childValue, childBitpos, childComment)) |
| |
| |
| class VulkanFlags: |
| |
| def __init__(self, rootNode): |
| self.name = rootNode.get('name') |
| self.type = rootNode.get('type') |
| self.enum = rootNode.get('requires') |
| # 64 bit flags use bitvalues, not requires |
| if self.enum is None: |
| self.enum = rootNode.get('bitvalues') |
| |
| |
| class VulkanVariable: |
| def __init__(self, rootNode, constants): |
| self.name = rootNode.find('name').text |
| # Typename, dereferenced and converted to a useable C++ token |
| self.typeID = rootNode.find('type').text |
| self.baseType = self.typeID |
| self.childType = None |
| self.arrayLength = None |
| self.text = '' |
| for node in rootNode.itertext(): |
| comment = rootNode.find('comment') |
| if comment is not None and comment.text == node: |
| continue |
| self.text += node |
| |
| typeMatch = re.search('.+?(?=' + self.name + ')', self.text) |
| self.type = typeMatch.string[typeMatch.start():typeMatch.end()] |
| self.type = ' '.join(self.type.split()) |
| bracketMatch = re.search('(?<=\\[)[a-zA-Z0-9_]+(?=\\])', self.text) |
| if bracketMatch is not None: |
| matchText = bracketMatch.string[bracketMatch.start( |
| ):bracketMatch.end()] |
| self.childType = self.type |
| self.type += '[' + matchText + ']' |
| if matchText in constants: |
| self.arrayLength = constants[matchText] |
| else: |
| self.arrayLength = matchText |
| |
| self.lengthMember = False |
| lengthString = rootNode.get('len') |
| lengths = [] |
| if lengthString is not None: |
| lengths = re.split(',', lengthString) |
| lengths = list(filter(('null-terminated').__ne__, lengths)) |
| if self.arrayLength is None and len(lengths) > 0: |
| self.childType = '*'.join(self.type.split('*')[0:-1]) |
| self.arrayLength = lengths[0] |
| self.lengthMember = True |
| if self.arrayLength is not None and self.arrayLength.startswith('latexmath'): |
| code = self.arrayLength[10:len(self.arrayLength)] |
| code = re.sub('\\[', '', code) |
| code = re.sub('\\]', '', code) |
| code = re.sub('\\\\(lceil|rceil)', '', code) |
| code = re.sub('{|}', '', code) |
| code = re.sub('\\\\mathit', '', code) |
| code = re.sub('\\\\over', '/', code) |
| code = re.sub('\\\\textrm', '', code) |
| self.arrayLength = code |
| |
| # Dereference if necessary and handle members of variables |
| if self.arrayLength is not None: |
| self.arrayLength = re.sub('::', '->', self.arrayLength) |
| sections = self.arrayLength.split('->') |
| if sections[-1][0] == 'p' and sections[0][1].isupper(): |
| self.arrayLength = '*' + self.arrayLength |
| |
| |
| class VulkanStructure: |
| def __init__(self, name, rootNode, constants, extTypes): |
| self.name = name |
| self.members = [] |
| self.guard = None |
| self.sTypeName = None |
| self.extendsStruct = rootNode.get('structextends') |
| self.hasLengthmember = False |
| |
| for node in rootNode.findall('member'): |
| if node.get('values') is not None: |
| self.sTypeName = node.get('values') |
| self.members.append(VulkanVariable(node, constants)) |
| |
| for member in self.members: |
| if member.lengthMember: |
| self.hasLengthmember = True |
| break |
| |
| for k, elem in extTypes.items(): |
| if k == self.name: |
| for e in elem: |
| if e.guard is not None: |
| self.guard = e.guard |
| |
| |
| class VulkanExtension: |
| def __init__(self, rootNode): |
| self.name = rootNode.get('name') |
| self.number = int(rootNode.get('number')) |
| self.type = rootNode.get('type') |
| self.dependency = rootNode.get('requires') |
| self.guard = GetFeatureProtect(rootNode) |
| self.supported = rootNode.get('supported') |
| self.extNameStr = None |
| self.vktypes = [] |
| self.vkfuncs = [] |
| self.constants = OrderedDict() |
| self.enumValues = OrderedDict() |
| self.node = rootNode |
| |
| for req in rootNode.findall('require'): |
| for ty in req.findall('type'): |
| self.vktypes.append(ty.get('name')) |
| |
| for func in req.findall('command'): |
| self.vkfuncs.append(func.get('name')) |
| |
| for enum in req.findall('enum'): |
| base = enum.get('extends') |
| name = enum.get('name') |
| value = enum.get('value') |
| bitpos = enum.get('bitpos') |
| offset = enum.get('offset') |
| # gets the VK_XXX_EXTENSION_NAME string |
| if value == f'"{self.name}"': |
| self.extNameStr = name |
| |
| if value is None and bitpos is not None: |
| value = 1 << int(bitpos) |
| |
| if offset is not None: |
| offset = int(offset) |
| if base is not None and offset is not None: |
| enumValue = 1000000000 + 1000*(self.number - 1) + offset |
| if enum.get('dir') == '-': |
| enumValue = -enumValue |
| self.enumValues[base] = (name, enumValue) |
| else: |
| self.constants[name] = value |
| |
| |
| class VulkanVersion: |
| def __init__(self, rootNode): |
| self.name = rootNode.get('name') |
| self.constant = self.name.replace('_VERSION_', '_API_VERSION_') |
| self.names = set() |
| |
| for req in rootNode.findall('require'): |
| for ty in req.findall('type'): |
| self.names.add(ty.get('name')) |
| for func in req.findall('command'): |
| self.names.add(func.get('name')) |
| for enum in req.findall('enum'): |
| self.names.add(enum.get('name')) |
| self.names = sorted(self.names) |
| |
| class VulkanFormatRange: |
| def __init__(self, min_inst_version, ext_name, first, last): |
| self.minimum_instance_version = min_inst_version |
| self.extension_name = ext_name |
| self.first_format = first |
| self.last_format = last |