Files
raven/scripts/generate-vulkan-chains.py
2026-10-25 13:54:45 +00:00

99 lines
5.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Generate the table of Vulkan feature, property and format query structures that can travel in a pNext chain.
Every structure named VkPhysicalDevice...Features or ...Properties, or Vk...Format...Properties (any vendor suffix), whose members hold no pointers except
pNext is a plain block of bytes: it can be copied between guest and host without translating handles. The table pairs each
structure type value with the size of the host structure.
The structure type value of each structure is read from the registry (the values attribute of its sType member) when its path is given
as a third argument, and guessed from the structure name otherwise; the guess fails for names with digit suffixes such as 3D.
usage: generate-vulkan-chains.py /usr/include/vulkan/vulkan_core.h build/native/vulkan-generated/vulkan_chain_table.inc [/usr/share/vulkan/registry/vk.xml]
"""
import os
import re
import subprocess
import sys
import tempfile
import xml.etree.ElementTree as ElementTree
def snake(name):
text = re.sub(r"(?<=[a-z0-9])(?=[A-Z])", "_", name)
text = re.sub(r"(?<=[A-Z])(?=[A-Z][a-z])", "_", text)
return text.upper()
def registry_types(path):
"""Structure name to the structure type enumerator named by its sType member."""
found = {}
for element in ElementTree.parse(path).getroot().iter("type"):
if element.get("category") != "struct":
continue
for member in element.findall("member"):
name = member.find("name")
if name is not None and name.text == "sType" and member.get("values"):
found[element.get("name")] = member.get("values").split(",")[0]
return found
def main():
header = open(sys.argv[1]).read()
registry = registry_types(sys.argv[3]) if len(sys.argv) > 3 else {}
enumerators = set(re.findall(r"\b(VK_STRUCTURE_TYPE_[A-Z0-9_]+)\b", header))
entries = []
for match in re.finditer(r"typedef struct (Vk\w+) \{(.*?)\} \1;", header, re.S):
name, body = match.groups()
device_structure = re.match(r"VkPhysicalDevice\w*(Features|Properties)\w*$", name)
# Format and image format query results (VkFormatProperties3, external and Ycbcr image format answers) also travel in chains.
format_answer = re.match(r"Vk(?!PhysicalDevice|Video)\w*Format\w*Properties\w*$", name) and name not in ("VkFormatProperties2", "VkImageFormatProperties2")
if not (device_structure or format_answer) or "sType" not in body:
continue
members = [line.strip() for line in body.split(";") if line.strip()]
if any("*" in member and "pNext" not in member for member in members):
continue
suffix = re.search(r"(KHR|EXT|NV|NVX|AMD|INTEL|QCOM|ARM|HUAWEI|VALVE|MESA|IMG|ANDROID|FUCHSIA|GOOGLE|SEC|QNX|MSFT|EXT_PRIVATE)$", name)
stem = name[:-len(suffix.group(1))] if suffix else name
base = snake(stem[2:])
tail = ("_" + suffix.group(1)) if suffix else ""
# The enumerator spelling of digits varies: Vulkan11 is VULKAN_1_1, Robustness2 is ROBUSTNESS_2, Int64 stays INT64.
candidates = [
base,
re.sub(r"(?<=[A-Z])(\d)", r"_\1", base),
re.sub(r"VULKAN(\d)(\d)", r"VULKAN_\1_\2", base),
re.sub(r"VULKAN(\d)(\d)", r"VULKAN_\1_\2", re.sub(r"(?<=[A-Z])(\d)", r"_\1", base)),
]
candidate = registry.get(name) or next((("VK_STRUCTURE_TYPE_" + item + tail) for item in candidates if ("VK_STRUCTURE_TYPE_" + item + tail) in enumerators), None)
if not candidate:
continue
entries.append((candidate, name))
entries.sort()
# Beta-only structures are declared behind preprocessor switches; keep what the plain headers really provide.
include_root = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[1])))
for _ in range(8):
with tempfile.TemporaryDirectory() as directory:
probe = os.path.join(directory, "probe.c")
with open(probe, "w") as handle:
handle.write("#include <vulkan/vulkan.h>\nstatic const struct { int type; unsigned long size; } probe[] = {\n")
for enumerator, name in entries:
handle.write(f"{{{enumerator}, sizeof({name})}},\n")
handle.write("};\n")
result = subprocess.run(["cc", "-fsyntax-only", "-I" + include_root, probe], capture_output=True, text=True)
missing = set(re.findall(r"[‘'](VK_STRUCTURE_TYPE_\w+)[’']", result.stderr)) | set(re.findall(r"[‘'](Vk\w+)[’']", result.stderr))
if result.returncode == 0:
break
before = len(entries)
entries = [(e, n) for e, n in entries if e not in missing and n not in missing]
if len(entries) == before:
raise SystemExit(result.stderr)
print("// Generated by scripts/generate-vulkan-chains.py from the Vulkan headers; do not edit.", file=open(sys.argv[2], "w"))
with open(sys.argv[2], "a") as output:
for enumerator, name in entries:
print(f"{{{enumerator}, sizeof({name})}},", file=output)
print(len(entries), "structures")
if __name__ == "__main__":
main()