Module: Mesa Branch: main Commit: fe12c1c29e52e13c2dbda35d9621ce09ccf101c2 URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=fe12c1c29e52e13c2dbda35d9621ce09ccf101c2
Author: Faith Ekstrand <faith.ekstr...@collabora.com> Date: Thu Nov 30 17:45:28 2023 -0600 vulkan: Add some auto-generated synchronization helpers These are helpful for drivers to implement synchronization rules Reviewed-by: Lionel Landwerlin <lionel.g.landwer...@intel.com> Reviewed-by: Konstantin Seurer <konstantin.seu...@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26407> --- src/vulkan/runtime/meson.build | 16 +- src/vulkan/runtime/vk_synchronization.h | 55 ++++++ src/vulkan/util/meson.build | 4 + src/vulkan/util/vk_synchronization_helpers_gen.py | 225 ++++++++++++++++++++++ 4 files changed, 299 insertions(+), 1 deletion(-) diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index e4450f797f4..9f4e5730db5 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -109,6 +109,7 @@ vulkan_runtime_files = files( 'vk_sync_timeline.c', 'vk_sync_timeline.h', 'vk_synchronization.c', + 'vk_synchronization.h', 'vk_texcompress_etc2.c', 'vk_texcompress_etc2.h', 'vk_video.c', @@ -218,6 +219,18 @@ vk_physical_device_properties = custom_target( depend_files : vk_physical_device_properties_gen_depend_files, ) +vk_synchronization_helpers = custom_target( + 'vk_synchronization_helpers', + input : [vk_synchronization_helpers_gen, vk_api_xml], + output : ['vk_synchronization_helpers.c', 'vk_synchronization_helpers.h'], + command : [ + prog_python, '@INPUT0@', '--xml', '@INPUT1@', + '--out-c', '@OUTPUT0@', + '--beta', with_vulkan_beta.to_string() + ], + depend_files : vk_synchronization_helpers_gen_depend_files, +) + vk_format_info = custom_target( 'vk_format_info', input : ['vk_format_info_gen.py', vk_api_xml], @@ -233,7 +246,8 @@ 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_physical_device_properties, vk_format_info], + vk_physical_device_properties, vk_synchronization_helpers, + vk_format_info], include_directories : [inc_include, inc_src], dependencies : vulkan_runtime_deps, c_args : c_msvc_compat_args, diff --git a/src/vulkan/runtime/vk_synchronization.h b/src/vulkan/runtime/vk_synchronization.h new file mode 100644 index 00000000000..da1565060e9 --- /dev/null +++ b/src/vulkan/runtime/vk_synchronization.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2023 Collabora, Ltd + * + * 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. + */ +#ifndef VK_SYNCHRONIZATION_H +#define VK_SYNCHRONIZATION_H + +#include <vulkan/vulkan_core.h> + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Expands pipeline stage group flags + * + * Some stages like VK_PIPELINE_SHADER_STAGE_2_ALL_GRAPHICS_BIT represent more + * than one stage. This helper expands any such bits out to the full set of + * individual stages bits they represent. + */ +VkPipelineStageFlags2 +vk_expand_pipeline_stage_flags2(VkPipelineStageFlags2 stages); + +/** Returns the set of read accesses allowed in the given stages */ +VkAccessFlags2 +vk_read_access2_for_pipeline_stage_flags2(VkPipelineStageFlags2 stages); + +/** Returns the set of write accesses allowed in the given stages */ +VkAccessFlags2 +vk_write_access2_for_pipeline_stage_flags2(VkPipelineStageFlags2 stages); + +#ifdef __cplusplus +} +#endif + +#endif /* VK_SYNCHRONIZATION_H */ diff --git a/src/vulkan/util/meson.build b/src/vulkan/util/meson.build index 451ca450720..90ebfd6fb71 100644 --- a/src/vulkan/util/meson.build +++ b/src/vulkan/util/meson.build @@ -52,6 +52,9 @@ vk_physical_device_features_gen_depend_files = [ vk_physical_device_properties_gen_depend_files = [ files('vk_extensions.py'), ] +vk_synchronization_helpers_gen_depend_files = [ + files('vk_extensions.py'), +] vk_entrypoints_gen = files('vk_entrypoints_gen.py') vk_extensions_gen = files('vk_extensions_gen.py') @@ -60,6 +63,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_synchronization_helpers_gen = files('vk_synchronization_helpers_gen.py') files_vulkan_util = files( 'vk_alloc.c', diff --git a/src/vulkan/util/vk_synchronization_helpers_gen.py b/src/vulkan/util/vk_synchronization_helpers_gen.py new file mode 100644 index 00000000000..46ed9ce1dd7 --- /dev/null +++ b/src/vulkan/util/vk_synchronization_helpers_gen.py @@ -0,0 +1,225 @@ +COPYRIGHT=u""" +/* Copyright © 2023 Collabora, Ltd. + * + * 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 +import os +import textwrap +import xml.etree.ElementTree as et + +from mako.template import Template +from vk_extensions import get_api_list + +TEMPLATE_C = Template(COPYRIGHT + """\ +#include "vk_synchronization.h" + +VkPipelineStageFlags2 +vk_expand_pipeline_stage_flags2(VkPipelineStageFlags2 stages) +{ +% for (group_stage, stages) in group_stages.items(): + if (stages & ${group_stage}) + stages |= ${' |\\n '.join(stages)}; + +% endfor + if (stages & VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT) { +% for (guard, stage) in all_commands_stages: +% if guard is not None: +#ifdef ${guard} +% endif + stages |= ${stage}; +% if guard is not None: +#endif +% endif +% endfor + } + + return stages; +} + +VkAccessFlags2 +vk_read_access2_for_pipeline_stage_flags2(VkPipelineStageFlags2 stages) +{ + VkAccessFlags2 access = 0; + +% for ((guard, stages), access) in stages_read_access.items(): +% if guard is not None: +#ifdef ${guard} +% endif + if (stages & (${' |\\n '.join(stages)})) + access |= ${' |\\n '.join(access)}; +% if guard is not None: +#endif +% endif + +% endfor + return access; +} + +VkAccessFlags2 +vk_write_access2_for_pipeline_stage_flags2(VkPipelineStageFlags2 stages) +{ + VkAccessFlags2 access = 0; + +% for ((guard, stages), access) in stages_write_access.items(): +% if guard is not None: +#ifdef ${guard} +% endif + if (stages & (${' |\\n '.join(stages)})) + access |= ${' |\\n '.join(access)}; +% if guard is not None: +#endif +% endif + +% endfor + return access; +} +""") + +def get_guards(xml, api): + guards = {} + for ext_elem in xml.findall('./extensions/extension'): + supported = get_api_list(ext_elem.attrib['supported']) + if api not in supported: + continue + + for enum in ext_elem.findall('./require/enum[@extends]'): + if enum.attrib['extends'] not in ('VkPipelineStageFlagBits2', + 'VkAccessFlagBits2'): + continue + + if 'protect' not in enum.attrib: + continue + + name = enum.attrib['name'] + guard = enum.attrib['protect'] + guards[name] = guard + + return guards + +def get_all_commands_stages(xml, guards): + stages = [] + for stage in xml.findall('./sync/syncstage'): + stage_name = stage.attrib['name'] + + exclude = [ + # This isn't a real stage + 'VK_PIPELINE_STAGE_2_NONE', + + # These are real stages but they're a bit weird to include in + # ALL_COMMANDS because they're context-dependent, depending on + # whether they're part of srcStagesMask or dstStagesMask. + # + # We could avoid all grouped stages but then if someone adds + # another group later, the behavior of this function may change in + # a backwards-compatible way. Also, the other ones aren't really + # hurting anything if we add them in. + 'VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT', + 'VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT', + + # This is all COMMANDS, not host. + 'VK_PIPELINE_STAGE_2_HOST_BIT', + ] + if stage_name in exclude: + continue + + guard = guards.get(stage_name, None) + stages.append((guard, stage_name)) + + return stages + +def get_group_stages(xml): + group_stages = {} + for stage in xml.findall('./sync/syncstage'): + name = stage.attrib['name'] + equiv = stage.find('./syncequivalent') + if equiv is not None: + stages = equiv.attrib['stage'].split(',') + group_stages[name] = stages + + return group_stages + +def access_is_read(name): + if 'READ' in name: + assert 'WRITE' not in name + return True + elif 'WRITE' in name: + return False + else: + print(name) + assert False, "Invalid access bit name" + +def get_stages_access(xml, read, guards): + stages_access = {} + for access in xml.findall('./sync/syncaccess'): + access_name = access.attrib['name'] + if access_name == 'VK_ACCESS_2_NONE': + continue + + if access_is_read(access_name) != read: + continue + + guard = guards.get(access_name, None) + support = access.find('./syncsupport') + if support is not None: + stages = support.attrib['stage'].split(',') + stages.sort() + key = (guard, tuple(stages)) + if key in stages_access: + stages_access[key].append(access_name) + else: + stages_access[key] = [access_name] + + return stages_access + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--beta', required=True, help='Enable beta extensions.') + parser.add_argument('--xml', required=True, help='Vulkan API XML file') + parser.add_argument('--out-c', required=True, help='Output C file.') + args = parser.parse_args() + + xml = et.parse(args.xml); + + guards = get_guards(xml, 'vulkan') + environment = { + 'all_commands_stages': get_all_commands_stages(xml, guards), + 'group_stages': get_group_stages(xml), + 'stages_read_access': get_stages_access(xml, True, guards), + 'stages_write_access': get_stages_access(xml, False, guards), + } + + try: + with open(args.out_c, 'w') as f: + f.write(TEMPLATE_C.render(**environment)) + except Exception: + # In the event there's an error, this imports 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 + import sys + from mako import exceptions + print(exceptions.text_error_template().render(), file=sys.stderr) + sys.exit(1) + +if __name__ == '__main__': + main()