Mercurial > games > semicongine
comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/base_generator.py @ 1501:f40d9d814c08 default tip main
did: correct vulkan-api generator
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 26 Nov 2025 23:34:29 +0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1500:91c8c3b7cbf0 | 1501:f40d9d814c08 |
|---|---|
| 1 #!/usr/bin/env python3 -i | |
| 2 # | |
| 3 # Copyright 2023-2025 The Khronos Group Inc. | |
| 4 # | |
| 5 # SPDX-License-Identifier: Apache-2.0 | |
| 6 | |
| 7 import pickle | |
| 8 import os | |
| 9 import tempfile | |
| 10 import copy | |
| 11 from vulkan_object import (VulkanObject, | |
| 12 Extension, Version, Legacy, Handle, Param, CommandScope, Command, | |
| 13 EnumField, Enum, Flag, Bitmask, ExternSync, Flags, Member, Struct, | |
| 14 Constant, FormatComponent, FormatPlane, Format, FeatureRequirement, | |
| 15 SyncSupport, SyncEquivalent, SyncStage, SyncAccess, SyncPipelineStage, SyncPipeline, | |
| 16 SpirvEnables, Spirv, | |
| 17 VideoCodec, VideoFormat, VideoProfiles, VideoProfileMember, VideoRequiredCapabilities, | |
| 18 VideoStd, VideoStdHeader) | |
| 19 | |
| 20 # These live in the Vulkan-Docs repo, but are pulled in via the | |
| 21 # Vulkan-Headers/registry folder | |
| 22 from generator import OutputGenerator, GeneratorOptions, write | |
| 23 from vkconventions import VulkanConventions | |
| 24 from reg import Registry | |
| 25 from xml.etree import ElementTree | |
| 26 | |
| 27 # An API style convention object | |
| 28 vulkanConventions = VulkanConventions() | |
| 29 | |
| 30 # Helpers to keep things cleaner | |
| 31 def splitIfGet(elem, name): | |
| 32 return elem.get(name).split(',') if elem.get(name) is not None and elem.get(name) != '' else [] | |
| 33 | |
| 34 def textIfFind(elem, name): | |
| 35 return elem.find(name).text if elem.find(name) is not None else None | |
| 36 | |
| 37 def intIfGet(elem, name): | |
| 38 return None if elem.get(name) is None else int(elem.get(name), 0) | |
| 39 | |
| 40 def boolGet(elem, name) -> bool: | |
| 41 return elem.get(name) is not None and elem.get(name) == "true" | |
| 42 | |
| 43 def externSyncGet(elem): | |
| 44 value = elem.get('externsync') | |
| 45 if value is None: | |
| 46 return (ExternSync.NONE, None) | |
| 47 if value == 'true': | |
| 48 return (ExternSync.ALWAYS, None) | |
| 49 if value == 'maybe': | |
| 50 return (ExternSync.MAYBE, None) | |
| 51 | |
| 52 # There are no cases where multiple members of the param are marked as | |
| 53 # externsync. Supporting that with maybe: requires more than | |
| 54 # ExternSync.SUBTYPE_MAYBE (which is only one bit of information), which is | |
| 55 # not currently done as there are no users. | |
| 56 # | |
| 57 # If this assert is hit, please consider simplifying the design such that | |
| 58 # externsync can move to the struct itself and so external synchronization | |
| 59 # requirements do not depend on the context. | |
| 60 assert ',' not in value | |
| 61 | |
| 62 if value.startswith('maybe:'): | |
| 63 return (ExternSync.SUBTYPE_MAYBE, value.removeprefix('maybe:')) | |
| 64 return (ExternSync.SUBTYPE, value) | |
| 65 | |
| 66 # Shared object used by Sync elements that do not have ones | |
| 67 maxSyncSupport = SyncSupport(None, None, True) | |
| 68 maxSyncEquivalent = SyncEquivalent(None, None, True) | |
| 69 | |
| 70 # Helpers to set GeneratorOptions options globally | |
| 71 def SetOutputFileName(fileName: str) -> None: | |
| 72 global globalFileName | |
| 73 globalFileName = fileName | |
| 74 | |
| 75 def SetOutputDirectory(directory: str) -> None: | |
| 76 global globalDirectory | |
| 77 globalDirectory = directory | |
| 78 | |
| 79 def SetTargetApiName(apiname: str) -> None: | |
| 80 global globalApiName | |
| 81 globalApiName = apiname | |
| 82 | |
| 83 def SetMergedApiNames(names: str) -> None: | |
| 84 global mergedApiNames | |
| 85 mergedApiNames = names | |
| 86 | |
| 87 cachingEnabled = False | |
| 88 def EnableCaching() -> None: | |
| 89 global cachingEnabled | |
| 90 cachingEnabled = True | |
| 91 | |
| 92 # This class is a container for any source code, data, or other behavior that is necessary to | |
| 93 # customize the generator script for a specific target API variant (e.g. Vulkan SC). As such, | |
| 94 # all of these API-specific interfaces and their use in the generator script are part of the | |
| 95 # contract between this repository and its downstream users. Changing or removing any of these | |
| 96 # interfaces or their use in the generator script will have downstream effects and thus | |
| 97 # should be avoided unless absolutely necessary. | |
| 98 class APISpecific: | |
| 99 # Version object factory method | |
| 100 @staticmethod | |
| 101 def createApiVersion(targetApiName: str, name: str, featureRequirement) -> Version: | |
| 102 match targetApiName: | |
| 103 | |
| 104 # Vulkan SC specific API version creation | |
| 105 case 'vulkansc': | |
| 106 nameApi = name.replace('VK_', 'VK_API_') | |
| 107 nameApi = nameApi.replace('VKSC_', 'VKSC_API_') | |
| 108 nameString = f'"{name}"' | |
| 109 return Version(name, nameString, nameApi, featureRequirement) | |
| 110 | |
| 111 # Vulkan specific API version creation | |
| 112 case 'vulkan': | |
| 113 nameApi = name.replace('VK_', 'VK_API_') | |
| 114 nameString = f'"{name}"' | |
| 115 return Version(name, nameString, nameApi, featureRequirement) | |
| 116 | |
| 117 # TODO - Currently genType in reg.py does not provide a good way to get this string to apply the C-macro | |
| 118 # We do our best to emulate the answer here the way the spec/headers will with goal to have a proper fix before these assumptions break | |
| 119 @staticmethod | |
| 120 def createHeaderVersion(targetApiName: str, vk: VulkanObject) -> str: | |
| 121 match targetApiName: | |
| 122 case 'vulkan': | |
| 123 major_version = 1 | |
| 124 minor_version = 4 | |
| 125 case 'vulkansc': | |
| 126 major_version = 1 | |
| 127 minor_version = 0 | |
| 128 return f'{major_version}.{minor_version}.{vk.headerVersion}' | |
| 129 | |
| 130 | |
| 131 # This Generator Option is used across all generators. | |
| 132 # After years of use, it has shown that most the options are unified across each generator (file) | |
| 133 # as it is easier to modify things per-file that need the difference | |
| 134 class BaseGeneratorOptions(GeneratorOptions): | |
| 135 def __init__(self, | |
| 136 customFileName = None, | |
| 137 customDirectory = None, | |
| 138 customApiName = None, | |
| 139 videoXmlPath = None): | |
| 140 GeneratorOptions.__init__(self, | |
| 141 conventions = vulkanConventions, | |
| 142 filename = customFileName if customFileName else globalFileName, | |
| 143 directory = customDirectory if customDirectory else globalDirectory, | |
| 144 apiname = customApiName if customApiName else globalApiName, | |
| 145 mergeApiNames = mergedApiNames, | |
| 146 defaultExtensions = customApiName if customApiName else globalApiName, | |
| 147 emitExtensions = '.*', | |
| 148 emitSpirv = '.*', | |
| 149 emitFormats = '.*') | |
| 150 # These are used by the generator.py script | |
| 151 self.apicall = 'VKAPI_ATTR ' | |
| 152 self.apientry = 'VKAPI_CALL ' | |
| 153 self.apientryp = 'VKAPI_PTR *' | |
| 154 self.alignFuncParam = 48 | |
| 155 | |
| 156 # This is used to provide the video.xml to the private video XML generator | |
| 157 self.videoXmlPath = videoXmlPath | |
| 158 | |
| 159 # | |
| 160 # This object handles all the parsing from reg.py generator scripts in the Vulkan-Headers | |
| 161 # It will grab all the data and form it into a single object the rest of the generators will use | |
| 162 class BaseGenerator(OutputGenerator): | |
| 163 def __init__(self): | |
| 164 OutputGenerator.__init__(self, None, None, None) | |
| 165 self.vk = VulkanObject() | |
| 166 self.targetApiName = globalApiName | |
| 167 | |
| 168 # reg.py has a `self.featureName` but this is nicer because | |
| 169 # it will be either the Version or Extension object | |
| 170 self.currentExtension = None | |
| 171 self.currentVersion = None | |
| 172 | |
| 173 # We need to flag extensions that we ignore because they are disabled or not | |
| 174 # supported in the target API(s) | |
| 175 self.unsupportedExtension = False | |
| 176 | |
| 177 # Will map alias to promoted name | |
| 178 # ex. ['VK_FILTER_CUBIC_IMG' : 'VK_FILTER_CUBIC_EXT'] | |
| 179 # When generating any code, there is no reason so use the old name | |
| 180 self.enumAliasMap = dict() | |
| 181 self.enumFieldAliasMap = dict() | |
| 182 self.bitmaskAliasMap = dict() | |
| 183 self.flagAliasMap = dict() | |
| 184 self.flagsAliasMap = dict() | |
| 185 self.structAliasMap = dict() | |
| 186 self.handleAliasMap = dict() | |
| 187 | |
| 188 # We track all enum constants and flag bits so that we can apply their aliases in the end | |
| 189 self.enumFieldMap: dict[str, EnumField] = dict() | |
| 190 self.flagMap: dict[str, Flag] = dict() | |
| 191 | |
| 192 # De-aliases a definition name based on the specified alias map. | |
| 193 # There are aliases of aliases. | |
| 194 # e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR aliases | |
| 195 # VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR which itself aliases | |
| 196 # But it is also common for EXT types promoted to KHR then to core. | |
| 197 # We should not make assumptions about the nesting level of aliases, instead we resolve any | |
| 198 # level of alias aliasing. | |
| 199 def dealias(self, name: str, aliasMap: dict): | |
| 200 while name in aliasMap: | |
| 201 name = aliasMap[name] | |
| 202 return name | |
| 203 | |
| 204 def write(self, data): | |
| 205 # Prevents having to check before writing | |
| 206 if data is not None and data != "": | |
| 207 write(data, file=self.outFile) | |
| 208 | |
| 209 def beginFile(self, genOpts): | |
| 210 OutputGenerator.beginFile(self, genOpts) | |
| 211 self.filename = genOpts.filename | |
| 212 | |
| 213 # No gen*() command to get these, so do it manually | |
| 214 for platform in self.registry.tree.findall('platforms/platform'): | |
| 215 self.vk.platforms[platform.get('name')] = platform.get('protect') | |
| 216 | |
| 217 for tags in self.registry.tree.findall('tags'): | |
| 218 for tag in tags.findall('tag'): | |
| 219 self.vk.vendorTags.append(tag.get('name')) | |
| 220 | |
| 221 # If the video.xml path is provided then we need to load and parse it using | |
| 222 # the private video std generator | |
| 223 if genOpts.videoXmlPath is not None: | |
| 224 videoStdGenerator = _VideoStdGenerator() | |
| 225 videoRegistry = Registry(videoStdGenerator, genOpts) | |
| 226 videoRegistry.loadElementTree(ElementTree.parse(genOpts.videoXmlPath)) | |
| 227 videoRegistry.apiGen() | |
| 228 self.vk.videoStd = videoStdGenerator.vk.videoStd | |
| 229 | |
| 230 # This function should be overloaded | |
| 231 def generate(self): | |
| 232 print("WARNING: This should not be called from the child class") | |
| 233 return | |
| 234 | |
| 235 # This function is dense, it does all the magic to set the right extensions dependencies! | |
| 236 # | |
| 237 # The issue is if 2 extension expose a command, genCmd() will only | |
| 238 # show one of the extension, at endFile() we can finally go through | |
| 239 # and update which things depend on which extensions | |
| 240 # | |
| 241 # self.featureDictionary is built for use in the reg.py framework | |
| 242 # Details found in Vulkan-Docs/scripts/scriptgenerator.py | |
| 243 def applyExtensionDependency(self): | |
| 244 for extension in self.vk.extensions.values(): | |
| 245 # dict.key() can be None, so need to double loop | |
| 246 dict = self.featureDictionary[extension.name]['command'] | |
| 247 | |
| 248 # "required" == None | |
| 249 # or | |
| 250 # an additional feature dependency, which is a boolean expression of | |
| 251 # one or more extension and/or core version names | |
| 252 for required in dict: | |
| 253 for commandName in dict[required]: | |
| 254 # Skip commands removed in the target API | |
| 255 # This check is needed because parts of the base generator code bypass the | |
| 256 # dependency resolution logic in the registry tooling and thus the generator | |
| 257 # may attempt to generate code for commands which are not supported in the | |
| 258 # target API variant, thus this check needs to happen even if any specific | |
| 259 # target API variant may not specifically need it | |
| 260 if not commandName in self.vk.commands: | |
| 261 continue | |
| 262 | |
| 263 command = self.vk.commands[commandName] | |
| 264 # Make sure list is unique | |
| 265 command.extensions.extend([extension.name] if extension.name not in command.extensions else []) | |
| 266 extension.commands.extend([command] if command not in extension.commands else []) | |
| 267 | |
| 268 # While genGroup() will call twice with aliased value, it does not provide all the information we need | |
| 269 dict = self.featureDictionary[extension.name]['enumconstant'] | |
| 270 for required in dict: | |
| 271 # group can be a Enum or Bitmask | |
| 272 for group in dict[required]: | |
| 273 if group in self.vk.handles: | |
| 274 handle = self.vk.handles[group] | |
| 275 # Make sure list is unique | |
| 276 handle.extensions.extend([extension.name] if extension.name not in handle.extensions else []) | |
| 277 extension.handles[group].extend([handle] if handle not in extension.handles[group] else []) | |
| 278 if group in self.vk.enums: | |
| 279 if group not in extension.enumFields: | |
| 280 extension.enumFields[group] = [] # Dict needs init | |
| 281 enum = self.vk.enums[group] | |
| 282 # Need to convert all alias so they match what is in EnumField | |
| 283 enumList = list(map(lambda x: x if x not in self.enumFieldAliasMap else self.dealias(x, self.enumFieldAliasMap), dict[required][group])) | |
| 284 | |
| 285 for enumField in [x for x in enum.fields if x.name in enumList]: | |
| 286 # Make sure list is unique | |
| 287 enum.fieldExtensions.extend([extension.name] if extension.name not in enum.fieldExtensions else []) | |
| 288 enumField.extensions.extend([extension.name] if extension.name not in enumField.extensions else []) | |
| 289 extension.enumFields[group].extend([enumField] if enumField not in extension.enumFields[group] else []) | |
| 290 if group in self.vk.bitmasks: | |
| 291 if group not in extension.flagBits: | |
| 292 extension.flagBits[group] = [] # Dict needs init | |
| 293 bitmask = self.vk.bitmasks[group] | |
| 294 # Need to convert all alias so they match what is in Flags | |
| 295 flagList = list(map(lambda x: x if x not in self.flagAliasMap else self.dealias(x, self.flagAliasMap), dict[required][group])) | |
| 296 | |
| 297 for flags in [x for x in bitmask.flags if x.name in flagList]: | |
| 298 # Make sure list is unique | |
| 299 bitmask.flagExtensions.extend([extension.name] if extension.name not in bitmask.flagExtensions else []) | |
| 300 flags.extensions.extend([extension.name] if extension.name not in flags.extensions else []) | |
| 301 extension.flagBits[group].extend([flags] if flags not in extension.flagBits[group] else []) | |
| 302 if group in self.vk.flags: | |
| 303 flags = self.vk.flags[group] | |
| 304 # Make sure list is unique | |
| 305 flags.extensions.extend([extension.name] if extension.name not in flags.extensions else []) | |
| 306 extension.flags.extend([flags] if flags not in extension.flags[group] else []) | |
| 307 | |
| 308 # Need to do 'enum'/'bitmask' after 'enumconstant' has applied everything so we can add implicit extensions | |
| 309 # | |
| 310 # Sometimes two extensions enable an Enum, but the newer extension version has extra flags allowed | |
| 311 # This information seems to be implicit, so need to update it here | |
| 312 # Go through each Flag and append the Enum extension to it | |
| 313 # | |
| 314 # ex. VkAccelerationStructureTypeKHR where GENERIC_KHR is not allowed with just VK_NV_ray_tracing | |
| 315 # This only works because the values are aliased as well, making the KHR a superset enum | |
| 316 for extension in self.vk.extensions.values(): | |
| 317 dict = self.featureDictionary[extension.name]['enum'] | |
| 318 for required in dict: | |
| 319 for group in dict[required]: | |
| 320 for enumName in dict[required][group]: | |
| 321 isAlias = enumName in self.enumAliasMap | |
| 322 enumName = self.dealias(enumName, self.enumAliasMap) | |
| 323 if enumName in self.vk.enums: | |
| 324 enum = self.vk.enums[enumName] | |
| 325 enum.extensions.extend([extension.name] if extension.name not in enum.extensions else []) | |
| 326 extension.enums.extend([enum] if enum not in extension.enums else []) | |
| 327 # Update fields with implicit base extension | |
| 328 if isAlias: | |
| 329 continue | |
| 330 enum.fieldExtensions.extend([extension.name] if extension.name not in enum.fieldExtensions else []) | |
| 331 for enumField in [x for x in enum.fields if (not x.extensions or (x.extensions and all(e in enum.extensions for e in x.extensions)))]: | |
| 332 enumField.extensions.extend([extension.name] if extension.name not in enumField.extensions else []) | |
| 333 if enumName not in extension.enumFields: | |
| 334 extension.enumFields[enumName] = [] # Dict needs init | |
| 335 extension.enumFields[enumName].extend([enumField] if enumField not in extension.enumFields[enumName] else []) | |
| 336 | |
| 337 dict = self.featureDictionary[extension.name]['bitmask'] | |
| 338 for required in dict: | |
| 339 for group in dict[required]: | |
| 340 for bitmaskName in dict[required][group]: | |
| 341 bitmaskName = bitmaskName.replace('Flags', 'FlagBits') # Works since Flags is not repeated in name | |
| 342 isAlias = bitmaskName in self.bitmaskAliasMap | |
| 343 bitmaskName = self.dealias(bitmaskName, self.bitmaskAliasMap) | |
| 344 if bitmaskName in self.vk.bitmasks: | |
| 345 bitmask = self.vk.bitmasks[bitmaskName] | |
| 346 bitmask.extensions.extend([extension.name] if extension.name not in bitmask.extensions else []) | |
| 347 extension.bitmasks.extend([bitmask] if bitmask not in extension.bitmasks else []) | |
| 348 # Update flags with implicit base extension | |
| 349 if isAlias: | |
| 350 continue | |
| 351 bitmask.flagExtensions.extend([extension.name] if extension.name not in bitmask.flagExtensions else []) | |
| 352 for flag in [x for x in bitmask.flags if (not x.extensions or (x.extensions and all(e in bitmask.extensions for e in x.extensions)))]: | |
| 353 flag.extensions.extend([extension.name] if extension.name not in flag.extensions else []) | |
| 354 if bitmaskName not in extension.flagBits: | |
| 355 extension.flagBits[bitmaskName] = [] # Dict needs init | |
| 356 extension.flagBits[bitmaskName].extend([flag] if flag not in extension.flagBits[bitmaskName] else []) | |
| 357 | |
| 358 # Some structs (ex VkAttachmentSampleCountInfoAMD) can have multiple alias pointing to same extension | |
| 359 for extension in self.vk.extensions.values(): | |
| 360 dict = self.featureDictionary[extension.name]['struct'] | |
| 361 for required in dict: | |
| 362 for group in dict[required]: | |
| 363 for structName in dict[required][group]: | |
| 364 isAlias = structName in self.structAliasMap | |
| 365 structName = self.dealias(structName, self.structAliasMap) | |
| 366 if structName in self.vk.structs: | |
| 367 struct = self.vk.structs[structName] | |
| 368 struct.extensions.extend([extension.name] if extension.name not in struct.extensions else []) | |
| 369 extension.structs.extend([struct] if struct not in extension.structs else []) | |
| 370 | |
| 371 # While we update struct alias inside other structs, the command itself might have the struct as a first level param. | |
| 372 # We use this time to update params to have the promoted name | |
| 373 # Example - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9322 | |
| 374 # TODO: It is unclear why only structs need dealiasing here, but not other types, so this probably needs revisiting | |
| 375 for command in self.vk.commands.values(): | |
| 376 for member in command.params: | |
| 377 if member.type in self.structAliasMap: | |
| 378 member.type = self.dealias(member.type, self.structAliasMap) | |
| 379 # Replace string with Version class now we have all version created | |
| 380 if command.legacy and command.legacy.version: | |
| 381 if command.legacy.version not in self.vk.versions: | |
| 382 # occurs if something like VK_VERSION_1_0, in which case we will always warn for deprecation | |
| 383 command.legacy.version = None | |
| 384 else: | |
| 385 command.legacy.version = self.vk.versions[command.legacy.version] | |
| 386 | |
| 387 # Could build up a reverse lookup map, but since these are not too large of list, just do here | |
| 388 # (Need to be done after we have found all the aliases) | |
| 389 for key, value in self.structAliasMap.items(): | |
| 390 self.vk.structs[self.dealias(value, self.structAliasMap)].aliases.append(key) | |
| 391 for key, value in self.enumFieldAliasMap.items(): | |
| 392 self.enumFieldMap[self.dealias(value, self.enumFieldAliasMap)].aliases.append(key) | |
| 393 for key, value in self.enumAliasMap.items(): | |
| 394 self.vk.enums[self.dealias(value, self.enumAliasMap)].aliases.append(key) | |
| 395 for key, value in self.flagAliasMap.items(): | |
| 396 self.flagMap[self.dealias(value, self.flagAliasMap)].aliases.append(key) | |
| 397 for key, value in self.bitmaskAliasMap.items(): | |
| 398 self.vk.bitmasks[self.dealias(value, self.bitmaskAliasMap)].aliases.append(key) | |
| 399 for key, value in self.flagsAliasMap.items(): | |
| 400 self.vk.flags[self.dealias(value, self.flagsAliasMap)].aliases.append(key) | |
| 401 for key, value in self.handleAliasMap.items(): | |
| 402 self.vk.handles[self.dealias(value, self.handleAliasMap)].aliases.append(key) | |
| 403 | |
| 404 def addConstants(self, constantNames: list[str]): | |
| 405 for constantName in constantNames: | |
| 406 enumInfo = self.registry.enumdict[constantName] | |
| 407 typeName = enumInfo.type | |
| 408 valueStr = enumInfo.elem.get('value') | |
| 409 # These values are represented in c-style | |
| 410 isHex = valueStr.upper().startswith('0X') | |
| 411 intBase = 16 if isHex else 10 | |
| 412 if valueStr.upper().endswith('F') and not isHex: | |
| 413 value = float(valueStr[:-1]) | |
| 414 elif valueStr.upper().endswith('U)'): | |
| 415 inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-1], intBase) | |
| 416 value = (~inner_number) & ((1 << 32) - 1) | |
| 417 elif valueStr.upper().endswith('ULL)'): | |
| 418 inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-3], intBase) | |
| 419 value = (~0) & ((1 << 64) - 1) | |
| 420 else: | |
| 421 value = int(valueStr, intBase) | |
| 422 self.vk.constants[constantName] = Constant(constantName, typeName, value, valueStr) | |
| 423 | |
| 424 def addVideoCodecs(self): | |
| 425 for xmlVideoCodec in self.registry.tree.findall('videocodecs/videocodec'): | |
| 426 name = xmlVideoCodec.get('name') | |
| 427 extend = xmlVideoCodec.get('extend') | |
| 428 value = xmlVideoCodec.get('value') | |
| 429 | |
| 430 profiles: dict[str, VideoProfiles] = {} | |
| 431 capabilities: dict[str, str] = {} | |
| 432 formats: dict[str, VideoFormat] = {} | |
| 433 | |
| 434 if extend is not None: | |
| 435 # Inherit base profiles, capabilities, and formats | |
| 436 profiles = copy.deepcopy(self.vk.videoCodecs[extend].profiles) | |
| 437 capabilities = copy.deepcopy(self.vk.videoCodecs[extend].capabilities) | |
| 438 formats = copy.deepcopy(self.vk.videoCodecs[extend].formats) | |
| 439 | |
| 440 for xmlVideoProfiles in xmlVideoCodec.findall('videoprofiles'): | |
| 441 videoProfileStructName = xmlVideoProfiles.get('struct') | |
| 442 videoProfileStructMembers : dict[str, VideoProfileMember] = {} | |
| 443 | |
| 444 for xmlVideoProfileMember in xmlVideoProfiles.findall('videoprofilemember'): | |
| 445 memberName = xmlVideoProfileMember.get('name') | |
| 446 memberValues: dict[str, str] = {} | |
| 447 | |
| 448 for xmlVideoProfile in xmlVideoProfileMember.findall('videoprofile'): | |
| 449 memberValues[xmlVideoProfile.get('value')] = xmlVideoProfile.get('name') | |
| 450 | |
| 451 videoProfileStructMembers[memberName] = VideoProfileMember(memberName, memberValues) | |
| 452 | |
| 453 profiles[videoProfileStructName] = VideoProfiles(videoProfileStructName, videoProfileStructMembers) | |
| 454 | |
| 455 for xmlVideoCapabilities in xmlVideoCodec.findall('videocapabilities'): | |
| 456 capabilities[xmlVideoCapabilities.get('struct')] = xmlVideoCapabilities.get('struct') | |
| 457 | |
| 458 for xmlVideoFormat in xmlVideoCodec.findall('videoformat'): | |
| 459 videoFormatName = xmlVideoFormat.get('name') | |
| 460 videoFormatExtend = xmlVideoFormat.get('extend') | |
| 461 | |
| 462 videoFormatRequiredCaps: list[VideoRequiredCapabilities] = [] | |
| 463 videoFormatProps: dict[str, str] = {} | |
| 464 | |
| 465 if videoFormatName is not None: | |
| 466 # This is a new video format category | |
| 467 videoFormatUsage = xmlVideoFormat.get('usage') | |
| 468 videoFormat = VideoFormat(videoFormatName, videoFormatUsage, videoFormatRequiredCaps, videoFormatProps) | |
| 469 formats[videoFormatName] = videoFormat | |
| 470 else: | |
| 471 # This is an extension to an already defined video format category | |
| 472 videoFormat = formats[videoFormatExtend] | |
| 473 videoFormatRequiredCaps = videoFormat.requiredCaps | |
| 474 videoFormatProps = videoFormat.properties | |
| 475 | |
| 476 for xmlVideoFormatRequiredCap in xmlVideoFormat.findall('videorequirecapabilities'): | |
| 477 requiredCap = VideoRequiredCapabilities(xmlVideoFormatRequiredCap.get('struct'), | |
| 478 xmlVideoFormatRequiredCap.get('member'), | |
| 479 xmlVideoFormatRequiredCap.get('value')) | |
| 480 videoFormatRequiredCaps.append(requiredCap) | |
| 481 | |
| 482 for xmlVideoFormatProperties in xmlVideoFormat.findall('videoformatproperties'): | |
| 483 videoFormatProps[xmlVideoFormatProperties.get('struct')] = xmlVideoFormatProperties.get('struct') | |
| 484 | |
| 485 self.vk.videoCodecs[name] = VideoCodec(name, value, profiles, capabilities, formats) | |
| 486 | |
| 487 def endFile(self): | |
| 488 # This is the point were reg.py has ran, everything is collected | |
| 489 # We do some post processing now | |
| 490 self.applyExtensionDependency() | |
| 491 | |
| 492 self.addConstants([k for k,v in self.registry.enumvaluedict.items() if v == 'API Constants']) | |
| 493 self.addVideoCodecs() | |
| 494 | |
| 495 self.vk.headerVersionComplete = APISpecific.createHeaderVersion(self.targetApiName, self.vk) | |
| 496 | |
| 497 # Use structs and commands to find which things are returnedOnly | |
| 498 for struct in [x for x in self.vk.structs.values() if not x.returnedOnly]: | |
| 499 for enum in [self.vk.enums[x.type] for x in struct.members if x.type in self.vk.enums]: | |
| 500 enum.returnedOnly = False | |
| 501 for bitmask in [self.vk.bitmasks[x.type] for x in struct.members if x.type in self.vk.bitmasks]: | |
| 502 bitmask.returnedOnly = False | |
| 503 for flags in [self.vk.flags[x.type] for x in struct.members if x.type in self.vk.flags]: | |
| 504 flags.returnedOnly = False | |
| 505 if flags.bitmaskName is not None: | |
| 506 self.vk.bitmasks[flags.bitmaskName].returnedOnly = False | |
| 507 for command in self.vk.commands.values(): | |
| 508 for enum in [self.vk.enums[x.type] for x in command.params if x.type in self.vk.enums]: | |
| 509 enum.returnedOnly = False | |
| 510 for bitmask in [self.vk.bitmasks[x.type] for x in command.params if x.type in self.vk.bitmasks]: | |
| 511 bitmask.returnedOnly = False | |
| 512 for flags in [self.vk.flags[x.type] for x in command.params if x.type in self.vk.flags]: | |
| 513 flags.returnedOnly = False | |
| 514 if flags.bitmaskName is not None: | |
| 515 self.vk.bitmasks[flags.bitmaskName].returnedOnly = False | |
| 516 | |
| 517 # Turn handle parents into pointers to classes | |
| 518 for handle in [x for x in self.vk.handles.values() if x.parent is not None]: | |
| 519 handle.parent = self.vk.handles[handle.parent] | |
| 520 # search up parent chain to see if instance or device | |
| 521 for handle in [x for x in self.vk.handles.values()]: | |
| 522 next_parent = handle.parent | |
| 523 while (not handle.instance and not handle.device): | |
| 524 handle.instance = next_parent.name == 'VkInstance' | |
| 525 handle.device = next_parent.name == 'VkDevice' | |
| 526 next_parent = next_parent.parent | |
| 527 | |
| 528 maxSyncSupport.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags | |
| 529 maxSyncEquivalent.accesses = self.vk.bitmasks['VkAccessFlagBits2'].flags | |
| 530 maxSyncEquivalent.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags | |
| 531 | |
| 532 # All inherited generators should run from here | |
| 533 self.generate() | |
| 534 | |
| 535 if cachingEnabled: | |
| 536 cachePath = os.path.join(tempfile.gettempdir(), f'vkobject_{os.getpid()}') | |
| 537 if not os.path.isfile(cachePath): | |
| 538 cacheFile = open(cachePath, 'wb') | |
| 539 pickle.dump(self.vk, cacheFile) | |
| 540 cacheFile.close() | |
| 541 | |
| 542 # This should not have to do anything but call into OutputGenerator | |
| 543 OutputGenerator.endFile(self) | |
| 544 | |
| 545 # | |
| 546 # Bypass the entire processing and load in the VkObject data | |
| 547 # Still need to handle the beingFile/endFile for reg.py | |
| 548 def generateFromCache(self, cacheVkObjectData, genOpts): | |
| 549 OutputGenerator.beginFile(self, genOpts) | |
| 550 self.filename = genOpts.filename | |
| 551 self.vk = cacheVkObjectData | |
| 552 self.generate() | |
| 553 OutputGenerator.endFile(self) | |
| 554 | |
| 555 # | |
| 556 # Processing point at beginning of each extension definition | |
| 557 def beginFeature(self, interface, emit): | |
| 558 OutputGenerator.beginFeature(self, interface, emit) | |
| 559 platform = interface.get('platform') | |
| 560 self.featureExtraProtec = self.vk.platforms[platform] if platform in self.vk.platforms else None | |
| 561 protect = self.vk.platforms[platform] if platform in self.vk.platforms else None | |
| 562 name = interface.get('name') | |
| 563 | |
| 564 # TODO - This is just mimicking featurerequirementsgenerator.py and works because the logic is simple enough (for now) | |
| 565 featureRequirement = [] | |
| 566 requires = interface.findall('./require') | |
| 567 for require in requires: | |
| 568 requireDepends = require.get('depends') | |
| 569 for feature in require.findall('./feature'): | |
| 570 featureStruct = feature.get('struct') | |
| 571 featureName = feature.get('name') | |
| 572 featureRequirement.append(FeatureRequirement(featureStruct, featureName, requireDepends)) | |
| 573 | |
| 574 if interface.tag == 'extension': | |
| 575 # Generator scripts built on BaseGenerator do not handle the `supported` attribute of extensions | |
| 576 # therefore historically the `generate_source.py` in individual ecosystem components hacked the | |
| 577 # registry by removing non-applicable or disabled extensions from the loaded XML already before | |
| 578 # reg.py parsed it. That broke the general behavior of reg.py for certain use cases so we now | |
| 579 # filter extensions here instead (after parsing) in order to no longer need the filtering hack | |
| 580 # in downstream `generate_source.py` scripts. | |
| 581 enabledApiList = [ globalApiName ] + ([] if mergedApiNames is None else mergedApiNames.split(',')) | |
| 582 if (sup := interface.get('supported')) is not None and all(api not in sup.split(',') for api in enabledApiList): | |
| 583 self.unsupportedExtension = True | |
| 584 return | |
| 585 | |
| 586 instance = interface.get('type') == 'instance' | |
| 587 device = not instance | |
| 588 depends = interface.get('depends') | |
| 589 vendorTag = name.split('_')[1] | |
| 590 platform = interface.get('platform') | |
| 591 provisional = boolGet(interface, 'provisional') | |
| 592 promotedto = interface.get('promotedto') | |
| 593 deprecatedby = interface.get('deprecatedby') | |
| 594 obsoletedby = interface.get('obsoletedby') | |
| 595 specialuse = splitIfGet(interface, 'specialuse') | |
| 596 ratifiedApis = splitIfGet(interface, 'ratified') | |
| 597 ratified = True if len(ratifiedApis) > 0 and self.genOpts.apiname in ratifiedApis else False | |
| 598 | |
| 599 # Not sure if better way to get this info | |
| 600 specVersion = self.featureDictionary[name]['enumconstant'][None][None][0] | |
| 601 nameString = self.featureDictionary[name]['enumconstant'][None][None][1] | |
| 602 | |
| 603 self.currentExtension = Extension(name, nameString, specVersion, instance, device, depends, vendorTag, | |
| 604 platform, protect, provisional, promotedto, deprecatedby, | |
| 605 obsoletedby, specialuse, featureRequirement, ratified) | |
| 606 self.vk.extensions[name] = self.currentExtension | |
| 607 else: # version | |
| 608 number = interface.get('number') | |
| 609 if number != '1.0': | |
| 610 self.currentVersion = APISpecific.createApiVersion(self.targetApiName, name, featureRequirement) | |
| 611 self.vk.versions[name] = self.currentVersion | |
| 612 | |
| 613 def endFeature(self): | |
| 614 OutputGenerator.endFeature(self) | |
| 615 self.currentExtension = None | |
| 616 self.currentVersion = None | |
| 617 self.unsupportedExtension = False | |
| 618 | |
| 619 # | |
| 620 # All <command> from XML | |
| 621 def genCmd(self, cmdinfo, name, alias): | |
| 622 OutputGenerator.genCmd(self, cmdinfo, name, alias) | |
| 623 | |
| 624 # Do not include APIs from unsupported extensions | |
| 625 if self.unsupportedExtension: | |
| 626 return | |
| 627 | |
| 628 params = [] | |
| 629 for param in cmdinfo.elem.findall('param'): | |
| 630 paramName = param.find('name').text | |
| 631 paramType = textIfFind(param, 'type') | |
| 632 paramAlias = param.get('alias') | |
| 633 | |
| 634 cdecl = self.makeCParamDecl(param, 0) | |
| 635 paramFullType = ' '.join(cdecl.split()[:-1]) | |
| 636 pointer = '*' in cdecl or paramType.startswith('PFN_') | |
| 637 paramConst = 'const' in cdecl | |
| 638 fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')] | |
| 639 | |
| 640 paramNoautovalidity = boolGet(param, 'noautovalidity') | |
| 641 | |
| 642 nullTerminated = False | |
| 643 length = param.get('altlen') if param.get('altlen') is not None else param.get('len') | |
| 644 if length: | |
| 645 # we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated" | |
| 646 # This finds both | |
| 647 nullTerminated = 'null-terminated' in length | |
| 648 length = length.replace(',null-terminated', '') if 'null-terminated' in length else length | |
| 649 length = None if length == 'null-terminated' else length | |
| 650 | |
| 651 if fixedSizeArray and not length: | |
| 652 length = ','.join(fixedSizeArray) | |
| 653 | |
| 654 # See Member::optional code for details of this | |
| 655 optionalValues = splitIfGet(param, 'optional') | |
| 656 optional = len(optionalValues) > 0 and optionalValues[0].lower() == "true" | |
| 657 optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true" | |
| 658 | |
| 659 # externsync will be 'true', 'maybe', '<expression>' or 'maybe:<expression>' | |
| 660 (externSync, externSyncPointer) = externSyncGet(param) | |
| 661 | |
| 662 params.append(Param(paramName, paramAlias, paramType, paramFullType, paramNoautovalidity, | |
| 663 paramConst, length, nullTerminated, pointer, fixedSizeArray, | |
| 664 optional, optionalPointer, | |
| 665 externSync, externSyncPointer, cdecl)) | |
| 666 | |
| 667 attrib = cmdinfo.elem.attrib | |
| 668 alias = attrib.get('alias') | |
| 669 tasks = splitIfGet(attrib, 'tasks') | |
| 670 | |
| 671 queues = splitIfGet(attrib, 'queues') | |
| 672 allowNoQueues = boolGet(attrib, 'allownoqueues') | |
| 673 successcodes = splitIfGet(attrib, 'successcodes') | |
| 674 errorcodes = splitIfGet(attrib, 'errorcodes') | |
| 675 cmdbufferlevel = attrib.get('cmdbufferlevel') | |
| 676 primary = cmdbufferlevel is not None and 'primary' in cmdbufferlevel | |
| 677 secondary = cmdbufferlevel is not None and 'secondary' in cmdbufferlevel | |
| 678 | |
| 679 renderpass = attrib.get('renderpass') | |
| 680 renderpass = CommandScope.NONE if renderpass is None else getattr(CommandScope, renderpass.upper()) | |
| 681 videocoding = attrib.get('videocoding') | |
| 682 videocoding = CommandScope.NONE if videocoding is None else getattr(CommandScope, videocoding.upper()) | |
| 683 | |
| 684 protoElem = cmdinfo.elem.find('proto') | |
| 685 returnType = textIfFind(protoElem, 'type') | |
| 686 | |
| 687 decls = self.makeCDecls(cmdinfo.elem) | |
| 688 cPrototype = decls[0] | |
| 689 cFunctionPointer = decls[1] | |
| 690 | |
| 691 legacy = None | |
| 692 if cmdinfo.deprecatedlink: | |
| 693 legacy = Legacy(cmdinfo.deprecatedlink, | |
| 694 cmdinfo.deprecatedbyversion, # is just the string, will update to class later | |
| 695 cmdinfo.deprecatedbyextensions) | |
| 696 | |
| 697 protect = self.currentExtension.protect if self.currentExtension is not None else None | |
| 698 | |
| 699 # These coammds have no way from the XML to detect they would be an instance command | |
| 700 specialInstanceCommand = ['vkCreateInstance', 'vkEnumerateInstanceExtensionProperties','vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion'] | |
| 701 instance = len(params) > 0 and (params[0].type == 'VkInstance' or params[0].type == 'VkPhysicalDevice' or name in specialInstanceCommand) | |
| 702 device = not instance | |
| 703 | |
| 704 implicitElem = cmdinfo.elem.find('implicitexternsyncparams') | |
| 705 implicitExternSyncParams = [x.text for x in implicitElem.findall('param')] if implicitElem is not None else [] | |
| 706 | |
| 707 self.vk.commands[name] = Command(name, alias, protect, [], self.currentVersion, | |
| 708 returnType, params, instance, device, | |
| 709 tasks, queues, allowNoQueues, successcodes, errorcodes, | |
| 710 primary, secondary, renderpass, videocoding, | |
| 711 implicitExternSyncParams, legacy, cPrototype, cFunctionPointer) | |
| 712 | |
| 713 # | |
| 714 # List the enum for the commands | |
| 715 # TODO - Seems empty groups like `VkDeviceDeviceMemoryReportCreateInfoEXT` do not show up in here | |
| 716 def genGroup(self, groupinfo, groupName, alias): | |
| 717 # Do not include APIs from unsupported extensions | |
| 718 if self.unsupportedExtension: | |
| 719 return | |
| 720 | |
| 721 # There can be case where the Enum/Bitmask is in a protect, but the individual | |
| 722 # fields also have their own protect | |
| 723 groupProtect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None | |
| 724 enumElem = groupinfo.elem | |
| 725 bitwidth = 32 if enumElem.get('bitwidth') is None else int(enumElem.get('bitwidth')) | |
| 726 fields = [] | |
| 727 if enumElem.get('type') == "enum": | |
| 728 if alias is not None: | |
| 729 self.enumAliasMap[groupName] = alias | |
| 730 return | |
| 731 | |
| 732 for elem in enumElem.findall('enum'): | |
| 733 fieldName = elem.get('name') | |
| 734 | |
| 735 # Do not include non-required enum constants | |
| 736 # reg.py emits the enum constants of the entire type, even constants that are part of unsupported | |
| 737 # extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks | |
| 738 # down these and also alias dependencies and marks the enum constants that are actually required | |
| 739 # with the 'required' attribute. Therefore we also have to verify that here to make sure we only | |
| 740 # include enum constants that are actually required in the target API(s). | |
| 741 if elem.get('required') is None: | |
| 742 continue | |
| 743 | |
| 744 if elem.get('alias') is not None: | |
| 745 self.enumFieldAliasMap[fieldName] = elem.get('alias') | |
| 746 continue | |
| 747 | |
| 748 negative = elem.get('dir') is not None | |
| 749 protect = elem.get('protect') | |
| 750 (valueInt, valueStr) = self.enumToValue(elem, True, bitwidth) | |
| 751 | |
| 752 # Some values have multiple extensions (ex VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR) | |
| 753 # genGroup() lists them twice | |
| 754 if next((x for x in fields if x.name == fieldName), None) is None: | |
| 755 self.enumFieldMap[fieldName] = EnumField(fieldName, [], protect, negative, valueInt, valueStr, []) | |
| 756 fields.append(self.enumFieldMap[fieldName]) | |
| 757 | |
| 758 self.vk.enums[groupName] = Enum(groupName, [], groupProtect, bitwidth, True, fields, [], []) | |
| 759 | |
| 760 else: # "bitmask" | |
| 761 if alias is not None: | |
| 762 self.bitmaskAliasMap[groupName] = alias | |
| 763 return | |
| 764 | |
| 765 for elem in enumElem.findall('enum'): | |
| 766 flagName = elem.get('name') | |
| 767 | |
| 768 # Do not include non-required enum constants | |
| 769 # reg.py emits the enum constants of the entire type, even constants that are part of unsupported | |
| 770 # extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks | |
| 771 # down these and also alias dependencies and marks the enum constants that are actually required | |
| 772 # with the 'required' attribute. Therefore we also have to verify that here to make sure we only | |
| 773 # include enum constants that are actually required in the target API(s). | |
| 774 if elem.get('required') is None: | |
| 775 continue | |
| 776 | |
| 777 if elem.get('alias') is not None: | |
| 778 self.flagAliasMap[flagName] = elem.get('alias') | |
| 779 continue | |
| 780 | |
| 781 protect = elem.get('protect') | |
| 782 | |
| 783 (valueInt, valueStr) = self.enumToValue(elem, True, bitwidth) | |
| 784 flagZero = valueInt == 0 | |
| 785 flagMultiBit = False | |
| 786 # if flag uses 'value' instead of 'bitpos', will be zero or a mask | |
| 787 if elem.get('bitpos') is None and elem.get('value'): | |
| 788 flagMultiBit = valueInt != 0 | |
| 789 | |
| 790 # Some values have multiple extensions (ex VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT) | |
| 791 # genGroup() lists them twice | |
| 792 if next((x for x in fields if x.name == flagName), None) is None: | |
| 793 self.flagMap[flagName] = Flag(flagName, [], protect, valueInt, valueStr, flagMultiBit, flagZero, []) | |
| 794 fields.append(self.flagMap[flagName]) | |
| 795 | |
| 796 flagName = groupName.replace('FlagBits', 'Flags') | |
| 797 self.vk.bitmasks[groupName] = Bitmask(groupName, [], flagName, groupProtect, bitwidth, True, fields, [], []) | |
| 798 | |
| 799 def genType(self, typeInfo, typeName, alias): | |
| 800 OutputGenerator.genType(self, typeInfo, typeName, alias) | |
| 801 | |
| 802 # Do not include APIs from unsupported extensions | |
| 803 if self.unsupportedExtension: | |
| 804 return | |
| 805 | |
| 806 typeElem = typeInfo.elem | |
| 807 protect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None | |
| 808 extension = [self.currentExtension.name] if self.currentExtension is not None else [] | |
| 809 category = typeElem.get('category') | |
| 810 if (category == 'struct' or category == 'union'): | |
| 811 if alias is not None: | |
| 812 self.structAliasMap[typeName] = alias | |
| 813 return | |
| 814 | |
| 815 union = category == 'union' | |
| 816 | |
| 817 returnedOnly = boolGet(typeElem, 'returnedonly') | |
| 818 allowDuplicate = boolGet(typeElem, 'allowduplicate') | |
| 819 | |
| 820 extends = splitIfGet(typeElem, 'structextends') | |
| 821 extendedBy = self.registry.validextensionstructs[typeName] if len(self.registry.validextensionstructs[typeName]) > 0 else [] | |
| 822 | |
| 823 membersElem = typeInfo.elem.findall('.//member') | |
| 824 members = [] | |
| 825 sType = None | |
| 826 | |
| 827 for member in membersElem: | |
| 828 for comment in member.findall('comment'): | |
| 829 member.remove(comment) | |
| 830 | |
| 831 name = textIfFind(member, 'name') | |
| 832 type = textIfFind(member, 'type') | |
| 833 sType = member.get('values') if member.get('values') is not None else sType | |
| 834 noautovalidity = boolGet(member, 'noautovalidity') | |
| 835 limittype = member.get('limittype') | |
| 836 | |
| 837 (externSync, externSyncPointer) = externSyncGet(member) | |
| 838 # No cases currently where a subtype of a struct is marked as externally synchronized. | |
| 839 assert externSyncPointer is None | |
| 840 | |
| 841 nullTerminated = False | |
| 842 length = member.get('altlen') if member.get('altlen') is not None else member.get('len') | |
| 843 if length: | |
| 844 # we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated" | |
| 845 # This finds both | |
| 846 nullTerminated = 'null-terminated' in length | |
| 847 length = length.replace(',null-terminated', '') if 'null-terminated' in length else length | |
| 848 length = None if length == 'null-terminated' else length | |
| 849 | |
| 850 cdecl = self.makeCParamDecl(member, 0) | |
| 851 fullType = ' '.join(cdecl[:cdecl.rfind(name)].split()) | |
| 852 pointer = '*' in cdecl or type.startswith('PFN_') | |
| 853 const = 'const' in cdecl | |
| 854 # Some structs like VkTransformMatrixKHR have a 2D array | |
| 855 fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')] | |
| 856 | |
| 857 if fixedSizeArray and not length: | |
| 858 length = ','.join(fixedSizeArray) | |
| 859 | |
| 860 # Handle C bit field members | |
| 861 bitFieldWidth = int(cdecl.split(':')[1]) if ':' in cdecl else None | |
| 862 | |
| 863 selector = member.get('selector') if not union else None | |
| 864 selection = member.get('selection') if union else None | |
| 865 selections = [] | |
| 866 if selection: | |
| 867 selections = [s for s in selection.split(',')] | |
| 868 | |
| 869 # if a pointer, this can be a something like: | |
| 870 # optional="true,false" for ppGeometries | |
| 871 # optional="false,true" for pPhysicalDeviceCount | |
| 872 # the first is if the variable itself is optional | |
| 873 # the second is the value of the pointer is optional; | |
| 874 optionalValues = splitIfGet(member, 'optional') | |
| 875 optional = len(optionalValues) > 0 and optionalValues[0].lower() == "true" | |
| 876 optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true" | |
| 877 | |
| 878 members.append(Member(name, type, fullType, noautovalidity, limittype, | |
| 879 const, length, nullTerminated, pointer, fixedSizeArray, | |
| 880 optional, optionalPointer, | |
| 881 externSync, cdecl, bitFieldWidth, selector, selections)) | |
| 882 | |
| 883 self.vk.structs[typeName] = Struct(typeName, [], extension, self.currentVersion, protect, members, | |
| 884 union, returnedOnly, sType, allowDuplicate, extends, extendedBy) | |
| 885 | |
| 886 elif category == 'handle': | |
| 887 if alias is not None: | |
| 888 self.handleAliasMap[typeName] = alias | |
| 889 return | |
| 890 type = typeElem.get('objtypeenum') | |
| 891 | |
| 892 # will resolve these later, the VulkanObjectType does not list things in dependent order | |
| 893 parent = typeElem.get('parent') | |
| 894 instance = typeName == 'VkInstance' | |
| 895 device = typeName == 'VkDevice' | |
| 896 | |
| 897 dispatchable = typeElem.find('type').text == 'VK_DEFINE_HANDLE' | |
| 898 | |
| 899 self.vk.handles[typeName] = Handle(typeName, [], type, protect, parent, instance, device, dispatchable, extension) | |
| 900 | |
| 901 elif category == 'define': | |
| 902 if typeName == 'VK_HEADER_VERSION': | |
| 903 self.vk.headerVersion = typeElem.find('name').tail.strip() | |
| 904 | |
| 905 elif category == 'bitmask': | |
| 906 if alias is not None: | |
| 907 self.flagsAliasMap[typeName] = alias | |
| 908 return | |
| 909 | |
| 910 # Bitmask types, i.e. flags | |
| 911 baseFlagsType = typeElem.find('type').text | |
| 912 bitWidth = 64 if baseFlagsType == 'VkFlags64' else 32 | |
| 913 | |
| 914 # Bitmask enum type is either in the 'requires' or 'bitvalues' attribute | |
| 915 # (for some reason there are two conventions) | |
| 916 bitmaskName = typeElem.get('bitvalues') | |
| 917 if bitmaskName is None: | |
| 918 bitmaskName = typeElem.get('requires') | |
| 919 | |
| 920 self.vk.flags[typeName] = Flags(typeName, [], bitmaskName, protect, baseFlagsType, bitWidth, True, extension) | |
| 921 | |
| 922 else: | |
| 923 # not all categories are used | |
| 924 # 'group'/'enum' are routed to genGroup instead | |
| 925 # 'basetype'/'include' are only for headers | |
| 926 # 'funcpointer` ignore until needed | |
| 927 return | |
| 928 | |
| 929 def genSpirv(self, spirvinfo, spirvName, alias): | |
| 930 OutputGenerator.genSpirv(self, spirvinfo, spirvName, alias) | |
| 931 spirvElem = spirvinfo.elem | |
| 932 name = spirvElem.get('name') | |
| 933 extension = True if spirvElem.tag == 'spirvextension' else False | |
| 934 capability = not extension | |
| 935 | |
| 936 enables = [] | |
| 937 for elem in spirvElem: | |
| 938 version = elem.attrib.get('version') | |
| 939 extensionEnable = elem.attrib.get('extension') | |
| 940 struct = elem.attrib.get('struct') | |
| 941 feature = elem.attrib.get('feature') | |
| 942 requires = elem.attrib.get('requires') | |
| 943 propertyEnable = elem.attrib.get('property') | |
| 944 member = elem.attrib.get('member') | |
| 945 value = elem.attrib.get('value') | |
| 946 enables.append(SpirvEnables(version, extensionEnable, struct, feature, | |
| 947 requires, propertyEnable, member, value)) | |
| 948 | |
| 949 self.vk.spirv.append(Spirv(name, extension, capability, enables)) | |
| 950 | |
| 951 def genFormat(self, format, formatinfo, alias): | |
| 952 OutputGenerator.genFormat(self, format, formatinfo, alias) | |
| 953 formatElem = format.elem | |
| 954 name = formatElem.get('name') | |
| 955 | |
| 956 components = [] | |
| 957 for component in formatElem.iterfind('component'): | |
| 958 type = component.get('name') | |
| 959 bits = component.get('bits') | |
| 960 numericFormat = component.get('numericFormat') | |
| 961 planeIndex = intIfGet(component, 'planeIndex') | |
| 962 components.append(FormatComponent(type, bits, numericFormat, planeIndex)) | |
| 963 | |
| 964 planes = [] | |
| 965 for plane in formatElem.iterfind('plane'): | |
| 966 index = int(plane.get('index')) | |
| 967 widthDivisor = int(plane.get('widthDivisor')) | |
| 968 heightDivisor = int(plane.get('heightDivisor')) | |
| 969 compatible = plane.get('compatible') | |
| 970 planes.append(FormatPlane(index, widthDivisor, heightDivisor, compatible)) | |
| 971 | |
| 972 className = formatElem.get('class') | |
| 973 blockSize = int(formatElem.get('blockSize')) | |
| 974 texelsPerBlock = int(formatElem.get('texelsPerBlock')) | |
| 975 blockExtent = splitIfGet(formatElem, 'blockExtent') | |
| 976 packed = intIfGet(formatElem, 'packed') | |
| 977 chroma = formatElem.get('chroma') | |
| 978 compressed = formatElem.get('compressed') | |
| 979 spirvImageFormat = formatElem.find('spirvimageformat') | |
| 980 if spirvImageFormat is not None: | |
| 981 spirvImageFormat = spirvImageFormat.get('name') | |
| 982 | |
| 983 self.vk.formats[name] = Format(name, className, blockSize, texelsPerBlock, | |
| 984 blockExtent, packed, chroma, compressed, | |
| 985 components, planes, spirvImageFormat) | |
| 986 | |
| 987 def genSyncStage(self, sync): | |
| 988 OutputGenerator.genSyncStage(self, sync) | |
| 989 syncElem = sync.elem | |
| 990 | |
| 991 support = maxSyncSupport | |
| 992 supportElem = syncElem.find('syncsupport') | |
| 993 if supportElem is not None: | |
| 994 queues = splitIfGet(supportElem, 'queues') | |
| 995 stageNames = splitIfGet(supportElem, 'stage') | |
| 996 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None | |
| 997 support = SyncSupport(queues, stages, False) | |
| 998 | |
| 999 equivalent = maxSyncEquivalent | |
| 1000 equivalentElem = syncElem.find('syncequivalent') | |
| 1001 if equivalentElem is not None: | |
| 1002 stageNames = splitIfGet(equivalentElem, 'stage') | |
| 1003 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None | |
| 1004 accessNames = splitIfGet(equivalentElem, 'access') | |
| 1005 accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 else None | |
| 1006 equivalent = SyncEquivalent(stages, accesses, False) | |
| 1007 | |
| 1008 flagName = syncElem.get('name') | |
| 1009 flag = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name == flagName] | |
| 1010 # This check is needed because not all API variants have VK_KHR_synchronization2 | |
| 1011 if flag: | |
| 1012 self.vk.syncStage.append(SyncStage(flag[0], support, equivalent)) | |
| 1013 | |
| 1014 def genSyncAccess(self, sync): | |
| 1015 OutputGenerator.genSyncAccess(self, sync) | |
| 1016 syncElem = sync.elem | |
| 1017 | |
| 1018 support = maxSyncSupport | |
| 1019 supportElem = syncElem.find('syncsupport') | |
| 1020 if supportElem is not None: | |
| 1021 queues = splitIfGet(supportElem, 'queues') | |
| 1022 stageNames = splitIfGet(supportElem, 'stage') | |
| 1023 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None | |
| 1024 support = SyncSupport(queues, stages, False) | |
| 1025 | |
| 1026 equivalent = maxSyncEquivalent | |
| 1027 equivalentElem = syncElem.find('syncequivalent') | |
| 1028 if equivalentElem is not None: | |
| 1029 stageNames = splitIfGet(equivalentElem, 'stage') | |
| 1030 stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 else None | |
| 1031 accessNames = splitIfGet(equivalentElem, 'access') | |
| 1032 accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 else None | |
| 1033 equivalent = SyncEquivalent(stages, accesses, False) | |
| 1034 | |
| 1035 flagName = syncElem.get('name') | |
| 1036 flag = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name == flagName] | |
| 1037 # This check is needed because not all API variants have VK_KHR_synchronization2 | |
| 1038 if flag: | |
| 1039 self.vk.syncAccess.append(SyncAccess(flag[0], support, equivalent)) | |
| 1040 | |
| 1041 def genSyncPipeline(self, sync): | |
| 1042 OutputGenerator.genSyncPipeline(self, sync) | |
| 1043 syncElem = sync.elem | |
| 1044 name = syncElem.get('name') | |
| 1045 depends = splitIfGet(syncElem, 'depends') | |
| 1046 stages = [] | |
| 1047 for stageElem in syncElem.findall('syncpipelinestage'): | |
| 1048 order = stageElem.get('order') | |
| 1049 before = stageElem.get('before') | |
| 1050 after = stageElem.get('after') | |
| 1051 value = stageElem.text | |
| 1052 stages.append(SyncPipelineStage(order, before, after, value)) | |
| 1053 | |
| 1054 self.vk.syncPipeline.append(SyncPipeline(name, depends, stages)) | |
| 1055 | |
| 1056 # | |
| 1057 # This object handles all the parsing from the video.xml (i.e. Video Std header definitions) | |
| 1058 # It will fill in video standard definitions into the VulkanObject | |
| 1059 class _VideoStdGenerator(BaseGenerator): | |
| 1060 def __init__(self): | |
| 1061 BaseGenerator.__init__(self) | |
| 1062 self.vk.videoStd = VideoStd() | |
| 1063 | |
| 1064 # Track the current Video Std header we are processing | |
| 1065 self.currentVideoStdHeader = None | |
| 1066 | |
| 1067 def write(self, data): | |
| 1068 # We do not write anything here | |
| 1069 return | |
| 1070 | |
| 1071 def beginFile(self, genOpts): | |
| 1072 # We intentionally skip default BaseGenerator behavior | |
| 1073 OutputGenerator.beginFile(self, genOpts) | |
| 1074 | |
| 1075 def endFile(self): | |
| 1076 # Move parsed definitions to the Video Std definitions | |
| 1077 self.vk.videoStd.enums = self.vk.enums | |
| 1078 self.vk.videoStd.structs = self.vk.structs | |
| 1079 self.vk.videoStd.constants = self.vk.constants | |
| 1080 | |
| 1081 # We intentionally skip default BaseGenerator behavior | |
| 1082 OutputGenerator.endFile(self) | |
| 1083 | |
| 1084 def beginFeature(self, interface, emit): | |
| 1085 # We intentionally skip default BaseGenerator behavior | |
| 1086 OutputGenerator.beginFeature(self, interface, emit) | |
| 1087 | |
| 1088 # Only "extension" is possible in the video.xml, identifying the Video Std header | |
| 1089 assert interface.tag == 'extension' | |
| 1090 name = interface.get('name') | |
| 1091 version: (str | None) = None | |
| 1092 depends: list[str] = [] | |
| 1093 | |
| 1094 # Handle Video Std header version constant | |
| 1095 for enum in interface.findall('require/enum[@value]'): | |
| 1096 enumName = enum.get('name') | |
| 1097 if enumName.endswith('_SPEC_VERSION'): | |
| 1098 version = enum.get('value') | |
| 1099 | |
| 1100 # Handle dependencies on other Video Std headers | |
| 1101 for type in interface.findall('require/type[@name]'): | |
| 1102 typeName = type.get('name') | |
| 1103 if typeName.startswith('vk_video/'): | |
| 1104 depends.append(typeName[len('vk_video/'):-len('.h')]) | |
| 1105 | |
| 1106 headerFile = f'vk_video/{name}.h' | |
| 1107 | |
| 1108 self.vk.videoStd.headers[name] = VideoStdHeader(name, version, headerFile, depends) | |
| 1109 | |
| 1110 self.currentVideoStdHeader = self.vk.videoStd.headers[name] | |
| 1111 | |
| 1112 # Handle constants here as that seems the most straightforward | |
| 1113 constantNames = [] | |
| 1114 for enum in interface.findall('require/enum[@type]'): | |
| 1115 constantNames.append(enum.get('name')) | |
| 1116 self.addConstants(constantNames) | |
| 1117 for constantName in constantNames: | |
| 1118 self.vk.constants[constantName].videoStdHeader = self.currentVideoStdHeader.name | |
| 1119 | |
| 1120 def endFeature(self): | |
| 1121 self.currentVideoStdHeader = None | |
| 1122 | |
| 1123 # We intentionally skip default BaseGenerator behavior | |
| 1124 OutputGenerator.endFeature(self) | |
| 1125 | |
| 1126 def genCmd(self, cmdinfo, name, alias): | |
| 1127 # video.xml should not contain any commands | |
| 1128 assert False | |
| 1129 | |
| 1130 def genGroup(self, groupinfo, groupName, alias): | |
| 1131 BaseGenerator.genGroup(self, groupinfo, groupName, alias) | |
| 1132 | |
| 1133 # We are supposed to be inside a video std header | |
| 1134 assert self.currentVideoStdHeader is not None | |
| 1135 | |
| 1136 # Mark the enum with the Video Std header it comes from | |
| 1137 if groupinfo.elem.get('type') == 'enum': | |
| 1138 assert alias is None | |
| 1139 self.vk.enums[groupName].videoStdHeader = self.currentVideoStdHeader.name | |
| 1140 | |
| 1141 def genType(self, typeInfo, typeName, alias): | |
| 1142 BaseGenerator.genType(self, typeInfo, typeName, alias) | |
| 1143 | |
| 1144 # We are supposed to be inside a video std header | |
| 1145 assert self.currentVideoStdHeader is not None | |
| 1146 | |
| 1147 # Mark the struct with the Video Std header it comes from | |
| 1148 if typeInfo.elem.get('category') == 'struct': | |
| 1149 assert alias is None | |
| 1150 self.vk.structs[typeName].videoStdHeader = self.currentVideoStdHeader.name | |
| 1151 | |
| 1152 def genSpirv(self, spirvinfo, spirvName, alias): | |
| 1153 # video.xml should not contain any SPIR-V info | |
| 1154 assert False | |
| 1155 | |
| 1156 def genFormat(self, format, formatinfo, alias): | |
| 1157 # video.xml should not contain any format info | |
| 1158 assert False | |
| 1159 | |
| 1160 def genSyncStage(self, sync): | |
| 1161 # video.xml should not contain any sync stage info | |
| 1162 assert False | |
| 1163 | |
| 1164 def genSyncAccess(self, sync): | |
| 1165 # video.xml should not contain any sync access info | |
| 1166 assert False | |
| 1167 | |
| 1168 def genSyncPipeline(self, sync): | |
| 1169 # video.xml should not contain any sync pipeline info | |
| 1170 assert False |
