From eaee792ea5f9e4e6e77f1399ed2a677ed5b0912c Mon Sep 17 00:00:00 2001 From: Konstantin Seurer Date: Mon, 7 Aug 2023 19:04:15 +0200 Subject: [PATCH] vulkan: Add a generated vk_properties struct Generates a physical device properties table to avoid dealing with pNext chains in the driver. Based on vk_features. Reviewed-by: Faith Ekstrand Part-of: --- src/amd/vulkan/radv_physical_device.c | 2 +- src/broadcom/vulkan/v3dv_device.c | 2 +- src/freedreno/vulkan/tu_device.cc | 1 + src/gallium/frontends/lavapipe/lvp_device.c | 2 +- src/imagination/vulkan/pvr_device.c | 1 + src/intel/vulkan/anv_device.c | 2 +- src/intel/vulkan_hasvk/anv_device.c | 2 +- src/microsoft/vulkan/dzn_device.c | 2 +- src/nouveau/vulkan/nvk_physical_device.c | 1 + src/panfrost/vulkan/panvk_device.c | 2 +- src/virtio/vulkan/vn_common.h | 2 +- src/vulkan/runtime/meson.build | 16 +- src/vulkan/runtime/vk_physical_device.c | 4 + src/vulkan/runtime/vk_physical_device.h | 7 + src/vulkan/util/meson.build | 4 + .../util/vk_physical_device_properties_gen.py | 293 ++++++++++++++++++ 16 files changed, 333 insertions(+), 10 deletions(-) create mode 100644 src/vulkan/util/vk_physical_device_properties_gen.py diff --git a/src/amd/vulkan/radv_physical_device.c b/src/amd/vulkan/radv_physical_device.c index e538da54188..387286520f6 100644 --- a/src/amd/vulkan/radv_physical_device.c +++ b/src/amd/vulkan/radv_physical_device.c @@ -1910,7 +1910,7 @@ radv_physical_device_try_create(struct radv_instance *instance, drmDevicePtr drm vk_physical_device_dispatch_table_from_entrypoints(&dispatch_table, &radv_physical_device_entrypoints, true); vk_physical_device_dispatch_table_from_entrypoints(&dispatch_table, &wsi_physical_device_entrypoints, false); - result = vk_physical_device_init(&device->vk, &instance->vk, NULL, NULL, &dispatch_table); + result = vk_physical_device_init(&device->vk, &instance->vk, NULL, NULL, NULL, &dispatch_table); if (result != VK_SUCCESS) { goto fail_alloc; } diff --git a/src/broadcom/vulkan/v3dv_device.c b/src/broadcom/vulkan/v3dv_device.c index abe5de4b4f2..6bf9dcd1aaf 100644 --- a/src/broadcom/vulkan/v3dv_device.c +++ b/src/broadcom/vulkan/v3dv_device.c @@ -1012,7 +1012,7 @@ create_physical_device(struct v3dv_instance *instance, &dispatch_table, &wsi_physical_device_entrypoints, false); result = vk_physical_device_init(&device->vk, &instance->vk, NULL, NULL, - &dispatch_table); + NULL, &dispatch_table); if (result != VK_SUCCESS) goto fail; diff --git a/src/freedreno/vulkan/tu_device.cc b/src/freedreno/vulkan/tu_device.cc index 3f3f88d378a..16044021f5c 100644 --- a/src/freedreno/vulkan/tu_device.cc +++ b/src/freedreno/vulkan/tu_device.cc @@ -677,6 +677,7 @@ tu_physical_device_init(struct tu_physical_device *device, result = vk_physical_device_init(&device->vk, &instance->vk, &supported_extensions, &supported_features, + NULL, &dispatch_table); if (result != VK_SUCCESS) goto fail_free_name; diff --git a/src/gallium/frontends/lavapipe/lvp_device.c b/src/gallium/frontends/lavapipe/lvp_device.c index be62172072a..43a9877f7e2 100644 --- a/src/gallium/frontends/lavapipe/lvp_device.c +++ b/src/gallium/frontends/lavapipe/lvp_device.c @@ -600,7 +600,7 @@ lvp_physical_device_init(struct lvp_physical_device *device, vk_physical_device_dispatch_table_from_entrypoints( &dispatch_table, &wsi_physical_device_entrypoints, false); result = vk_physical_device_init(&device->vk, &instance->vk, - NULL, NULL, &dispatch_table); + NULL, NULL, NULL, &dispatch_table); if (result != VK_SUCCESS) { vk_error(instance, result); goto fail; diff --git a/src/imagination/vulkan/pvr_device.c b/src/imagination/vulkan/pvr_device.c index 68cf18e57d6..ed42dc221ac 100644 --- a/src/imagination/vulkan/pvr_device.c +++ b/src/imagination/vulkan/pvr_device.c @@ -427,6 +427,7 @@ static VkResult pvr_physical_device_init(struct pvr_physical_device *pdevice, &instance->vk, &supported_extensions, &supported_features, + NULL, &dispatch_table); if (result != VK_SUCCESS) goto err_pvr_winsys_destroy; diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c index 71b12af42cd..99022283d84 100644 --- a/src/intel/vulkan/anv_device.c +++ b/src/intel/vulkan/anv_device.c @@ -1281,7 +1281,7 @@ anv_physical_device_try_create(struct vk_instance *vk_instance, &dispatch_table, &wsi_physical_device_entrypoints, false); result = vk_physical_device_init(&device->vk, &instance->vk, - NULL, NULL, /* We set up extensions later */ + NULL, NULL, NULL, /* We set up extensions later */ &dispatch_table); if (result != VK_SUCCESS) { vk_error(instance, result); diff --git a/src/intel/vulkan_hasvk/anv_device.c b/src/intel/vulkan_hasvk/anv_device.c index 000878e8887..b49de49bac8 100644 --- a/src/intel/vulkan_hasvk/anv_device.c +++ b/src/intel/vulkan_hasvk/anv_device.c @@ -725,7 +725,7 @@ anv_physical_device_try_create(struct vk_instance *vk_instance, &dispatch_table, &wsi_physical_device_entrypoints, false); result = vk_physical_device_init(&device->vk, &instance->vk, - NULL, NULL, /* We set up extensions later */ + NULL, NULL, NULL, /* We set up extensions later */ &dispatch_table); if (result != VK_SUCCESS) { vk_error(instance, result); diff --git a/src/microsoft/vulkan/dzn_device.c b/src/microsoft/vulkan/dzn_device.c index a0ba831743a..f6189b725f7 100644 --- a/src/microsoft/vulkan/dzn_device.c +++ b/src/microsoft/vulkan/dzn_device.c @@ -792,7 +792,7 @@ dzn_physical_device_create(struct vk_instance *instance, VkResult result = vk_physical_device_init(&pdev->vk, instance, - NULL, NULL, /* We set up extensions later */ + NULL, NULL, NULL, /* We set up extensions later */ &dispatch_table); if (result != VK_SUCCESS) { vk_free(&instance->alloc, pdev); diff --git a/src/nouveau/vulkan/nvk_physical_device.c b/src/nouveau/vulkan/nvk_physical_device.c index 72565c6ec1f..6fb5824b515 100644 --- a/src/nouveau/vulkan/nvk_physical_device.c +++ b/src/nouveau/vulkan/nvk_physical_device.c @@ -773,6 +773,7 @@ nvk_create_drm_physical_device(struct vk_instance *_instance, result = vk_physical_device_init(&pdev->vk, &instance->vk, &supported_extensions, &supported_features, + NULL, &dispatch_table); if (result != VK_SUCCESS) goto fail_alloc; diff --git a/src/panfrost/vulkan/panvk_device.c b/src/panfrost/vulkan/panvk_device.c index 9a36ba8eb30..d0555e9c12e 100644 --- a/src/panfrost/vulkan/panvk_device.c +++ b/src/panfrost/vulkan/panvk_device.c @@ -425,7 +425,7 @@ panvk_physical_device_init(struct panvk_physical_device *device, result = vk_physical_device_init(&device->vk, &instance->vk, &supported_extensions, - &supported_features, &dispatch_table); + &supported_features, NULL, &dispatch_table); if (result != VK_SUCCESS) { vk_error(instance, result); diff --git a/src/virtio/vulkan/vn_common.h b/src/virtio/vulkan/vn_common.h index 7b8248016f6..7bf1142da88 100644 --- a/src/virtio/vulkan/vn_common.h +++ b/src/virtio/vulkan/vn_common.h @@ -276,7 +276,7 @@ vn_physical_device_base_init( { VkResult result = vk_physical_device_init(&physical_dev->base, &instance->base, - supported_extensions, NULL, dispatch_table); + supported_extensions, NULL, NULL, dispatch_table); physical_dev->id = (uintptr_t)physical_dev; return result; } diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index 783cf1dae16..16d57908a0e 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -194,6 +194,18 @@ vk_physical_device_features = custom_target( depend_files : vk_physical_device_features_gen_depend_files, ) +vk_physical_device_properties = custom_target( + 'vk_physical_device_properties', + input : [vk_physical_device_properties_gen, vk_api_xml], + output : ['vk_physical_device_properties.c', 'vk_physical_device_properties.h'], + command : [ + prog_python, '@INPUT0@', '--xml', '@INPUT1@', + '--out-c', '@OUTPUT0@', '--out-h', '@OUTPUT1@', + '--beta', with_vulkan_beta.to_string() + ], + depend_files : vk_physical_device_properties_gen_depend_files, +) + vk_format_info = custom_target( 'vk_format_info', input : ['vk_format_info_gen.py', vk_api_xml], @@ -209,7 +221,7 @@ libvulkan_runtime = static_library( [vulkan_runtime_files, vk_common_entrypoints, vk_cmd_queue, vk_cmd_enqueue_entrypoints, vk_dispatch_trampolines, vk_physical_device_features, - vk_format_info], + vk_physical_device_properties, vk_format_info], include_directories : [inc_include, inc_src, inc_gallium], dependencies : vulkan_runtime_deps, # For glsl_type_singleton @@ -220,7 +232,7 @@ libvulkan_runtime = static_library( ) idep_vulkan_runtime_headers = declare_dependency( - sources : [vk_cmd_queue[1], vk_physical_device_features[1]], + sources : [vk_cmd_queue[1], vk_physical_device_features[1], vk_physical_device_properties[1]], include_directories : include_directories('.'), ) diff --git a/src/vulkan/runtime/vk_physical_device.c b/src/vulkan/runtime/vk_physical_device.c index be55a42c97a..ea277516be2 100644 --- a/src/vulkan/runtime/vk_physical_device.c +++ b/src/vulkan/runtime/vk_physical_device.c @@ -31,6 +31,7 @@ vk_physical_device_init(struct vk_physical_device *pdevice, struct vk_instance *instance, const struct vk_device_extension_table *supported_extensions, const struct vk_features *supported_features, + const struct vk_properties *properties, const struct vk_physical_device_dispatch_table *dispatch_table) { memset(pdevice, 0, sizeof(*pdevice)); @@ -43,6 +44,9 @@ vk_physical_device_init(struct vk_physical_device *pdevice, if (supported_features != NULL) pdevice->supported_features = *supported_features; + if (properties != NULL) + pdevice->properties = *properties; + pdevice->dispatch_table = *dispatch_table; /* Add common entrypoints without overwriting driver-provided ones. */ diff --git a/src/vulkan/runtime/vk_physical_device.h b/src/vulkan/runtime/vk_physical_device.h index 7cd4795097f..f204527e378 100644 --- a/src/vulkan/runtime/vk_physical_device.h +++ b/src/vulkan/runtime/vk_physical_device.h @@ -27,6 +27,7 @@ #include "vk_extensions.h" #include "vk_object.h" #include "vk_physical_device_features.h" +#include "vk_physical_device_properties.h" #include "util/list.h" @@ -74,6 +75,11 @@ struct vk_physical_device { */ struct vk_features supported_features; + /** Table of all physical device properties which is initialized similarly + * to supported_features + */ + struct vk_properties properties; + /** Physical-device-level dispatch table */ struct vk_physical_device_dispatch_table dispatch_table; @@ -125,6 +131,7 @@ vk_physical_device_init(struct vk_physical_device *physical_device, struct vk_instance *instance, const struct vk_device_extension_table *supported_extensions, const struct vk_features *supported_features, + const struct vk_properties *properties, const struct vk_physical_device_dispatch_table *dispatch_table); /** Tears down a vk_physical_device diff --git a/src/vulkan/util/meson.build b/src/vulkan/util/meson.build index 72b1dbb0117..dd748eede0a 100644 --- a/src/vulkan/util/meson.build +++ b/src/vulkan/util/meson.build @@ -49,6 +49,9 @@ vk_cmd_queue_gen_depend_files = [ vk_physical_device_features_gen_depend_files = [ files('vk_extensions.py'), ] +vk_physical_device_properties_gen_depend_files = [ + files('vk_extensions.py'), +] vk_entrypoints_gen = files('vk_entrypoints_gen.py') vk_extensions_gen = files('vk_extensions_gen.py') @@ -56,6 +59,7 @@ vk_icd_gen = files('vk_icd_gen.py') vk_cmd_queue_gen = files('vk_cmd_queue_gen.py') vk_dispatch_trampolines_gen = files('vk_dispatch_trampolines_gen.py') vk_physical_device_features_gen = files('vk_physical_device_features_gen.py') +vk_physical_device_properties_gen = files('vk_physical_device_properties_gen.py') files_vulkan_util = files( 'vk_alloc.c', diff --git a/src/vulkan/util/vk_physical_device_properties_gen.py b/src/vulkan/util/vk_physical_device_properties_gen.py new file mode 100644 index 00000000000..7b8e039df48 --- /dev/null +++ b/src/vulkan/util/vk_physical_device_properties_gen.py @@ -0,0 +1,293 @@ +COPYRIGHT=u""" +/* Copyright © 2021 Intel Corporation + * + * 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 argparse +from collections import OrderedDict +from dataclasses import dataclass +import os +import sys +import typing +import xml.etree.ElementTree as et +import re + +import mako +from mako.template import Template + +from vk_extensions import get_all_required, filter_api + +def str_removeprefix(s, prefix): + if s.startswith(prefix): + return s[len(prefix):] + return s + +RENAMED_PROPERTIES = { + ("DrmPropertiesEXT", "hasPrimary"): "drmHasPrimary", + ("DrmPropertiesEXT", "primaryMajor"): "drmPrimaryMajor", + ("DrmPropertiesEXT", "primaryMinor"): "drmPrimaryMinor", + ("DrmPropertiesEXT", "hasRender"): "drmHasRender", + ("DrmPropertiesEXT", "renderMajor"): "drmRenderMajor", + ("DrmPropertiesEXT", "renderMinor"): "drmRenderMinor", + ("SparseProperties", "residencyStandard2DBlockShape"): "sparseResidencyStandard2DBlockShape", + ("SparseProperties", "residencyStandard2DMultisampleBlockShape"): "sparseResidencyStandard2DMultisampleBlockShape", + ("SparseProperties", "residencyStandard3DBlockShape"): "sparseResidencyStandard3DBlockShape", + ("SparseProperties", "residencyAlignedMipSize"): "sparseResidencyAlignedMipSize", + ("SparseProperties", "residencyNonResidentStrict"): "sparseResidencyNonResidentStrict", + ("SubgroupProperties", "supportedStages"): "subgroupSupportedStages", + ("SubgroupProperties", "supportedOperations"): "subgroupSupportedOperations", + ("SubgroupProperties", "quadOperationsInAllStages"): "subgroupQuadOperationsInAllStages", +} + +@dataclass +class Property: + decl: str + name: str + actual_name: str + length: str + + def __init__(self, p, property_struct_name): + self.decl = "" + for element in p: + if element.tag != "comment": + self.decl += "".join(element.itertext()) + if element.tail: + self.decl += re.sub(" +", " ", element.tail) + + self.name = p.find("./name").text + self.actual_name = RENAMED_PROPERTIES.get((property_struct_name, self.name), self.name) + + length = p.attrib.get("len", "1") + self.length = RENAMED_PROPERTIES.get((property_struct_name, length), length) + + self.decl = self.decl.replace(self.name, self.actual_name) + +@dataclass +class PropertyStruct: + c_type: str + s_type: str + name: str + properties: typing.List[Property] + +def copy_property(dst, src, decl, length="1"): + if "[" in decl: + return "memcpy(%s, %s, sizeof(%s));" % (dst, src, dst) + elif "*" in decl: + return "if (%s) memcpy(%s, %s, sizeof(%s[0] * %s));" % (dst, dst, src, dst, length) + else: + return "%s = %s;" % (dst, src) + +TEMPLATE_H = Template(COPYRIGHT + """ +/* This file generated from ${filename}, don"t edit directly. */ +#ifndef VK_PROPERTIES_H +#define VK_PROPERTIES_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct vk_properties { +% for prop in all_properties: + ${prop.decl}; +% endfor +}; + +#ifdef __cplusplus +} +#endif + +#endif +""", output_encoding="utf-8") + +TEMPLATE_C = Template(COPYRIGHT + """ +/* This file generated from ${filename}, don"t edit directly. */ + +#include "vk_common_entrypoints.h" +#include "vk_log.h" +#include "vk_physical_device.h" +#include "vk_physical_device_properties.h" +#include "vk_util.h" + +VKAPI_ATTR void VKAPI_CALL +vk_common_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2 *pProperties) +{ + VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); + +% for prop in pdev_properties: + ${copy_property("pProperties->properties." + prop.name, "pdevice->properties." + prop.actual_name, prop.decl)} +% endfor + + vk_foreach_struct(ext, pProperties) { + switch (ext->sType) { +% for property_struct in property_structs: + case ${property_struct.s_type}: { + ${property_struct.c_type} *properties = (void *)ext; +% for prop in property_struct.properties: + ${copy_property("properties->" + prop.name, "pdevice->properties." + prop.actual_name, prop.decl, "pdevice->properties." + prop.length)} +% endfor + break; + } +% endfor + default: + break; + } + } +} +""", output_encoding="utf-8") + +def get_pdev_properties(doc, struct_name): + _type = doc.find(".types/type[@name=\"VkPhysicalDevice%s\"]" % struct_name) + if _type is not None: + properties = [] + for p in _type.findall("./member"): + properties.append(Property(p, struct_name)) + return properties + return None + +def filter_api(elem, api): + if "api" not in elem.attrib: + return True + + return api in elem.attrib["api"].split(",") + +def get_property_structs(doc, api, beta): + property_structs = OrderedDict() + + required = get_all_required(doc, "type", api, beta) + + # parse all struct types where structextends VkPhysicalDeviceProperties2 + for _type in doc.findall("./types/type[@category=\"struct\"]"): + if _type.attrib.get("structextends") != "VkPhysicalDeviceProperties2": + continue + + full_name = _type.attrib["name"] + if full_name not in required: + continue + + # Skip extensions with a define for now + if required[full_name].guard is not None: + continue + + # find Vulkan structure type + for elem in _type: + if "STRUCTURE_TYPE" in str(elem.attrib): + s_type = elem.attrib.get("values") + + name = str_removeprefix(full_name, "VkPhysicalDevice") + + # collect a list of properties + properties = [] + + for p in _type.findall("./member"): + if not filter_api(p, api): + continue + + m_name = p.find("./name").text + if m_name == "pNext": + pass + elif m_name == "sType": + s_type = p.attrib.get("values") + else: + properties.append(Property(p, name)) + + property_struct = PropertyStruct(c_type=full_name, s_type=s_type, name=name, properties=properties) + property_structs[property_struct.c_type] = property_struct + + return property_structs.values() + +def get_property_structs_from_xml(xml_files, beta, api="vulkan"): + diagnostics = [] + + pdev_properties = None + property_structs = [] + + for filename in xml_files: + doc = et.parse(filename) + property_structs += get_property_structs(doc, api, beta) + if not pdev_properties: + pdev_properties = get_pdev_properties(doc, "Properties") + pdev_properties = [prop for prop in pdev_properties if prop.name != "limits" and prop.name != "sparseProperties"] + + limits = get_pdev_properties(doc, "Limits") + for limit in limits: + limit.name = "limits." + limit.name + pdev_properties += limits + + sparse_properties = get_pdev_properties(doc, "SparseProperties") + for prop in sparse_properties: + prop.name = "sparseProperties." + prop.name + pdev_properties += sparse_properties + + # Gather all properties, make sure that aliased declarations match up. + property_names = OrderedDict() + all_properties = [] + for prop in pdev_properties: + property_names[prop.actual_name] = prop + all_properties.append(prop) + + for property_struct in property_structs: + for prop in property_struct.properties: + if prop.actual_name not in property_names: + property_names[prop.actual_name] = prop + all_properties.append(prop) + elif prop.decl != property_names[prop.actual_name].decl: + diagnostics.append("Declaration mismatch ('%s' vs. '%s')" % (prop.decl, property_names[prop.actual_name].decl)) + + return pdev_properties, property_structs, all_properties + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--out-c", required=True, help="Output C file.") + parser.add_argument("--out-h", required=True, help="Output H file.") + parser.add_argument("--beta", required=True, help="Enable beta extensions.") + parser.add_argument("--xml", + help="Vulkan API XML file.", + required=True, action="append", dest="xml_files") + args = parser.parse_args() + + pdev_properties, property_structs, all_properties = get_property_structs_from_xml(args.xml_files, args.beta) + + environment = { + "filename": os.path.basename(__file__), + "pdev_properties": pdev_properties, + "property_structs": property_structs, + "all_properties": all_properties, + "copy_property": copy_property + } + + try: + with open(args.out_c, "wb") as f: + f.write(TEMPLATE_C.render(**environment)) + with open(args.out_h, "wb") as f: + f.write(TEMPLATE_H.render(**environment)) + except Exception: + # In the event there"s an error, this uses some helpers from mako + # to print a useful stack trace and prints it, then exits with + # status 1, if python is run with debug; otherwise it just raises + # the exception + print(mako.exceptions.text_error_template().render(), file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main()