scripts: Use new VulkanObject from headers

This commit is contained in:
spencer-lunarg 2025-02-21 13:31:47 -05:00 committed by Spencer Fricke
parent c4ede3c600
commit 42b34ac73e
8 changed files with 9 additions and 1196 deletions

View file

@ -33,7 +33,7 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
print("Inside Vulkan-Headers there is a registry/reg.py file that is used.")
sys.exit(1) # Return without call stack so easy to spot error
from generators.base_generator import BaseGeneratorOptions
from base_generator import BaseGeneratorOptions
from generators.dispatch_table_generator import DispatchTableOutputGenerator
from generators.enum_string_helper_generator import EnumStringHelperOutputGenerator
from generators.format_utils_generator import FormatUtilsOutputGenerator
@ -42,7 +42,7 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
# These set fields that are needed by both OutputGenerator and BaseGenerator,
# but are uniform and don't need to be set at a per-generated file level
from generators.base_generator import (SetTargetApiName, SetMergedApiNames)
from base_generator import (SetTargetApiName, SetMergedApiNames)
SetTargetApiName(api)
# Build up a list of all generators and custom options

View file

@ -1,782 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2023-2025 Valve Corporation
# Copyright (c) 2023-2025 LunarG, Inc.
# Copyright (c) 2023-2025 RasterGrid Kft.
#
# SPDX-License-Identifier: Apache-2.0
import pickle
import os
import tempfile
from generators.vulkan_object import (VulkanObject,
Extension, Version, Handle, Param, Queues, CommandScope, Command,
EnumField, Enum, Flag, Bitmask, Member, Struct,
FormatComponent, FormatPlane, Format,
SyncSupport, SyncEquivalent, SyncStage, SyncAccess, SyncPipelineStage, SyncPipeline,
SpirvEnables, Spirv)
# These live in the Vulkan-Docs repo, but are pulled in via the
# Vulkan-Headers/registry folder
from generator import OutputGenerator, GeneratorOptions, write
from vkconventions import VulkanConventions
# An API style convention object
vulkanConventions = VulkanConventions()
# Helpers to keep things cleaner
def splitIfGet(elem, name):
return elem.get(name).split(',') if elem.get(name) is not None and elem.get(name) != '' else None
def textIfFind(elem, name):
return elem.find(name).text if elem.find(name) is not None else None
def intIfGet(elem, name):
return None if elem.get(name) is None else int(elem.get(name), 0)
def boolGet(elem, name) -> bool:
return elem.get(name) is not None and elem.get(name) == "true"
def getQueues(elem) -> Queues:
queues = 0
queues_list = splitIfGet(elem, 'queues')
if queues_list is not None:
queues |= Queues.TRANSFER if 'transfer' in queues_list else 0
queues |= Queues.GRAPHICS if 'graphics' in queues_list else 0
queues |= Queues.COMPUTE if 'compute' in queues_list else 0
queues |= Queues.PROTECTED if 'protected' in queues_list else 0
queues |= Queues.SPARSE_BINDING if 'sparse_binding' in queues_list else 0
queues |= Queues.OPTICAL_FLOW if 'opticalflow' in queues_list else 0
queues |= Queues.DECODE if 'decode' in queues_list else 0
queues |= Queues.ENCODE if 'encode' in queues_list else 0
return queues
# Shared object used by Sync elements that don't have ones
maxSyncSupport = SyncSupport(None, None, True)
maxSyncEquivalent = SyncEquivalent(None, None, True)
# Helpers to set GeneratorOptions options globally
def SetOutputFileName(fileName: str) -> None:
global globalFileName
globalFileName = fileName
def SetOutputDirectory(directory: str) -> None:
global globalDirectory
globalDirectory = directory
def SetTargetApiName(apiname: str) -> None:
global globalApiName
globalApiName = apiname
def SetMergedApiNames(names: str) -> None:
global mergedApiNames
mergedApiNames = names
cachingEnabled = False
def EnableCaching() -> None:
global cachingEnabled
cachingEnabled = True
# This class is a container for any source code, data, or other behavior that is necessary to
# customize the generator script for a specific target API variant (e.g. Vulkan SC). As such,
# all of these API-specific interfaces and their use in the generator script are part of the
# contract between this repository and its downstream users. Changing or removing any of these
# interfaces or their use in the generator script will have downstream effects and thus
# should be avoided unless absolutely necessary.
class APISpecific:
# Version object factory method
@staticmethod
def createApiVersion(targetApiName: str, name: str) -> Version:
match targetApiName:
# Vulkan specific API version creation
case 'vulkan':
nameApi = name.replace('VK_', 'VK_API_')
nameString = f'"{name}"'
return Version(name, nameString, nameApi)
# This Generator Option is used across all generators.
# After years of use, it has shown that most the options are unified across each generator (file)
# as it is easier to modifiy things per-file that need the difference
class BaseGeneratorOptions(GeneratorOptions):
def __init__(self,
customFileName = None,
customDirectory = None,
customApiName = None):
GeneratorOptions.__init__(self,
conventions = vulkanConventions,
filename = customFileName if customFileName else globalFileName,
directory = customDirectory if customDirectory else globalDirectory,
apiname = customApiName if customApiName else globalApiName,
mergeApiNames = mergedApiNames,
defaultExtensions = customApiName if customApiName else globalApiName,
emitExtensions = '.*',
emitSpirv = '.*',
emitFormats = '.*')
# These are used by the generator.py script
self.apicall = 'VKAPI_ATTR '
self.apientry = 'VKAPI_CALL '
self.apientryp = 'VKAPI_PTR *'
self.alignFuncParam = 48
#
# This object handles all the parsing from reg.py generator scripts in the Vulkan-Headers
# It will grab all the data and form it into a single object the rest of the generators will use
class BaseGenerator(OutputGenerator):
def __init__(self):
OutputGenerator.__init__(self, None, None, None)
self.vk = VulkanObject()
self.targetApiName = globalApiName
# reg.py has a `self.featureName` but this is nicer because
# it will be either the Version or Extension object
self.currentExtension = None
self.currentVersion = None
# Will map alias to promoted name
# ex. ['VK_FILTER_CUBIC_IMG' : 'VK_FILTER_CUBIC_EXT']
# When generating any code, there is no reason so use the old name
self.enumAliasMap = dict()
self.enumFieldAliasMap = dict()
self.bitmaskAliasMap = dict()
self.flagAliasMap = dict()
self.structAliasMap = dict()
def write(self, data):
# Prevents having to check before writting
if data is not None and data != "":
write(data, file=self.outFile)
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
self.filename = genOpts.filename
# No gen*() command to get these, so do it manually
for platform in self.registry.tree.findall('platforms/platform'):
self.vk.platforms[platform.get('name')] = platform.get('protect')
for tags in self.registry.tree.findall('tags'):
for tag in tags.findall('tag'):
self.vk.vendorTags.append(tag.get('name'))
# No way known to get this from the XML
self.vk.queueBits[Queues.TRANSFER] = 'VK_QUEUE_TRANSFER_BIT'
self.vk.queueBits[Queues.GRAPHICS] = 'VK_QUEUE_GRAPHICS_BIT'
self.vk.queueBits[Queues.COMPUTE] = 'VK_QUEUE_COMPUTE_BIT'
self.vk.queueBits[Queues.PROTECTED] = 'VK_QUEUE_PROTECTED_BIT'
self.vk.queueBits[Queues.SPARSE_BINDING] = 'VK_QUEUE_SPARSE_BINDING_BIT'
self.vk.queueBits[Queues.OPTICAL_FLOW] = 'VK_QUEUE_OPTICAL_FLOW_BIT_NV'
self.vk.queueBits[Queues.DECODE] = 'VK_QUEUE_VIDEO_DECODE_BIT_KHR'
self.vk.queueBits[Queues.ENCODE] = 'VK_QUEUE_VIDEO_ENCODE_BIT_KHR'
# This function should be overloaded
def generate(self):
print("WARNING: This should not be called from the child class")
return
# This function is dense, it does all the magic to set the right extensions dependencies!
#
# The issue is if 2 extension expose a command, genCmd() will only
# show one of the extension, at endFile() we can finally go through
# and update which things depend on which extensions
#
# self.featureDictionary is built for use in the reg.py framework
# Details found in Vulkan-Docs/scripts/scriptgenerator.py
def applyExtensionDependency(self):
for extension in self.vk.extensions.values():
# dict.key() can be None, so need to double loop
dict = self.featureDictionary[extension.name]['command']
# "required" == None
# or
# an additional feature dependency, which is a boolean expression of
# one or more extension and/or core version names
for required in dict:
for commandName in dict[required]:
# Skip commands removed in the target API
# This check is needed because parts of the base generator code bypass the
# dependency resolution logic in the registry tooling and thus the generator
# may attempt to generate code for commands which are not supported in the
# target API variant, thus this check needs to happen even if any specific
# target API variant may not specifically need it
if not commandName in self.vk.commands:
continue
command = self.vk.commands[commandName]
# Make sure list is unique
command.extensions.extend([extension] if extension not in command.extensions else [])
extension.commands.extend([command] if command not in extension.commands else [])
# While genGroup() will call twice with aliased value, it doesn't provide all the information we need
dict = self.featureDictionary[extension.name]['enumconstant']
for required in dict:
# group can be a Enum or Bitmask
for group in dict[required]:
if group in self.vk.enums:
if group not in extension.enumFields:
extension.enumFields[group] = [] # Dict needs init
enum = self.vk.enums[group]
# Need to convert all alias so they match what is in EnumField
enumList = list(map(lambda x: x if x not in self.enumFieldAliasMap else self.enumFieldAliasMap[x], dict[required][group]))
for enumField in [x for x in enum.fields if x.name in enumList]:
# Make sure list is unique
enum.fieldExtensions.extend([extension] if extension not in enum.fieldExtensions else [])
enumField.extensions.extend([extension] if extension not in enumField.extensions else [])
extension.enumFields[group].extend([enumField] if enumField not in extension.enumFields[group] else [])
if group in self.vk.bitmasks:
if group not in extension.flags:
extension.flags[group] = [] # Dict needs init
bitmask = self.vk.bitmasks[group]
# Need to convert all alias so they match what is in Flags
flagList = list(map(lambda x: x if x not in self.flagAliasMap else self.flagAliasMap[x], dict[required][group]))
for flags in [x for x in bitmask.flags if x.name in flagList]:
# Make sure list is unique
bitmask.flagExtensions.extend([extension] if extension not in bitmask.flagExtensions else [])
flags.extensions.extend([extension] if extension not in flags.extensions else [])
extension.flags[group].extend([flags] if flags not in extension.flags[group] else [])
# Need to do 'enum'/'bitmask' after 'enumconstant' has applied everything so we can add implicit extensions
#
# Sometimes two extensions enable an Enum, but the newer extension version has extra flags allowed
# This information seems to be implicit, so need to update it here
# Go through each Flag and append the Enum extension to it
#
# ex. VkAccelerationStructureTypeKHR where GENERIC_KHR is not allowed with just VK_NV_ray_tracing
# This only works because the values are aliased as well, making the KHR a superset enum
for extension in self.vk.extensions.values():
dict = self.featureDictionary[extension.name]['enum']
for required in dict:
for group in dict[required]:
for enumName in dict[required][group]:
isAlias = enumName in self.enumAliasMap
enumName = self.enumAliasMap[enumName] if isAlias else enumName
if enumName in self.vk.enums:
enum = self.vk.enums[enumName]
enum.extensions.extend([extension] if extension not in enum.extensions else [])
extension.enums.extend([enum] if enum not in extension.enums else [])
# Update fields with implicit base extension
if isAlias:
continue
enum.fieldExtensions.extend([extension] if extension not in enum.fieldExtensions else [])
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)))]:
enumField.extensions.extend([extension] if extension not in enumField.extensions else [])
if enumName not in extension.enumFields:
extension.enumFields[enumName] = [] # Dict needs init
extension.enumFields[enumName].extend([enumField] if enumField not in extension.enumFields[enumName] else [])
dict = self.featureDictionary[extension.name]['bitmask']
for required in dict:
for group in dict[required]:
for bitmaskName in dict[required][group]:
bitmaskName = bitmaskName.replace('Flags', 'FlagBits') # Works since Flags isn't repeated in name
isAlias = bitmaskName in self.bitmaskAliasMap
bitmaskName = self.bitmaskAliasMap[bitmaskName] if isAlias else bitmaskName
if bitmaskName in self.vk.bitmasks:
bitmask = self.vk.bitmasks[bitmaskName]
bitmask.extensions.extend([extension] if extension not in bitmask.extensions else [])
extension.bitmasks.extend([bitmask] if bitmask not in extension.bitmasks else [])
# Update flags with implicit base extension
if isAlias:
continue
bitmask.flagExtensions.extend([extension] if extension not in bitmask.flagExtensions else [])
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)))]:
flag.extensions.extend([extension] if extension not in flag.extensions else [])
if bitmaskName not in extension.flags:
extension.flags[bitmaskName] = [] # Dict needs init
extension.flags[bitmaskName].extend([flag] if flag not in extension.flags[bitmaskName] else [])
# Some structs (ex VkAttachmentSampleCountInfoAMD) can have multiple alias pointing to same extension
for extension in self.vk.extensions.values():
dict = self.featureDictionary[extension.name]['struct']
for required in dict:
for group in dict[required]:
for structName in dict[required][group]:
isAlias = structName in self.structAliasMap
structName = self.structAliasMap[structName] if isAlias else structName
# An EXT struct can alias a KHR struct,
# that in turns aliaes a core struct
# => Try to propagate aliasing, it can safely result in a no-op
isAlias = structName in self.structAliasMap
structName = self.structAliasMap[structName] if isAlias else structName
if structName in self.vk.structs:
struct = self.vk.structs[structName]
struct.extensions.extend([extension] if extension not in struct.extensions else [])
# While we update struct alias inside other structs, the command itself might have the struct as a first level param.
# We use this time to update params to have the promoted name
# Example - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9322
for command in self.vk.commands.values():
for member in command.params:
if member.type in self.structAliasMap:
member.type = self.structAliasMap[member.type]
def endFile(self):
# This is the point were reg.py has ran, everything is collected
# We do some post processing now
self.applyExtensionDependency()
# Use structs and commands to find which things are returnedOnly
for struct in [x for x in self.vk.structs.values() if not x.returnedOnly]:
for enum in [self.vk.enums[x.type] for x in struct.members if x.type in self.vk.enums]:
enum.returnedOnly = False
for bitmask in [self.vk.bitmasks[x.type] for x in struct.members if x.type in self.vk.bitmasks]:
bitmask.returnedOnly = False
for bitmask in [self.vk.bitmasks[x.type.replace('Flags', 'FlagBits')] for x in struct.members if x.type.replace('Flags', 'FlagBits') in self.vk.bitmasks]:
bitmask.returnedOnly = False
for command in self.vk.commands.values():
for enum in [self.vk.enums[x.type] for x in command.params if x.type in self.vk.enums]:
enum.returnedOnly = False
for bitmask in [self.vk.bitmasks[x.type] for x in command.params if x.type in self.vk.bitmasks]:
bitmask.returnedOnly = False
for bitmask in [self.vk.bitmasks[x.type.replace('Flags', 'FlagBits')] for x in command.params if x.type.replace('Flags', 'FlagBits') in self.vk.bitmasks]:
bitmask.returnedOnly = False
# Turn handle parents into pointers to classess
for handle in [x for x in self.vk.handles.values() if x.parent is not None]:
handle.parent = self.vk.handles[handle.parent]
# search up parent chain to see if instance or device
for handle in [x for x in self.vk.handles.values()]:
next_parent = handle.parent
while (not handle.instance and not handle.device):
handle.instance = next_parent.name == 'VkInstance'
handle.device = next_parent.name == 'VkDevice'
next_parent = next_parent.parent
maxSyncSupport.queues = Queues.ALL
maxSyncSupport.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags
maxSyncEquivalent.accesses = self.vk.bitmasks['VkAccessFlagBits2'].flags
maxSyncEquivalent.stages = self.vk.bitmasks['VkPipelineStageFlagBits2'].flags
# All inherited generators should run from here
self.generate()
if cachingEnabled:
cachePath = os.path.join(tempfile.gettempdir(), f'vkobject_{os.getpid()}')
if not os.path.isfile(cachePath):
cacheFile = open(cachePath, 'wb')
pickle.dump(self.vk, cacheFile)
cacheFile.close()
# This should not have to do anything but call into OutputGenerator
OutputGenerator.endFile(self)
#
# Bypass the entire processing and load in the VkObject data
# Still need to handle the beingFile/endFile for reg.py
def generateFromCache(self, cacheVkObjectData, genOpts):
OutputGenerator.beginFile(self, genOpts)
self.filename = genOpts.filename
self.vk = cacheVkObjectData
self.generate()
OutputGenerator.endFile(self)
#
# Processing point at beginning of each extension definition
def beginFeature(self, interface, emit):
OutputGenerator.beginFeature(self, interface, emit)
platform = interface.get('platform')
self.featureExtraProtec = self.vk.platforms[platform] if platform in self.vk.platforms else None
protect = self.vk.platforms[platform] if platform in self.vk.platforms else None
name = interface.get('name')
if interface.tag == 'extension':
instance = interface.get('type') == 'instance'
device = not instance
depends = interface.get('depends')
vendorTag = interface.get('author')
platform = interface.get('platform')
provisional = boolGet(interface, 'provisional')
promotedto = interface.get('promotedto')
deprecatedby = interface.get('deprecatedby')
obsoletedby = interface.get('obsoletedby')
specialuse = splitIfGet(interface, 'specialuse')
# Not sure if better way to get this info
specVersion = self.featureDictionary[name]['enumconstant'][None][None][0]
nameString = self.featureDictionary[name]['enumconstant'][None][None][1]
self.currentExtension = Extension(name, nameString, specVersion, instance, device, depends, vendorTag,
platform, protect, provisional, promotedto, deprecatedby,
obsoletedby, specialuse)
self.vk.extensions[name] = self.currentExtension
else: # version
number = interface.get('number')
if number != '1.0':
self.currentVersion = APISpecific.createApiVersion(self.targetApiName, name)
self.vk.versions[name] = self.currentVersion
def endFeature(self):
OutputGenerator.endFeature(self)
self.currentExtension = None
self.currentVersion = None
#
# All <command> from XML
def genCmd(self, cmdinfo, name, alias):
OutputGenerator.genCmd(self, cmdinfo, name, alias)
params = []
for param in cmdinfo.elem.findall('param'):
paramName = param.find('name').text
paramType = textIfFind(param, 'type')
paramAlias = param.get('alias')
cdecl = self.makeCParamDecl(param, 0)
pointer = '*' in cdecl or paramType.startswith('PFN_')
paramConst = 'const' in cdecl
fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')]
paramNoautovalidity = boolGet(param, 'noautovalidity')
nullTerminated = False
length = param.get('altlen') if param.get('altlen') is not None else param.get('len')
if length:
# we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated"
# This finds both
nullTerminated = 'null-terminated' in length
length = length.replace(',null-terminated', '') if 'null-terminated' in length else length
length = None if length == 'null-terminated' else length
if fixedSizeArray and not length:
length = ','.join(fixedSizeArray)
# See Member::optional code for details of this
optionalValues = splitIfGet(param, 'optional')
optional = optionalValues is not None and optionalValues[0].lower() == "true"
optionalPointer = optionalValues is not None and len(optionalValues) > 1 and optionalValues[1].lower() == "true"
# externsync will be 'true' or expression
# if expression, it should be same as 'true'
externSync = boolGet(param, 'externsync')
externSyncPointer = None if externSync else splitIfGet(param, 'externsync')
if not externSync and externSyncPointer is not None:
externSync = True
params.append(Param(paramName, paramAlias, paramType, paramNoautovalidity,
paramConst, length, nullTerminated, pointer, fixedSizeArray,
optional, optionalPointer,
externSync, externSyncPointer, cdecl))
attrib = cmdinfo.elem.attrib
alias = attrib.get('alias')
tasks = splitIfGet(attrib, 'tasks')
queues = getQueues(attrib)
successcodes = splitIfGet(attrib, 'successcodes')
errorcodes = splitIfGet(attrib, 'errorcodes')
cmdbufferlevel = attrib.get('cmdbufferlevel')
primary = cmdbufferlevel is not None and 'primary' in cmdbufferlevel
secondary = cmdbufferlevel is not None and 'secondary' in cmdbufferlevel
renderpass = attrib.get('renderpass')
renderpass = CommandScope.NONE if renderpass is None else getattr(CommandScope, renderpass.upper())
videocoding = attrib.get('videocoding')
videocoding = CommandScope.NONE if videocoding is None else getattr(CommandScope, videocoding.upper())
protoElem = cmdinfo.elem.find('proto')
returnType = textIfFind(protoElem, 'type')
decls = self.makeCDecls(cmdinfo.elem)
cPrototype = decls[0]
cFunctionPointer = decls[1]
protect = self.currentExtension.protect if self.currentExtension is not None else None
# These coammds have no way from the XML to detect they would be an instance command
specialInstanceCommand = ['vkCreateInstance', 'vkEnumerateInstanceExtensionProperties','vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion']
instance = len(params) > 0 and (params[0].type == 'VkInstance' or params[0].type == 'VkPhysicalDevice' or name in specialInstanceCommand)
device = not instance
implicitElem = cmdinfo.elem.find('implicitexternsyncparams')
implicitExternSyncParams = [x.text for x in implicitElem.findall('param')] if implicitElem else []
self.vk.commands[name] = Command(name, alias, protect, [], self.currentVersion,
returnType, params, instance, device,
tasks, queues, successcodes, errorcodes,
primary, secondary, renderpass, videocoding,
implicitExternSyncParams, cPrototype, cFunctionPointer)
#
# List the enum for the commands
# TODO - Seems empty groups like `VkDeviceDeviceMemoryReportCreateInfoEXT` don't show up in here
def genGroup(self, groupinfo, groupName, alias):
# There can be case where the Enum/Bitmask is in a protect, but the individual
# fields also have their own protect
groupProtect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None
enumElem = groupinfo.elem
bitwidth = 32 if enumElem.get('bitwidth') is None else int(enumElem.get('bitwidth'))
fields = []
if enumElem.get('type') == "enum":
if alias is not None:
self.enumAliasMap[groupName] = alias
return
for elem in enumElem.findall('enum'):
fieldName = elem.get('name')
if elem.get('alias') is not None:
self.enumFieldAliasMap[fieldName] = elem.get('alias')
continue
negative = elem.get('dir') is not None
protect = elem.get('protect')
# Some values have multiple extensions (ex VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR)
# genGroup() lists them twice
if next((x for x in fields if x.name == fieldName), None) is None:
fields.append(EnumField(fieldName, negative, protect, []))
self.vk.enums[groupName] = Enum(groupName, groupProtect, bitwidth, True, fields, [], [])
else: # "bitmask"
if alias is not None:
self.bitmaskAliasMap[groupName] = alias
return
for elem in enumElem.findall('enum'):
flagName = elem.get('name')
if elem.get('alias') is not None:
self.flagAliasMap[flagName] = elem.get('alias')
continue
flagMultiBit = False
flagZero = False
flagValue = intIfGet(elem, 'bitpos')
if flagValue is None:
flagValue = intIfGet(elem, 'value')
flagMultiBit = flagValue != 0
flagZero = flagValue == 0
protect = elem.get('protect')
# Some values have multiple extensions (ex VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT)
# genGroup() lists them twice
if next((x for x in fields if x.name == flagName), None) is None:
fields.append(Flag(flagName, protect, flagValue, flagMultiBit, flagZero, []))
flagName = groupName.replace('FlagBits', 'Flags')
self.vk.bitmasks[groupName] = Bitmask(groupName, flagName, groupProtect, bitwidth, True, fields, [], [])
def genType(self, typeInfo, typeName, alias):
OutputGenerator.genType(self, typeInfo, typeName, alias)
typeElem = typeInfo.elem
protect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None
category = typeElem.get('category')
if (category == 'struct' or category == 'union'):
extension = [self.currentExtension] if self.currentExtension is not None else []
if alias is not None:
self.structAliasMap[typeName] = alias
return
union = category == 'union'
returnedOnly = boolGet(typeElem, 'returnedonly')
allowDuplicate = boolGet(typeElem, 'allowduplicate')
extends = splitIfGet(typeElem, 'structextends')
extendedBy = self.registry.validextensionstructs[typeName] if len(self.registry.validextensionstructs[typeName]) > 0 else None
membersElem = typeInfo.elem.findall('.//member')
members = []
sType = None
for member in membersElem:
for comment in member.findall('comment'):
member.remove(comment)
name = textIfFind(member, 'name')
type = textIfFind(member, 'type')
sType = member.get('values') if member.get('values') is not None else sType
externSync = boolGet(member, 'externsync')
noautovalidity = boolGet(member, 'noautovalidity')
limittype = member.get('limittype')
nullTerminated = False
length = member.get('altlen') if member.get('altlen') is not None else member.get('len')
if length:
# we will either find it like "null-terminated" or "enabledExtensionCount,null-terminated"
# This finds both
nullTerminated = 'null-terminated' in length
length = length.replace(',null-terminated', '') if 'null-terminated' in length else length
length = None if length == 'null-terminated' else length
cdecl = self.makeCParamDecl(member, 0)
pointer = '*' in cdecl or type.startswith('PFN_')
const = 'const' in cdecl
# Some structs like VkTransformMatrixKHR have a 2D array
fixedSizeArray = [x[:-1] for x in cdecl.split('[') if x.endswith(']')]
if fixedSizeArray and not length:
length = ','.join(fixedSizeArray)
# if a pointer, this can be a something like:
# optional="true,false" for ppGeometries
# optional="false,true" for pPhysicalDeviceCount
# the first is if the variable itself is optional
# the second is the value of the pointer is optiona;
optionalValues = splitIfGet(member, 'optional')
optional = optionalValues is not None and optionalValues[0].lower() == "true"
optionalPointer = optionalValues is not None and len(optionalValues) > 1 and optionalValues[1].lower() == "true"
members.append(Member(name, type, noautovalidity, limittype,
const, length, nullTerminated, pointer, fixedSizeArray,
optional, optionalPointer,
externSync, cdecl))
self.vk.structs[typeName] = Struct(typeName, extension, self.currentVersion, protect, members,
union, returnedOnly, sType, allowDuplicate, extends, extendedBy)
elif category == 'handle':
if alias is not None:
return
type = typeElem.get('objtypeenum')
# will resolve these later, the VulkanObjectType doesn't list things in dependent order
parent = typeElem.get('parent')
instance = typeName == 'VkInstance'
device = typeName == 'VkDevice'
dispatchable = typeElem.find('type').text == 'VK_DEFINE_HANDLE'
self.vk.handles[typeName] = Handle(typeName, type, protect, parent, instance, device, dispatchable)
elif category == 'define':
if typeName == 'VK_HEADER_VERSION':
self.vk.headerVersion = typeElem.find('name').tail.strip()
else:
# not all categories are used
# 'group'/'enum'/'bitmask' are routed to genGroup instead
# 'basetype'/'include' are only for headers
# 'funcpointer` ingore until needed
return
def genSpirv(self, spirvinfo, spirvName, alias):
OutputGenerator.genSpirv(self, spirvinfo, spirvName, alias)
spirvElem = spirvinfo.elem
name = spirvElem.get('name')
extension = True if spirvElem.tag == 'spirvextension' else False
capability = not extension
enables = []
for elem in spirvElem:
version = elem.attrib.get('version')
extensionEnable = elem.attrib.get('extension')
struct = elem.attrib.get('struct')
feature = elem.attrib.get('feature')
requires = elem.attrib.get('requires')
propertyEnable = elem.attrib.get('property')
member = elem.attrib.get('member')
value = elem.attrib.get('value')
enables.append(SpirvEnables(version, extensionEnable, struct, feature,
requires, propertyEnable, member, value))
self.vk.spirv.append(Spirv(name, extension, capability, enables))
def genFormat(self, format, formatinfo, alias):
OutputGenerator.genFormat(self, format, formatinfo, alias)
formatElem = format.elem
name = formatElem.get('name')
components = []
for component in formatElem.iterfind('component'):
type = component.get('name')
bits = component.get('bits')
numericFormat = component.get('numericFormat')
planeIndex = intIfGet(component, 'planeIndex')
components.append(FormatComponent(type, bits, numericFormat, planeIndex))
planes = []
for plane in formatElem.iterfind('plane'):
index = int(plane.get('index'))
widthDivisor = int(plane.get('widthDivisor'))
heightDivisor = int(plane.get('heightDivisor'))
compatible = plane.get('compatible')
planes.append(FormatPlane(index, widthDivisor, heightDivisor, compatible))
className = formatElem.get('class')
blockSize = int(formatElem.get('blockSize'))
texelsPerBlock = int(formatElem.get('texelsPerBlock'))
blockExtent = splitIfGet(formatElem, 'blockExtent')
packed = intIfGet(formatElem, 'packed')
chroma = formatElem.get('chroma')
compressed = formatElem.get('compressed')
spirvImageFormat = formatElem.find('spirvimageformat')
if spirvImageFormat is not None:
spirvImageFormat = spirvImageFormat.get('name')
self.vk.formats[name] = Format(name, className, blockSize, texelsPerBlock,
blockExtent, packed, chroma, compressed,
components, planes, spirvImageFormat)
def genSyncStage(self, sync):
OutputGenerator.genSyncStage(self, sync)
syncElem = sync.elem
support = maxSyncSupport
supportElem = syncElem.find('syncsupport')
if supportElem is not None:
queues = getQueues(supportElem)
stageNames = splitIfGet(supportElem, 'stage')
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if stageNames is not None else None
support = SyncSupport(queues, stages, False)
equivalent = maxSyncEquivalent
equivalentElem = syncElem.find('syncequivalent')
if equivalentElem is not None:
stageNames = splitIfGet(equivalentElem, 'stage')
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if stageNames is not None else None
accessNames = splitIfGet(equivalentElem, 'access')
accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if accessNames is not None else None
equivalent = SyncEquivalent(stages, accesses, False)
flagName = syncElem.get('name')
flag = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name == flagName]
# This check is needed because not all API variants have VK_KHR_synchronization2
if flag:
self.vk.syncStage.append(SyncStage(flag[0], support, equivalent))
def genSyncAccess(self, sync):
OutputGenerator.genSyncAccess(self, sync)
syncElem = sync.elem
support = maxSyncSupport
supportElem = syncElem.find('syncsupport')
if supportElem is not None:
queues = getQueues(supportElem)
stageNames = splitIfGet(supportElem, 'stage')
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if stageNames is not None else None
support = SyncSupport(queues, stages, False)
equivalent = maxSyncEquivalent
equivalentElem = syncElem.find('syncequivalent')
if equivalentElem is not None:
stageNames = splitIfGet(equivalentElem, 'stage')
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if stageNames is not None else None
accessNames = splitIfGet(equivalentElem, 'access')
accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if accessNames is not None else None
equivalent = SyncEquivalent(stages, accesses, False)
flagName = syncElem.get('name')
flag = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name == flagName]
# This check is needed because not all API variants have VK_KHR_synchronization2
if flag:
self.vk.syncAccess.append(SyncAccess(flag[0], support, equivalent))
def genSyncPipeline(self, sync):
OutputGenerator.genSyncPipeline(self, sync)
syncElem = sync.elem
name = syncElem.get('name')
depends = splitIfGet(syncElem, 'depends')
stages = []
for stageElem in syncElem.findall('syncpipelinestage'):
order = stageElem.get('order')
before = stageElem.get('before')
after = stageElem.get('after')
value = stageElem.text
stages.append(SyncPipelineStage(order, before, after, value))
self.vk.syncPipeline.append(SyncPipeline(name, depends, stages))

View file

@ -7,7 +7,7 @@
# SPDX-License-Identifier: Apache-2.0
import os
from generators.base_generator import BaseGenerator
from base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper
class DispatchTableOutputGenerator(BaseGenerator):

View file

@ -7,7 +7,7 @@
# SPDX-License-Identifier: Apache-2.0
import os
from generators.base_generator import BaseGenerator
from base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper
class EnumStringHelperOutputGenerator(BaseGenerator):

View file

@ -7,8 +7,8 @@
# SPDX-License-Identifier: Apache-2.0
import os
from generators.vulkan_object import (Format)
from generators.base_generator import BaseGenerator
from vulkan_object import (Format)
from base_generator import BaseGenerator
# Make C name friendly class name
def getClassName(className: str) -> str:

View file

@ -10,8 +10,8 @@
import os
import re
from generators.vulkan_object import Struct, Member
from generators.base_generator import BaseGenerator
from vulkan_object import Struct, Member
from base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper
class SafeStructOutputGenerator(BaseGenerator):

View file

@ -19,7 +19,7 @@
# limitations under the License.
import os
from generators.base_generator import BaseGenerator
from base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper
class StructHelperOutputGenerator(BaseGenerator):

View file

@ -1,405 +0,0 @@
#!/usr/bin/python3 -i
#
# Copyright (c) 2023-2024 Valve Corporation
# Copyright (c) 2023-2024 LunarG, Inc.
#
# SPDX-License-Identifier: Apache-2.0
from dataclasses import dataclass, field
from enum import IntFlag, Enum, auto
@dataclass
class Extension:
"""<extension>"""
name: str # ex) VK_KHR_SURFACE
nameString: str # marco with string, ex) VK_KHR_SURFACE_EXTENSION_NAME
specVersion: str # marco with string, ex) VK_KHR_SURFACE_SPEC_VERSION
# Only one will be True, the other is False
instance: bool
device: bool
depends: (str | None)
vendorTag: (str | None) # ex) EXT, KHR, etc
platform: (str | None) # ex) android
protect: (str | None) # ex) VK_USE_PLATFORM_ANDROID_KHR
provisional: bool
promotedTo: (str | None) # ex) VK_VERSION_1_1
deprecatedBy: (str | None)
obsoletedBy: (str | None)
specialUse: list[str]
# These are here to allow for easy reverse lookups
# Quotes allow us to forward declare the dataclass
commands: list['Command'] = field(default_factory=list, init=False)
enums: list['Enum'] = field(default_factory=list, init=False)
bitmasks: list['Bitmask'] = field(default_factory=list, init=False)
# Use the Enum name to see what fields are extended
enumFields: dict[str, list['EnumField']] = field(default_factory=dict, init=False)
# Use the Bitmaks name to see what flags are extended
flags: dict[str, list['Flag']] = field(default_factory=dict, init=False)
@dataclass
class Version:
"""
<feature> which represents a version
This will NEVER be Version 1.0, since having 'no version' is same as being 1.0
"""
name: str # ex) VK_VERSION_1_1
nameString: str # ex) "VK_VERSION_1_1" (no marco, so has quotes)
nameApi: str # ex) VK_API_VERSION_1_1
@dataclass
class Handle:
"""<type> which represents a dispatch handle"""
name: str # ex) VkBuffer
type: str # ex) VK_OBJECT_TYPE_BUFFER
protect: (str | None) # ex) VK_USE_PLATFORM_ANDROID_KHR
parent: 'Handle' # Chain of parent handles, can be None
# Only one will be True, the other is False
instance: bool
device: bool
dispatchable: bool
def __lt__(self, other):
return self.name < other.name
@dataclass
class Param:
"""<command/param>"""
name: str
alias: str
type: str # ex) void, VkFormat, etc
noAutoValidity: bool
const: bool # type contains 'const'
length: (str | None) # the known length of pointer, will never be 'null-terminated'
nullTerminated: bool # If a UTF-8 string, it will be null-terminated
pointer: bool # type contains a pointer (include 'PFN' function pointers)
fixedSizeArray: list[str] # for VkTransformMatrixKHR:matrix this is [3, 4]
optional: bool
optionalPointer: bool # if type contains a pointer, is the pointer value optional
externSync: bool
externSyncPointer: list[str] # if type contains a pointer, might only specific members modified
# C string of member, example:
# - const void* pNext
# - VkFormat format
# - VkStructureType sType
cDeclaration: str
def __lt__(self, other):
return self.name < other.name
class Queues(IntFlag):
TRANSFER = auto() # VK_QUEUE_TRANSFER_BIT
GRAPHICS = auto() # VK_QUEUE_GRAPHICS_BIT
COMPUTE = auto() # VK_QUEUE_COMPUTE_BIT
PROTECTED = auto() # VK_QUEUE_PROTECTED_BIT
SPARSE_BINDING = auto() # VK_QUEUE_SPARSE_BINDING_BIT
OPTICAL_FLOW = auto() # VK_QUEUE_OPTICAL_FLOW_BIT_NV
DECODE = auto() # VK_QUEUE_VIDEO_DECODE_BIT_KHR
ENCODE = auto() # VK_QUEUE_VIDEO_ENCODE_BIT_KHR
ALL = TRANSFER | GRAPHICS | COMPUTE | PROTECTED | SPARSE_BINDING | OPTICAL_FLOW | DECODE | ENCODE
class CommandScope(Enum):
NONE = auto()
INSIDE = auto()
OUTSIDE = auto()
BOTH = auto()
@dataclass
class Command:
"""<command>"""
name: str # ex) vkCmdDraw
alias: str # Because commands are interfaces into layers/drivers, we need all command alias
protect: (str | None) # ex) 'VK_ENABLE_BETA_EXTENSIONS'
extensions: list[Extension] # All extensions that enable the struct
version: (Version | None) # None if Version 1.0
returnType: str # ex) void, VkResult, etc
params: list[Param] # Each parameter of the command
# Only one will be True, the other is False
instance: bool
device: bool
tasks: list[str] # ex) [ action, state, synchronization ]
queues: Queues # zero == No Queues found
successCodes: list[str] # ex) [ VK_SUCCESS, VK_INCOMPLETE ]
errorCodes: list[str] # ex) [ VK_ERROR_OUT_OF_HOST_MEMORY ]
# Shows support if command can be in a primary and/or secondary command buffer
primary: bool
secondary: bool
renderPass: CommandScope
videoCoding: CommandScope
implicitExternSyncParams: list[str]
# C prototype string - ex:
# VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
# const VkInstanceCreateInfo* pCreateInfo,
# const VkAllocationCallbacks* pAllocator,
# VkInstance* pInstance);
cPrototype: str
# function pointer typedef - ex:
# typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)
# (const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
cFunctionPointer: str
def __lt__(self, other):
return self.name < other.name
@dataclass
class Member:
"""<member>"""
name: str # ex) sharingMode
type: str # ex) VkSharingMode
noAutoValidity: bool
limitType: (str | None) # ex) 'max', 'bitmask', 'bits', 'min,mul'
const: bool # type contains 'const'
length: (str | None) # the known length of pointer, will never be 'null-terminated'
nullTerminated: bool # If a UTF-8 string, it will be null-terminated
pointer: bool # type contains a pointer (include 'PFN' function pointers)
fixedSizeArray: list[str] # for VkTransformMatrixKHR:matrix this is [3, 4]
optional: bool
optionalPointer: bool # if type contains a pointer, is the pointer value optional
externSync: bool
# C string of member, example:
# - const void* pNext
# - VkFormat format
# - VkStructureType sType
cDeclaration: str
def __lt__(self, other):
return self.name < other.name
@dataclass
class Struct:
"""<type category="struct"> or <type category="union">"""
name: str # ex. VkBufferCreateInfo
extensions: list[Extension] # All extensions that enable the struct
version: (Version | None) # None if Version 1.0
protect: (str | None) # ex) VK_ENABLE_BETA_EXTENSIONS
members: list[Member]
union: bool # Unions are just a subset of a Structs
returnedOnly: bool
sType: (str | None) # ex) VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
allowDuplicate: bool # can have a pNext point to itself
# These use to be list['Struct'] but some circular loops occur and cause
# pydevd warnings and made debugging slow (30 seconds to index a Struct)
extends: list[str] # Struct names that this struct extends
extendedBy: list[str] # Struct names that can be extended by this struct
def __lt__(self, other):
return self.name < other.name
@dataclass
class EnumField:
"""<enum> of type enum"""
name: str # ex) VK_DYNAMIC_STATE_SCISSOR
negative: bool # True if negative values are allowed (ex. VkResult)
protect: (str | None) # ex) VK_ENABLE_BETA_EXTENSIONS
# some fields are enabled from 2 extensions (ex) VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR)
extensions: list[Extension] # None if part of 1.0 core
def __lt__(self, other):
return self.name < other.name
@dataclass
class Enum:
"""<enums> of type enum"""
name: str # ex) VkDynamicState
protect: (str | None) # ex) VK_ENABLE_BETA_EXTENSIONS
bitWidth: int # 32 or 64
returnedOnly: bool
fields: list[EnumField]
extensions: list[Extension] # None if part of 1.0 core
# Unique list of all extension that are involved in 'fields' (superset of 'extensions')
fieldExtensions: list[Extension]
def __lt__(self, other):
return self.name < other.name
@dataclass
class Flag:
"""<enum> of type bitmask"""
name: str # ex) VK_ACCESS_2_SHADER_READ_BIT
protect: (str | None) # ex) VK_ENABLE_BETA_EXTENSIONS
value: int
multiBit: bool # if true, more than one bit is set (ex) VK_SHADER_STAGE_ALL_GRAPHICS)
zero: bool # if true, the value is zero (ex) VK_PIPELINE_STAGE_NONE)
# some fields are enabled from 2 extensions (ex) VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT)
extensions: list[Extension] # None if part of 1.0 core
def __lt__(self, other):
return self.name < other.name
@dataclass
class Bitmask:
"""<enums> of type bitmask"""
name: str # ex) VkAccessFlagBits2
flagName: str # ex) VkAccessFlags2
protect: (str | None) # ex) VK_ENABLE_BETA_EXTENSIONS
bitWidth: int # 32 or 64
returnedOnly: bool
flags: list[Flag]
extensions: list[Extension] # None if part of 1.0 core
# Unique list of all extension that are involved in 'flag' (superset of 'extensions')
flagExtensions: list[Extension]
def __lt__(self, other):
return self.name < other.name
@dataclass
class FormatComponent:
"""<format/component>"""
type: str # ex) R, G, B, A, D, S, etc
bits: str # will be an INT or 'compressed'
numericFormat: str # ex) UNORM, SINT, etc
planeIndex: (int | None) # None if no planeIndex in format
@dataclass
class FormatPlane:
"""<format/plane>"""
index: int
widthDivisor: int
heightDivisor: int
compatible: str
@dataclass
class Format:
"""<format>"""
name: str
className: str
blockSize: int
texelsPerBlock: int
blockExtent: list[str]
packed: (int | None) # None == not-packed
chroma: (str | None)
compressed: (str | None)
components: list[FormatComponent] # <format/component>
planes: list[FormatPlane] # <format/plane>
spirvImageFormat: (str | None)
@dataclass
class SyncSupport:
"""<syncsupport>"""
queues: Queues
stages: list[Flag] # VkPipelineStageFlagBits2
max: bool # If this supports max values
@dataclass
class SyncEquivalent:
"""<syncequivalent>"""
stages: list[Flag] # VkPipelineStageFlagBits2
accesses: list[Flag] # VkAccessFlagBits2
max: bool # If this equivalent to everything
@dataclass
class SyncStage:
"""<syncstage>"""
flag: Flag # VkPipelineStageFlagBits2
support: SyncSupport
equivalent: SyncEquivalent
@dataclass
class SyncAccess:
"""<syncaccess>"""
flag: Flag # VkAccessFlagBits2
support: SyncSupport
equivalent: SyncEquivalent
@dataclass
class SyncPipelineStage:
"""<syncpipelinestage>"""
order: (str | None)
before: (str | None)
after: (str | None)
value: str
@dataclass
class SyncPipeline:
"""<syncpipeline>"""
name: str
depends: list[str]
stages: list[SyncPipelineStage]
@dataclass
class SpirvEnables:
"""What is needed to enable the SPIR-V element"""
version: (str | None)
extension: (str | None)
struct: (str | None)
feature: (str | None)
requires: (str | None)
property: (str | None)
member: (str | None)
value: (str | None)
@dataclass
class Spirv:
"""<spirvextension> and <spirvcapability>"""
name: str
# Only one will be True, the other is False
extension: bool
capability: bool
enable: list[SpirvEnables]
# This is the global Vulkan Object that holds all the information from parsing the XML
# This class is designed so all generator scripts can use this to obtain data
@dataclass
class VulkanObject():
headerVersion: int = 0 # value of VK_HEADER_VERSION
extensions: dict[str, Extension] = field(default_factory=dict, init=False)
versions: dict[str, Version] = field(default_factory=dict, init=False)
handles: dict[str, Handle] = field(default_factory=dict, init=False)
commands: dict[str, Command] = field(default_factory=dict, init=False)
structs: dict[str, Struct] = field(default_factory=dict, init=False)
enums: dict[str, Enum] = field(default_factory=dict, init=False)
bitmasks: dict[str, Bitmask] = field(default_factory=dict, init=False)
formats: dict[str, Format] = field(default_factory=dict, init=False)
syncStage: list[SyncStage] = field(default_factory=list, init=False)
syncAccess: list[SyncAccess] = field(default_factory=list, init=False)
syncPipeline: list[SyncPipeline] = field(default_factory=list, init=False)
spirv: list[Spirv] = field(default_factory=list, init=False)
# ex) [ xlib : VK_USE_PLATFORM_XLIB_KHR ]
platforms: dict[str, str] = field(default_factory=dict, init=False)
# list of all vendor Sufix names (KHR, EXT, etc. )
vendorTags: list[str] = field(default_factory=list, init=False)
# ex) [ Queues.COMPUTE : VK_QUEUE_COMPUTE_BIT ]
queueBits: dict[IntFlag, str] = field(default_factory=dict, init=False)