diff --git a/docs/header-stubs/compiler/spirv/spirv_info.h b/docs/header-stubs/compiler/spirv/spirv_info.h new file mode 100644 index 00000000000..d8db07f5f1b --- /dev/null +++ b/docs/header-stubs/compiler/spirv/spirv_info.h @@ -0,0 +1 @@ +struct spirv_capabilities {}; diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index 24181f1340e..400d023af33 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -153,6 +153,17 @@ vk_physical_device_properties = custom_target( depend_files : vk_physical_device_properties_gen_depend_files, ) +vk_physical_device_spirv_caps = custom_target( + 'vk_physical_device_spirv_caps', + input : [vk_physical_device_spirv_caps_gen, vk_api_xml], + output : 'vk_physical_device_spirv_caps.c', + command : [ + prog_python, '@INPUT0@', '--xml', '@INPUT1@', + '--out-c', '@OUTPUT0@', '--beta', with_vulkan_beta.to_string() + ], + depend_files : vk_physical_device_spirv_caps_gen_depend_files, +) + vk_synchronization_helpers = custom_target( 'vk_synchronization_helpers', input : [vk_synchronization_helpers_gen, vk_api_xml], @@ -183,6 +194,7 @@ vulkan_lite_runtime_files += [ vk_format_info, vk_physical_device_features, vk_physical_device_properties, + vk_physical_device_spirv_caps, vk_synchronization_helpers, ] @@ -230,6 +242,7 @@ libvulkan_lite_instance = static_library( # - idep_vulkan_runtime idep_vulkan_lite_runtime_headers = declare_dependency( sources : [ + spirv_info_h, vk_cmd_enqueue_entrypoints[0], vk_cmd_queue[1], vk_common_entrypoints[0], diff --git a/src/vulkan/runtime/vk_physical_device.h b/src/vulkan/runtime/vk_physical_device.h index e7da1ec34da..7aa54df07e8 100644 --- a/src/vulkan/runtime/vk_physical_device.h +++ b/src/vulkan/runtime/vk_physical_device.h @@ -145,6 +145,9 @@ VkResult vk_physical_device_check_device_features(struct vk_physical_device *physical_device, const VkDeviceCreateInfo *pCreateInfo); +struct spirv_capabilities +vk_physical_device_get_spirv_capabilities(const struct vk_physical_device *pdev); + #ifdef __cplusplus } #endif diff --git a/src/vulkan/util/meson.build b/src/vulkan/util/meson.build index 3861501efc1..969589b3d4e 100644 --- a/src/vulkan/util/meson.build +++ b/src/vulkan/util/meson.build @@ -52,6 +52,10 @@ vk_physical_device_features_gen_depend_files = [ vk_physical_device_properties_gen_depend_files = [ files('vk_extensions.py'), ] +vk_physical_device_spirv_caps_gen_depend_files = [ + files('vk_extensions.py'), + files('vk_physical_device_features_gen.py'), +] vk_synchronization_helpers_gen_depend_files = [ files('vk_extensions.py'), ] @@ -63,6 +67,7 @@ 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') +vk_physical_device_spirv_caps_gen = files('vk_physical_device_spirv_caps_gen.py') vk_synchronization_helpers_gen = files('vk_synchronization_helpers_gen.py') files_vulkan_util = files( diff --git a/src/vulkan/util/vk_physical_device_spirv_caps_gen.py b/src/vulkan/util/vk_physical_device_spirv_caps_gen.py new file mode 100644 index 00000000000..a50ad605046 --- /dev/null +++ b/src/vulkan/util/vk_physical_device_spirv_caps_gen.py @@ -0,0 +1,125 @@ +COPYRIGHT=u""" +/* Copyright 2024 Valve Corporation + * Copyright 2021 Intel Corporation + * SPDX-License-Identifier: MIT + */ +""" + +import argparse +from vk_physical_device_features_gen import get_renamed_feature, str_removeprefix +import os +import sys +import xml.etree.ElementTree as et + +import mako +from mako.template import Template + +TEMPLATE_C = Template(COPYRIGHT + """ +/* This file generated from ${filename}, don't edit directly. */ + +#include "vk_physical_device.h" +#include "vk_instance.h" +#include "vk_shader.h" + +/* for spirv_supported_capabilities */ +#include "compiler/spirv/spirv_info.h" + +struct spirv_capabilities +vk_physical_device_get_spirv_capabilities(const struct vk_physical_device *pdev) +{ + const struct vk_features *f = &pdev->supported_features; + const struct vk_device_extension_table *e = &pdev->supported_extensions; + const struct vk_properties *p = &pdev->properties; + uint32_t api_version = pdev->instance->app_info.api_version; + + struct spirv_capabilities caps = { false, }; + + /* We |= for everything because some caps have multiple names but the + * same enum value and they sometimes have different enables in the + * Vulkan spec. To handle this, we just | all the enables together. + */ +% for cap in caps: + caps.${cap} |= ${' | '.join(caps[cap])}; +% endfor + + return caps; +} +""") + +# These don't exist in the SPIR-V headers for one reason or another. +NON_EXISTANT_CAPS = [ + # This isn't a cap, it's an execution mode. + # + # https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6618 + 'MaximallyReconvergesKHR', + + # This extension got published but never got merged to SPIRV-Headers + # + # https://gitlab.khronos.org/spirv/spirv-extensions/-/merge_requests/238 + 'ClusterCullingShadingHUAWEI', + + # Exclude the one beta cap. + 'ShaderEnqueueAMDX', +] + +def process_enable(enab): + attrib = enab.attrib + + if 'property' in attrib: + if attrib['value'] == 'VK_TRUE': + return f"p->{attrib['member']}" + else: + return f"(p->{attrib['member']} & {attrib['value']})" + elif 'extension' in attrib: + return f"e->{str_removeprefix(attrib['extension'], 'VK_')}" + elif 'feature' in attrib: + feat = get_renamed_feature(attrib['struct'], attrib['feature']) + return f"f->{feat}" + else: + version = attrib['version'] + return f"(api_version >= VK_API_{str_removeprefix(version, 'VK_')})" + +def get_capabilities(doc, beta): + caps = {} + + for cap in doc.findall('./spirvcapabilities/spirvcapability'): + name = cap.attrib['name'] + if name in NON_EXISTANT_CAPS: + continue + + enables = cap.findall('enable') + lst = caps.setdefault(name, []) + lst += [process_enable(x) for x in enables] + + # Remove duplicates + for cap in caps: + caps[cap] = list(dict.fromkeys(caps[cap])) + + return caps + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--out-c', required=True, help='Output C file.') + parser.add_argument('--beta', required=True, help='Enable beta extensions.') + parser.add_argument('--xml', required=True, help='Vulkan API XML file.') + args = parser.parse_args() + + environment = { + 'filename': os.path.basename(__file__), + 'caps': get_capabilities(et.parse(args.xml), args.beta), + } + + try: + with open(args.out_c, 'w', encoding='utf-8') as f: + f.write(TEMPLATE_C.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()