1164 lines
58 KiB
Python
1164 lines
58 KiB
Python
#!/usr/bin/env python3 -i
|
|
#
|
|
# Copyright 2023-2025 The Khronos Group Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import pickle
|
|
import os
|
|
import tempfile
|
|
import copy
|
|
from vulkan_object import (VulkanObject,
|
|
Extension, Version, Deprecate, Handle, Param, CommandScope, Command,
|
|
EnumField, Enum, Flag, Bitmask, ExternSync, Flags, Member, Struct,
|
|
Constant, FormatComponent, FormatPlane, Format, FeatureRequirement,
|
|
SyncSupport, SyncEquivalent, SyncStage, SyncAccess, SyncPipelineStage, SyncPipeline,
|
|
SpirvEnables, Spirv,
|
|
VideoCodec, VideoFormat, VideoProfiles, VideoProfileMember, VideoRequiredCapabilities,
|
|
VideoStd, VideoStdHeader)
|
|
|
|
# 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
|
|
from reg import Registry
|
|
from xml.etree import ElementTree
|
|
|
|
# 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 []
|
|
|
|
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 externSyncGet(elem):
|
|
value = elem.get('externsync')
|
|
if value is None:
|
|
return (ExternSync.NONE, None)
|
|
if value == 'true':
|
|
return (ExternSync.ALWAYS, None)
|
|
if value == 'maybe':
|
|
return (ExternSync.MAYBE, None)
|
|
|
|
# There are no cases where multiple members of the param are marked as
|
|
# externsync. Supporting that with maybe: requires more than
|
|
# ExternSync.SUBTYPE_MAYBE (which is only one bit of information), which is
|
|
# not currently done as there are no users.
|
|
#
|
|
# If this assert is hit, please consider simplifying the design such that
|
|
# externsync can move to the struct itself and so external synchronization
|
|
# requirements do not depend on the context.
|
|
assert ',' not in value
|
|
|
|
if value.startswith('maybe:'):
|
|
return (ExternSync.SUBTYPE_MAYBE, value.removeprefix('maybe:'))
|
|
return (ExternSync.SUBTYPE, value)
|
|
|
|
# Shared object used by Sync elements that do not 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, featureRequirement) -> Version:
|
|
match targetApiName:
|
|
|
|
# Vulkan SC specific API version creation
|
|
case 'vulkansc':
|
|
nameApi = name.replace('VK_', 'VK_API_')
|
|
nameApi = nameApi.replace('VKSC_', 'VKSC_API_')
|
|
nameString = f'"{name}"'
|
|
return Version(name, nameString, nameApi, featureRequirement)
|
|
|
|
# Vulkan specific API version creation
|
|
case 'vulkan':
|
|
nameApi = name.replace('VK_', 'VK_API_')
|
|
nameString = f'"{name}"'
|
|
return Version(name, nameString, nameApi, featureRequirement)
|
|
|
|
# TODO - Currently genType in reg.py does not provide a good way to get this string to apply the C-macro
|
|
# 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
|
|
@staticmethod
|
|
def createHeaderVersion(targetApiName: str, vk: VulkanObject) -> str:
|
|
match targetApiName:
|
|
case 'vulkan':
|
|
major_version = 1
|
|
minor_version = 4
|
|
case 'vulkansc':
|
|
major_version = 1
|
|
minor_version = 0
|
|
return f'{major_version}.{minor_version}.{vk.headerVersion}'
|
|
|
|
|
|
# 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 modify things per-file that need the difference
|
|
class BaseGeneratorOptions(GeneratorOptions):
|
|
def __init__(self,
|
|
customFileName = None,
|
|
customDirectory = None,
|
|
customApiName = None,
|
|
videoXmlPath = 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 is used to provide the video.xml to the private video XML generator
|
|
self.videoXmlPath = videoXmlPath
|
|
|
|
#
|
|
# 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
|
|
|
|
# We need to flag extensions that we ignore because they are disabled or not
|
|
# supported in the target API(s)
|
|
self.unsupportedExtension = False
|
|
|
|
# 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.flagsAliasMap = dict()
|
|
self.structAliasMap = dict()
|
|
self.handleAliasMap = dict()
|
|
|
|
# We track all enum constants and flag bits so that we can apply their aliases in the end
|
|
self.enumFieldMap: dict[str, EnumField] = dict()
|
|
self.flagMap: dict[str, Flag] = dict()
|
|
|
|
# De-aliases a definition name based on the specified alias map.
|
|
# There are aliases of aliases.
|
|
# e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR aliases
|
|
# VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR which itself aliases
|
|
# But it is also common for EXT types promoted to KHR then to core.
|
|
# We should not make assumptions about the nesting level of aliases, instead we resolve any
|
|
# level of alias aliasing.
|
|
def dealias(self, name: str, aliasMap: dict):
|
|
while name in aliasMap:
|
|
name = aliasMap[name]
|
|
return name
|
|
|
|
def write(self, data):
|
|
# Prevents having to check before writing
|
|
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'))
|
|
|
|
# If the video.xml path is provided then we need to load and parse it using
|
|
# the private video std generator
|
|
if genOpts.videoXmlPath is not None:
|
|
videoStdGenerator = _VideoStdGenerator()
|
|
videoRegistry = Registry(videoStdGenerator, genOpts)
|
|
videoRegistry.loadElementTree(ElementTree.parse(genOpts.videoXmlPath))
|
|
videoRegistry.apiGen()
|
|
self.vk.videoStd = videoStdGenerator.vk.videoStd
|
|
|
|
# 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.name] if extension.name 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 does not 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.handles:
|
|
handle = self.vk.handles[group]
|
|
# Make sure list is unique
|
|
handle.extensions.extend([extension.name] if extension.name not in handle.extensions else [])
|
|
extension.handles[group].extend([handle] if handle not in extension.handles[group] else [])
|
|
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.dealias(x, self.enumFieldAliasMap), 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.name] if extension.name not in enum.fieldExtensions else [])
|
|
enumField.extensions.extend([extension.name] if extension.name 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.flagBits:
|
|
extension.flagBits[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.dealias(x, self.flagAliasMap), 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.name] if extension.name not in bitmask.flagExtensions else [])
|
|
flags.extensions.extend([extension.name] if extension.name not in flags.extensions else [])
|
|
extension.flagBits[group].extend([flags] if flags not in extension.flagBits[group] else [])
|
|
if group in self.vk.flags:
|
|
flags = self.vk.flags[group]
|
|
# Make sure list is unique
|
|
flags.extensions.extend([extension.name] if extension.name not in flags.extensions else [])
|
|
extension.flags.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.dealias(enumName, self.enumAliasMap)
|
|
if enumName in self.vk.enums:
|
|
enum = self.vk.enums[enumName]
|
|
enum.extensions.extend([extension.name] if extension.name 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.name] if extension.name 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.name] if extension.name 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 is not repeated in name
|
|
isAlias = bitmaskName in self.bitmaskAliasMap
|
|
bitmaskName = self.dealias(bitmaskName, self.bitmaskAliasMap)
|
|
if bitmaskName in self.vk.bitmasks:
|
|
bitmask = self.vk.bitmasks[bitmaskName]
|
|
bitmask.extensions.extend([extension.name] if extension.name 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.name] if extension.name 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.name] if extension.name not in flag.extensions else [])
|
|
if bitmaskName not in extension.flagBits:
|
|
extension.flagBits[bitmaskName] = [] # Dict needs init
|
|
extension.flagBits[bitmaskName].extend([flag] if flag not in extension.flagBits[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.dealias(structName, self.structAliasMap)
|
|
if structName in self.vk.structs:
|
|
struct = self.vk.structs[structName]
|
|
struct.extensions.extend([extension.name] if extension.name not in struct.extensions else [])
|
|
extension.structs.extend([struct] if struct not in extension.structs 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
|
|
# TODO: It is unclear why only structs need dealiasing here, but not other types, so this probably needs revisiting
|
|
for command in self.vk.commands.values():
|
|
for member in command.params:
|
|
if member.type in self.structAliasMap:
|
|
member.type = self.dealias(member.type, self.structAliasMap)
|
|
# Replace string with Version class now we have all version created
|
|
if command.deprecate and command.deprecate.version:
|
|
if command.deprecate.version not in self.vk.versions:
|
|
# occurs if something like VK_VERSION_1_0, in which case we will always warn for deprecation
|
|
command.deprecate.version = None
|
|
else:
|
|
command.deprecate.version = self.vk.versions[command.deprecate.version]
|
|
|
|
# Could build up a reverse lookup map, but since these are not too large of list, just do here
|
|
# (Need to be done after we have found all the aliases)
|
|
for key, value in self.structAliasMap.items():
|
|
self.vk.structs[self.dealias(value, self.structAliasMap)].aliases.append(key)
|
|
for key, value in self.enumFieldAliasMap.items():
|
|
self.enumFieldMap[self.dealias(value, self.enumFieldAliasMap)].aliases.append(key)
|
|
for key, value in self.enumAliasMap.items():
|
|
self.vk.enums[self.dealias(value, self.enumAliasMap)].aliases.append(key)
|
|
for key, value in self.flagAliasMap.items():
|
|
self.flagMap[self.dealias(value, self.flagAliasMap)].aliases.append(key)
|
|
for key, value in self.bitmaskAliasMap.items():
|
|
self.vk.bitmasks[self.dealias(value, self.bitmaskAliasMap)].aliases.append(key)
|
|
for key, value in self.flagsAliasMap.items():
|
|
self.vk.flags[self.dealias(value, self.flagsAliasMap)].aliases.append(key)
|
|
for key, value in self.handleAliasMap.items():
|
|
self.vk.handles[self.dealias(value, self.handleAliasMap)].aliases.append(key)
|
|
|
|
def addConstants(self, constantNames: list[str]):
|
|
for constantName in constantNames:
|
|
enumInfo = self.registry.enumdict[constantName]
|
|
typeName = enumInfo.type
|
|
valueStr = enumInfo.elem.get('value')
|
|
# These values are represented in c-style
|
|
isHex = valueStr.upper().startswith('0X')
|
|
intBase = 16 if isHex else 10
|
|
if valueStr.upper().endswith('F') and not isHex:
|
|
value = float(valueStr[:-1])
|
|
elif valueStr.upper().endswith('U)'):
|
|
inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-1], intBase)
|
|
value = (~inner_number) & ((1 << 32) - 1)
|
|
elif valueStr.upper().endswith('ULL)'):
|
|
inner_number = int(valueStr.removeprefix("(~").removesuffix(")")[:-3], intBase)
|
|
value = (~0) & ((1 << 64) - 1)
|
|
else:
|
|
value = int(valueStr, intBase)
|
|
self.vk.constants[constantName] = Constant(constantName, typeName, value, valueStr)
|
|
|
|
def addVideoCodecs(self):
|
|
for xmlVideoCodec in self.registry.tree.findall('videocodecs/videocodec'):
|
|
name = xmlVideoCodec.get('name')
|
|
extend = xmlVideoCodec.get('extend')
|
|
value = xmlVideoCodec.get('value')
|
|
|
|
profiles: dict[str, VideoProfiles] = {}
|
|
capabilities: dict[str, str] = {}
|
|
formats: dict[str, VideoFormat] = {}
|
|
|
|
if extend is not None:
|
|
# Inherit base profiles, capabilities, and formats
|
|
profiles = copy.deepcopy(self.vk.videoCodecs[extend].profiles)
|
|
capabilities = copy.deepcopy(self.vk.videoCodecs[extend].capabilities)
|
|
formats = copy.deepcopy(self.vk.videoCodecs[extend].formats)
|
|
|
|
for xmlVideoProfiles in xmlVideoCodec.findall('videoprofiles'):
|
|
videoProfileStructName = xmlVideoProfiles.get('struct')
|
|
videoProfileStructMembers : dict[str, VideoProfileMember] = {}
|
|
|
|
for xmlVideoProfileMember in xmlVideoProfiles.findall('videoprofilemember'):
|
|
memberName = xmlVideoProfileMember.get('name')
|
|
memberValues: dict[str, str] = {}
|
|
|
|
for xmlVideoProfile in xmlVideoProfileMember.findall('videoprofile'):
|
|
memberValues[xmlVideoProfile.get('value')] = xmlVideoProfile.get('name')
|
|
|
|
videoProfileStructMembers[memberName] = VideoProfileMember(memberName, memberValues)
|
|
|
|
profiles[videoProfileStructName] = VideoProfiles(videoProfileStructName, videoProfileStructMembers)
|
|
|
|
for xmlVideoCapabilities in xmlVideoCodec.findall('videocapabilities'):
|
|
capabilities[xmlVideoCapabilities.get('struct')] = xmlVideoCapabilities.get('struct')
|
|
|
|
for xmlVideoFormat in xmlVideoCodec.findall('videoformat'):
|
|
videoFormatName = xmlVideoFormat.get('name')
|
|
videoFormatExtend = xmlVideoFormat.get('extend')
|
|
|
|
videoFormatRequiredCaps: list[VideoRequiredCapabilities] = []
|
|
videoFormatProps: dict[str, str] = {}
|
|
|
|
if videoFormatName is not None:
|
|
# This is a new video format category
|
|
videoFormatUsage = xmlVideoFormat.get('usage')
|
|
videoFormat = VideoFormat(videoFormatName, videoFormatUsage, videoFormatRequiredCaps, videoFormatProps)
|
|
formats[videoFormatName] = videoFormat
|
|
else:
|
|
# This is an extension to an already defined video format category
|
|
videoFormat = formats[videoFormatExtend]
|
|
videoFormatRequiredCaps = videoFormat.requiredCaps
|
|
videoFormatProps = videoFormat.properties
|
|
|
|
for xmlVideoFormatRequiredCap in xmlVideoFormat.findall('videorequirecapabilities'):
|
|
requiredCap = VideoRequiredCapabilities(xmlVideoFormatRequiredCap.get('struct'),
|
|
xmlVideoFormatRequiredCap.get('member'),
|
|
xmlVideoFormatRequiredCap.get('value'))
|
|
videoFormatRequiredCaps.append(requiredCap)
|
|
|
|
for xmlVideoFormatProperties in xmlVideoFormat.findall('videoformatproperties'):
|
|
videoFormatProps[xmlVideoFormatProperties.get('struct')] = xmlVideoFormatProperties.get('struct')
|
|
|
|
self.vk.videoCodecs[name] = VideoCodec(name, value, profiles, capabilities, formats)
|
|
|
|
def endFile(self):
|
|
# This is the point were reg.py has ran, everything is collected
|
|
# We do some post processing now
|
|
self.applyExtensionDependency()
|
|
|
|
self.addConstants([k for k,v in self.registry.enumvaluedict.items() if v == 'API Constants'])
|
|
self.addVideoCodecs()
|
|
|
|
self.vk.headerVersionComplete = APISpecific.createHeaderVersion(self.targetApiName, self.vk)
|
|
|
|
# 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 flags in [self.vk.flags[x.type] for x in struct.members if x.type in self.vk.flags]:
|
|
flags.returnedOnly = False
|
|
if flags.bitmaskName is not None:
|
|
self.vk.bitmasks[flags.bitmaskName].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 flags in [self.vk.flags[x.type] for x in command.params if x.type in self.vk.flags]:
|
|
flags.returnedOnly = False
|
|
if flags.bitmaskName is not None:
|
|
self.vk.bitmasks[flags.bitmaskName].returnedOnly = False
|
|
|
|
# Turn handle parents into pointers to classes
|
|
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.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')
|
|
|
|
# TODO - This is just mimicking featurerequirementsgenerator.py and works because the logic is simple enough (for now)
|
|
featureRequirement = []
|
|
requires = interface.findall('./require')
|
|
for require in requires:
|
|
requireDepends = require.get('depends')
|
|
for feature in require.findall('./feature'):
|
|
featureStruct = feature.get('struct')
|
|
featureName = feature.get('name')
|
|
featureRequirement.append(FeatureRequirement(featureStruct, featureName, requireDepends))
|
|
|
|
if interface.tag == 'extension':
|
|
# Generator scripts built on BaseGenerator do not handle the `supported` attribute of extensions
|
|
# therefore historically the `generate_source.py` in individual ecosystem components hacked the
|
|
# registry by removing non-applicable or disabled extensions from the loaded XML already before
|
|
# reg.py parsed it. That broke the general behavior of reg.py for certain use cases so we now
|
|
# filter extensions here instead (after parsing) in order to no longer need the filtering hack
|
|
# in downstream `generate_source.py` scripts.
|
|
enabledApiList = [ globalApiName ] + ([] if mergedApiNames is None else mergedApiNames.split(','))
|
|
if (sup := interface.get('supported')) is not None and all(api not in sup.split(',') for api in enabledApiList):
|
|
self.unsupportedExtension = True
|
|
return
|
|
|
|
instance = interface.get('type') == 'instance'
|
|
device = not instance
|
|
depends = interface.get('depends')
|
|
vendorTag = name.split('_')[1]
|
|
platform = interface.get('platform')
|
|
provisional = boolGet(interface, 'provisional')
|
|
promotedto = interface.get('promotedto')
|
|
deprecatedby = interface.get('deprecatedby')
|
|
obsoletedby = interface.get('obsoletedby')
|
|
specialuse = splitIfGet(interface, 'specialuse')
|
|
ratifiedApis = splitIfGet(interface, 'ratified')
|
|
ratified = True if len(ratifiedApis) > 0 and self.genOpts.apiname in ratifiedApis else False
|
|
|
|
# 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, featureRequirement, ratified)
|
|
self.vk.extensions[name] = self.currentExtension
|
|
else: # version
|
|
number = interface.get('number')
|
|
if number != '1.0':
|
|
self.currentVersion = APISpecific.createApiVersion(self.targetApiName, name, featureRequirement)
|
|
self.vk.versions[name] = self.currentVersion
|
|
|
|
def endFeature(self):
|
|
OutputGenerator.endFeature(self)
|
|
self.currentExtension = None
|
|
self.currentVersion = None
|
|
self.unsupportedExtension = False
|
|
|
|
#
|
|
# All <command> from XML
|
|
def genCmd(self, cmdinfo, name, alias):
|
|
OutputGenerator.genCmd(self, cmdinfo, name, alias)
|
|
|
|
# Do not include APIs from unsupported extensions
|
|
if self.unsupportedExtension:
|
|
return
|
|
|
|
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)
|
|
paramFullType = ' '.join(cdecl.split()[:-1])
|
|
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 = len(optionalValues) > 0 and optionalValues[0].lower() == "true"
|
|
optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true"
|
|
|
|
# externsync will be 'true', 'maybe', '<expression>' or 'maybe:<expression>'
|
|
(externSync, externSyncPointer) = externSyncGet(param)
|
|
|
|
params.append(Param(paramName, paramAlias, paramType, paramFullType, paramNoautovalidity,
|
|
paramConst, length, nullTerminated, pointer, fixedSizeArray,
|
|
optional, optionalPointer,
|
|
externSync, externSyncPointer, cdecl))
|
|
|
|
attrib = cmdinfo.elem.attrib
|
|
alias = attrib.get('alias')
|
|
tasks = splitIfGet(attrib, 'tasks')
|
|
|
|
queues = splitIfGet(attrib, 'queues')
|
|
allowNoQueues = boolGet(attrib, 'allownoqueues')
|
|
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]
|
|
|
|
deprecate = None
|
|
if cmdinfo.deprecatedlink:
|
|
deprecate = Deprecate(cmdinfo.deprecatedlink,
|
|
cmdinfo.deprecatedbyversion, # is just the string, will update to class later
|
|
cmdinfo.deprecatedbyextensions)
|
|
|
|
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 is not None else []
|
|
|
|
self.vk.commands[name] = Command(name, alias, protect, [], self.currentVersion,
|
|
returnType, params, instance, device,
|
|
tasks, queues, allowNoQueues, successcodes, errorcodes,
|
|
primary, secondary, renderpass, videocoding,
|
|
implicitExternSyncParams, deprecate, cPrototype, cFunctionPointer)
|
|
|
|
#
|
|
# List the enum for the commands
|
|
# TODO - Seems empty groups like `VkDeviceDeviceMemoryReportCreateInfoEXT` do not show up in here
|
|
def genGroup(self, groupinfo, groupName, alias):
|
|
# Do not include APIs from unsupported extensions
|
|
if self.unsupportedExtension:
|
|
return
|
|
|
|
# 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')
|
|
|
|
# Do not include non-required enum constants
|
|
# reg.py emits the enum constants of the entire type, even constants that are part of unsupported
|
|
# extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks
|
|
# down these and also alias dependencies and marks the enum constants that are actually required
|
|
# with the 'required' attribute. Therefore we also have to verify that here to make sure we only
|
|
# include enum constants that are actually required in the target API(s).
|
|
if elem.get('required') is None:
|
|
continue
|
|
|
|
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')
|
|
(valueInt, valueStr) = self.enumToValue(elem, True, bitwidth)
|
|
|
|
# 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:
|
|
self.enumFieldMap[fieldName] = EnumField(fieldName, [], protect, negative, valueInt, valueStr, [])
|
|
fields.append(self.enumFieldMap[fieldName])
|
|
|
|
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')
|
|
|
|
# Do not include non-required enum constants
|
|
# reg.py emits the enum constants of the entire type, even constants that are part of unsupported
|
|
# extensions or those that are removed by <remove> elements in a given API. reg.py correctly tracks
|
|
# down these and also alias dependencies and marks the enum constants that are actually required
|
|
# with the 'required' attribute. Therefore we also have to verify that here to make sure we only
|
|
# include enum constants that are actually required in the target API(s).
|
|
if elem.get('required') is None:
|
|
continue
|
|
|
|
if elem.get('alias') is not None:
|
|
self.flagAliasMap[flagName] = elem.get('alias')
|
|
continue
|
|
|
|
protect = elem.get('protect')
|
|
|
|
(valueInt, valueStr) = self.enumToValue(elem, True, bitwidth)
|
|
flagZero = valueInt == 0
|
|
flagMultiBit = False
|
|
# if flag uses 'value' instead of 'bitpos', will be zero or a mask
|
|
if elem.get('bitpos') is None and elem.get('value'):
|
|
flagMultiBit = valueInt != 0
|
|
|
|
# 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:
|
|
self.flagMap[flagName] = Flag(flagName, [], protect, valueInt, valueStr, flagMultiBit, flagZero, [])
|
|
fields.append(self.flagMap[flagName])
|
|
|
|
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)
|
|
|
|
# Do not include APIs from unsupported extensions
|
|
if self.unsupportedExtension:
|
|
return
|
|
|
|
typeElem = typeInfo.elem
|
|
protect = self.currentExtension.protect if hasattr(self.currentExtension, 'protect') and self.currentExtension.protect is not None else None
|
|
extension = [self.currentExtension.name] if self.currentExtension is not None else []
|
|
category = typeElem.get('category')
|
|
if (category == 'struct' or category == 'union'):
|
|
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 []
|
|
|
|
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
|
|
noautovalidity = boolGet(member, 'noautovalidity')
|
|
limittype = member.get('limittype')
|
|
|
|
(externSync, externSyncPointer) = externSyncGet(member)
|
|
# No cases currently where a subtype of a struct is marked as externally synchronized.
|
|
assert externSyncPointer is None
|
|
|
|
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)
|
|
fullType = ' '.join(cdecl[:cdecl.rfind(name)].split())
|
|
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)
|
|
|
|
# Handle C bit field members
|
|
bitFieldWidth = int(cdecl.split(':')[1]) if ':' in cdecl else None
|
|
|
|
# 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 optional;
|
|
optionalValues = splitIfGet(member, 'optional')
|
|
optional = len(optionalValues) > 0 and optionalValues[0].lower() == "true"
|
|
optionalPointer = len(optionalValues) > 1 and optionalValues[1].lower() == "true"
|
|
|
|
members.append(Member(name, type, fullType, noautovalidity, limittype,
|
|
const, length, nullTerminated, pointer, fixedSizeArray,
|
|
optional, optionalPointer,
|
|
externSync, cdecl, bitFieldWidth))
|
|
|
|
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:
|
|
self.handleAliasMap[typeName] = alias
|
|
return
|
|
type = typeElem.get('objtypeenum')
|
|
|
|
# will resolve these later, the VulkanObjectType does not 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, extension)
|
|
|
|
elif category == 'define':
|
|
if typeName == 'VK_HEADER_VERSION':
|
|
self.vk.headerVersion = typeElem.find('name').tail.strip()
|
|
|
|
elif category == 'bitmask':
|
|
if alias is not None:
|
|
self.flagsAliasMap[typeName] = alias
|
|
return
|
|
|
|
# Bitmask types, i.e. flags
|
|
baseFlagsType = typeElem.find('type').text
|
|
bitWidth = 64 if baseFlagsType == 'VkFlags64' else 32
|
|
|
|
# Bitmask enum type is either in the 'requires' or 'bitvalues' attribute
|
|
# (for some reason there are two conventions)
|
|
bitmaskName = typeElem.get('bitvalues')
|
|
if bitmaskName is None:
|
|
bitmaskName = typeElem.get('requires')
|
|
|
|
self.vk.flags[typeName] = Flags(typeName, [], bitmaskName, protect, baseFlagsType, bitWidth, True, extension)
|
|
|
|
else:
|
|
# not all categories are used
|
|
# 'group'/'enum' are routed to genGroup instead
|
|
# 'basetype'/'include' are only for headers
|
|
# 'funcpointer` ignore 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 = splitIfGet(supportElem, 'queues')
|
|
stageNames = splitIfGet(supportElem, 'stage')
|
|
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 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 len(stageNames) > 0 else None
|
|
accessNames = splitIfGet(equivalentElem, 'access')
|
|
accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 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 = splitIfGet(supportElem, 'queues')
|
|
stageNames = splitIfGet(supportElem, 'stage')
|
|
stages = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name in stageNames] if len(stageNames) > 0 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 len(stageNames) > 0 else None
|
|
accessNames = splitIfGet(equivalentElem, 'access')
|
|
accesses = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name in accessNames] if len(accessNames) > 0 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))
|
|
|
|
#
|
|
# This object handles all the parsing from the video.xml (i.e. Video Std header definitions)
|
|
# It will fill in video standard definitions into the VulkanObject
|
|
class _VideoStdGenerator(BaseGenerator):
|
|
def __init__(self):
|
|
BaseGenerator.__init__(self)
|
|
self.vk.videoStd = VideoStd()
|
|
|
|
# Track the current Video Std header we are processing
|
|
self.currentVideoStdHeader = None
|
|
|
|
def write(self, data):
|
|
# We do not write anything here
|
|
return
|
|
|
|
def beginFile(self, genOpts):
|
|
# We intentionally skip default BaseGenerator behavior
|
|
OutputGenerator.beginFile(self, genOpts)
|
|
|
|
def endFile(self):
|
|
# Move parsed definitions to the Video Std definitions
|
|
self.vk.videoStd.enums = self.vk.enums
|
|
self.vk.videoStd.structs = self.vk.structs
|
|
self.vk.videoStd.constants = self.vk.constants
|
|
|
|
# We intentionally skip default BaseGenerator behavior
|
|
OutputGenerator.endFile(self)
|
|
|
|
def beginFeature(self, interface, emit):
|
|
# We intentionally skip default BaseGenerator behavior
|
|
OutputGenerator.beginFeature(self, interface, emit)
|
|
|
|
# Only "extension" is possible in the video.xml, identifying the Video Std header
|
|
assert interface.tag == 'extension'
|
|
name = interface.get('name')
|
|
version: (str | None) = None
|
|
depends: list[str] = []
|
|
|
|
# Handle Video Std header version constant
|
|
for enum in interface.findall('require/enum[@value]'):
|
|
enumName = enum.get('name')
|
|
if enumName.endswith('_SPEC_VERSION'):
|
|
version = enum.get('value')
|
|
|
|
# Handle dependencies on other Video Std headers
|
|
for type in interface.findall('require/type[@name]'):
|
|
typeName = type.get('name')
|
|
if typeName.startswith('vk_video/'):
|
|
depends.append(typeName[len('vk_video/'):-len('.h')])
|
|
|
|
headerFile = f'vk_video/{name}.h'
|
|
|
|
self.vk.videoStd.headers[name] = VideoStdHeader(name, version, headerFile, depends)
|
|
|
|
self.currentVideoStdHeader = self.vk.videoStd.headers[name]
|
|
|
|
# Handle constants here as that seems the most straightforward
|
|
constantNames = []
|
|
for enum in interface.findall('require/enum[@type]'):
|
|
constantNames.append(enum.get('name'))
|
|
self.addConstants(constantNames)
|
|
for constantName in constantNames:
|
|
self.vk.constants[constantName].videoStdHeader = self.currentVideoStdHeader.name
|
|
|
|
def endFeature(self):
|
|
self.currentVideoStdHeader = None
|
|
|
|
# We intentionally skip default BaseGenerator behavior
|
|
OutputGenerator.endFeature(self)
|
|
|
|
def genCmd(self, cmdinfo, name, alias):
|
|
# video.xml should not contain any commands
|
|
assert False
|
|
|
|
def genGroup(self, groupinfo, groupName, alias):
|
|
BaseGenerator.genGroup(self, groupinfo, groupName, alias)
|
|
|
|
# We are supposed to be inside a video std header
|
|
assert self.currentVideoStdHeader is not None
|
|
|
|
# Mark the enum with the Video Std header it comes from
|
|
if groupinfo.elem.get('type') == 'enum':
|
|
assert alias is None
|
|
self.vk.enums[groupName].videoStdHeader = self.currentVideoStdHeader.name
|
|
|
|
def genType(self, typeInfo, typeName, alias):
|
|
BaseGenerator.genType(self, typeInfo, typeName, alias)
|
|
|
|
# We are supposed to be inside a video std header
|
|
assert self.currentVideoStdHeader is not None
|
|
|
|
# Mark the struct with the Video Std header it comes from
|
|
if typeInfo.elem.get('category') == 'struct':
|
|
assert alias is None
|
|
self.vk.structs[typeName].videoStdHeader = self.currentVideoStdHeader.name
|
|
|
|
def genSpirv(self, spirvinfo, spirvName, alias):
|
|
# video.xml should not contain any SPIR-V info
|
|
assert False
|
|
|
|
def genFormat(self, format, formatinfo, alias):
|
|
# video.xml should not contain any format info
|
|
assert False
|
|
|
|
def genSyncStage(self, sync):
|
|
# video.xml should not contain any sync stage info
|
|
assert False
|
|
|
|
def genSyncAccess(self, sync):
|
|
# video.xml should not contain any sync access info
|
|
assert False
|
|
|
|
def genSyncPipeline(self, sync):
|
|
# video.xml should not contain any sync pipeline info
|
|
assert False
|