342 lines
13 KiB
Python
342 lines
13 KiB
Python
# Copyright © 2020 Hoe Hao Cheng
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
# to deal in the Software without restriction, including without limitation
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice (including the next
|
|
# paragraph) shall be included in all copies or substantial portions of the
|
|
# Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
# IN THE SOFTWARE.
|
|
#
|
|
|
|
import re
|
|
from xml.etree import ElementTree
|
|
from typing import List,Tuple
|
|
|
|
class Version:
|
|
device_version = (1,0,0)
|
|
struct_version = (1,0)
|
|
|
|
def __init__(self, version, struct=()):
|
|
self.device_version = version
|
|
|
|
if not struct:
|
|
self.struct_version = (version[0], version[1])
|
|
else:
|
|
self.struct_version = struct
|
|
|
|
# e.g. "VK_MAKE_VERSION(1,2,0)"
|
|
def version(self):
|
|
return ("VK_MAKE_VERSION("
|
|
+ str(self.device_version[0])
|
|
+ ","
|
|
+ str(self.device_version[1])
|
|
+ ","
|
|
+ str(self.device_version[2])
|
|
+ ")")
|
|
|
|
# e.g. "10"
|
|
def struct(self):
|
|
return (str(self.struct_version[0])+str(self.struct_version[1]))
|
|
|
|
# the sType of the extension's struct
|
|
# e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT
|
|
# for VK_EXT_transform_feedback and struct="FEATURES"
|
|
def stype(self, struct: str):
|
|
return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_"
|
|
+ str(self.struct_version[0]) + "_" + str(self.struct_version[1])
|
|
+ '_' + struct)
|
|
|
|
class Extension:
|
|
name = None
|
|
alias = None
|
|
is_required = False
|
|
is_nonstandard = False
|
|
enable_conds = None
|
|
core_since = None
|
|
|
|
# these are specific to zink_device_info.py:
|
|
has_properties = False
|
|
has_features = False
|
|
guard = False
|
|
features_promoted = False
|
|
properties_promoted = False
|
|
|
|
|
|
# these are specific to zink_instance.py:
|
|
platform_guard = None
|
|
|
|
def __init__(self, name, alias="", required=False, nonstandard=False,
|
|
properties=False, features=False, conditions=None, guard=False):
|
|
self.name = name
|
|
self.alias = alias
|
|
self.is_required = required
|
|
self.is_nonstandard = nonstandard
|
|
self.has_properties = properties
|
|
self.has_features = features
|
|
self.enable_conds = conditions
|
|
self.guard = guard
|
|
|
|
if alias == "" and (properties == True or features == True):
|
|
raise RuntimeError("alias must be available when properties and/or features are used")
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "robustness2"
|
|
def pure_name(self):
|
|
return '_'.join(self.name.split('_')[2:])
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "EXT_robustness2"
|
|
def name_with_vendor(self):
|
|
return self.name[3:]
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "Robustness2"
|
|
def name_in_camel_case(self):
|
|
return "".join([x.title() for x in self.name.split('_')[2:]])
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS_2"
|
|
def name_in_snake_uppercase(self):
|
|
def replace(original):
|
|
# we do not split the types into two, e.g. INT_32
|
|
match_types = re.match(".*(int|float)(8|16|32|64)$", original)
|
|
|
|
# do not match win32
|
|
match_os = re.match(".*win32$", original)
|
|
|
|
# try to match extensions with alphanumeric names, like robustness2
|
|
match_alphanumeric = re.match("([a-z]+)(\d+)", original)
|
|
|
|
if match_types is not None or match_os is not None:
|
|
return original.upper()
|
|
|
|
if match_alphanumeric is not None:
|
|
return (match_alphanumeric[1].upper()
|
|
+ '_'
|
|
+ match_alphanumeric[2])
|
|
|
|
return original.upper()
|
|
|
|
replaced = list(map(replace, self.name.split('_')))
|
|
return '_'.join(replaced)
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "ROBUSTNESS_2"
|
|
def pure_name_in_snake_uppercase(self):
|
|
return '_'.join(self.name_in_snake_uppercase().split('_')[2:])
|
|
|
|
# e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS_2_EXTENSION_NAME"
|
|
def extension_name(self):
|
|
return self.name_in_snake_uppercase() + "_EXTENSION_NAME"
|
|
|
|
# generate a C string literal for the extension
|
|
def extension_name_literal(self):
|
|
return '"' + self.name + '"'
|
|
|
|
# get the field in zink_device_info that refers to the extension's
|
|
# feature/properties struct
|
|
# e.g. rb2_<suffix> for VK_EXT_robustness2
|
|
def field(self, suffix: str):
|
|
return self.alias + '_' + suffix
|
|
|
|
def physical_device_struct(self, struct: str):
|
|
if self.name_in_camel_case().endswith(struct):
|
|
struct = ""
|
|
|
|
return ("VkPhysicalDevice"
|
|
+ self.name_in_camel_case()
|
|
+ struct
|
|
+ self.vendor())
|
|
|
|
# the sType of the extension's struct
|
|
# e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT
|
|
# for VK_EXT_transform_feedback and struct="FEATURES"
|
|
def stype(self, struct: str):
|
|
return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_"
|
|
+ self.pure_name_in_snake_uppercase()
|
|
+ '_' + struct + '_'
|
|
+ self.vendor())
|
|
|
|
# e.g. EXT in VK_EXT_robustness2
|
|
def vendor(self):
|
|
return self.name.split('_')[1]
|
|
|
|
# Type aliases
|
|
Layer = Extension
|
|
|
|
class ExtensionRegistryEntry:
|
|
# type of extension - right now it's either "instance" or "device"
|
|
ext_type = ""
|
|
# the version in which the extension is promoted to core VK
|
|
promoted_in = None
|
|
# functions added by the extension are referred to as "commands" in the registry
|
|
device_commands = None
|
|
pdevice_commands = None
|
|
instance_commands = None
|
|
constants = None
|
|
features_struct = None
|
|
features_fields = None
|
|
features_promoted = False
|
|
properties_struct = None
|
|
properties_fields = None
|
|
properties_promoted = False
|
|
# some instance extensions are locked behind certain platforms
|
|
platform_guard = ""
|
|
|
|
class ExtensionRegistry:
|
|
# key = extension name, value = registry entry
|
|
registry = dict()
|
|
|
|
def __init__(self, vkxml_path: str):
|
|
vkxml = ElementTree.parse(vkxml_path)
|
|
|
|
commands_type = dict()
|
|
command_aliases = dict()
|
|
platform_guards = dict()
|
|
struct_aliases = dict()
|
|
|
|
for cmd in vkxml.findall("commands/command"):
|
|
name = cmd.find("./proto/name")
|
|
|
|
if name is not None and name.text:
|
|
commands_type[name.text] = cmd.find("./param/type").text
|
|
elif cmd.get("name") is not None:
|
|
command_aliases[cmd.get("name")] = cmd.get("alias")
|
|
|
|
for typ in vkxml.findall("types/type"):
|
|
if typ.get("category") != "struct":
|
|
continue
|
|
|
|
name = typ.get("name")
|
|
alias = typ.get("alias")
|
|
|
|
if name and alias:
|
|
struct_aliases[name] = alias
|
|
|
|
for (cmd, alias) in command_aliases.items():
|
|
commands_type[cmd] = commands_type[alias]
|
|
|
|
for platform in vkxml.findall("platforms/platform"):
|
|
name = platform.get("name")
|
|
guard = platform.get("protect")
|
|
platform_guards[name] = guard
|
|
|
|
for ext in vkxml.findall("extensions/extension"):
|
|
# Reserved extensions are marked with `supported="disabled"`
|
|
if ext.get("supported") == "disabled":
|
|
continue
|
|
|
|
name = ext.attrib["name"]
|
|
|
|
entry = ExtensionRegistryEntry()
|
|
entry.ext_type = ext.attrib["type"]
|
|
entry.promoted_in = self.parse_promotedto(ext.get("promotedto"))
|
|
|
|
entry.device_commands = []
|
|
entry.pdevice_commands = []
|
|
entry.instance_commands = []
|
|
entry.features_fields = []
|
|
entry.properties_fields = []
|
|
|
|
for cmd in ext.findall("require/command"):
|
|
cmd_name = cmd.get("name")
|
|
if cmd_name:
|
|
if commands_type[cmd_name] in ("VkDevice", "VkCommandBuffer", "VkQueue"):
|
|
entry.device_commands.append(cmd_name)
|
|
elif commands_type[cmd_name] in ("VkPhysicalDevice"):
|
|
entry.pdevice_commands.append(cmd_name)
|
|
else:
|
|
entry.instance_commands.append(cmd_name)
|
|
|
|
entry.constants = []
|
|
for enum in ext.findall("require/enum"):
|
|
enum_name = enum.get("name")
|
|
enum_extends = enum.get("extends")
|
|
# we are only interested in VK_*_EXTENSION_NAME, which does not
|
|
# have an "extends" attribute
|
|
if not enum_extends:
|
|
entry.constants.append(enum_name)
|
|
|
|
for ty in ext.findall("require/type"):
|
|
ty_name = ty.get("name")
|
|
if (self.is_features_struct(ty_name) and
|
|
entry.features_struct is None):
|
|
entry.features_struct = ty_name
|
|
|
|
elif (self.is_properties_struct(ty_name) and
|
|
entry.properties_struct is None):
|
|
entry.properties_struct = ty_name
|
|
|
|
if entry.features_struct:
|
|
struct_name = entry.features_struct
|
|
if entry.features_struct in struct_aliases:
|
|
struct_name = struct_aliases[entry.features_struct]
|
|
entry.features_promoted = True
|
|
|
|
elif entry.promoted_in is not None:
|
|
# if the extension is promoted but a core-Vulkan alias is not
|
|
# available for the features, then consider the features struct
|
|
# non-core-promoted
|
|
entry.features_promoted = False
|
|
|
|
for field in vkxml.findall("./types/type[@name='{}']/member".format(struct_name)):
|
|
field_name = field.find("name").text
|
|
|
|
# we ignore sType and pNext since they are irrelevant
|
|
if field_name not in ["sType", "pNext"]:
|
|
entry.features_fields.append(field_name)
|
|
|
|
if entry.properties_struct:
|
|
struct_name = entry.properties_struct
|
|
if entry.properties_struct in struct_aliases:
|
|
struct_name = struct_aliases[entry.properties_struct]
|
|
entry.properties_promoted = True
|
|
|
|
elif entry.promoted_in is not None:
|
|
# if the extension is promoted but a core-Vulkan alias is not
|
|
# available for the properties, then it is not promoted to core
|
|
entry.properties_promoted = False
|
|
|
|
for field in vkxml.findall("./types/type[@name='{}']/member".format(struct_name)):
|
|
field_name = field.find("name").text
|
|
|
|
# we ignore sType and pNext since they are irrelevant
|
|
if field_name not in ["sType", "pNext"]:
|
|
entry.properties_fields.append(field_name)
|
|
|
|
if ext.get("platform") is not None:
|
|
entry.platform_guard = platform_guards[ext.get("platform")]
|
|
|
|
self.registry[name] = entry
|
|
|
|
def in_registry(self, ext_name: str):
|
|
return ext_name in self.registry
|
|
|
|
def get_registry_entry(self, ext_name: str):
|
|
if self.in_registry(ext_name):
|
|
return self.registry[ext_name]
|
|
|
|
# Parses e.g. "VK_VERSION_x_y" to integer tuple (x, y)
|
|
# For any erroneous inputs, None is returned
|
|
def parse_promotedto(self, promotedto: str):
|
|
result = None
|
|
|
|
if promotedto and promotedto.startswith("VK_VERSION_"):
|
|
(major, minor) = promotedto.split('_')[-2:]
|
|
result = (int(major), int(minor))
|
|
|
|
return result
|
|
|
|
def is_features_struct(self, struct: str):
|
|
return re.match(r"VkPhysicalDevice.*Features.*", struct) is not None
|
|
|
|
def is_properties_struct(self, struct: str):
|
|
return re.match(r"VkPhysicalDevice.*Properties.*", struct) is not None
|