From 2e5718c957415b93da6be5e1ff61d09e5e9933a2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tapani=20P=C3=A4lli?= Date: Fri, 1 Oct 2021 07:55:09 +0300 Subject: [PATCH] vulkan: provide common functions to check device features MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit v2: move checks to vk_device_init, ignore struct types from provisional extensions + lots of cleanups/fixes (Jason Ekstrand) v3: squash in following patch from Jason: "vulkan: Restructure vk_physical_device_check_device_features()" v4 (Jason): Fix a Windows build error Signed-off-by: Tapani Pälli Reviewed-by: Jason Ekstrand Part-of: --- src/vulkan/util/meson.build | 17 +- src/vulkan/util/vk_device.c | 7 + src/vulkan/util/vk_physical_device_features.py | 266 +++++++++++++++++++++++++ 3 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 src/vulkan/util/vk_physical_device_features.py diff --git a/src/vulkan/util/meson.build b/src/vulkan/util/meson.build index dddcfad..bdbacbc 100644 --- a/src/vulkan/util/meson.build +++ b/src/vulkan/util/meson.build @@ -42,6 +42,10 @@ vk_commands_gen_depend_files = [ files('vk_dispatch_table_gen.py'), vk_dispatch_table_gen_depend_files, ] +vk_physical_device_features_gen_depend_files = [ + files('vk_dispatch_table_gen.py'), + vk_dispatch_table_gen_depend_files, +] vk_entrypoints_gen = files('vk_entrypoints_gen.py') vk_extensions_gen = files('vk_extensions_gen.py') @@ -138,10 +142,21 @@ vk_cmd_queue = custom_target( depend_files : vk_cmd_queue_gen_depend_files, ) +vk_physical_device_features = custom_target( + 'vk_physical_device_features', + input : ['vk_physical_device_features.py', vk_api_xml], + output : ['vk_physical_device_features.c', 'vk_physical_device_features.h'], + command : [ + prog_python, '@INPUT0@', '--xml', '@INPUT1@', + '--out-c', '@OUTPUT0@', '--out-h', '@OUTPUT1@' + ], + depend_files : vk_physical_device_features_gen_depend_files, +) + libvulkan_util = static_library( 'vulkan_util', [files_vulkan_util, vk_common_entrypoints, vk_dispatch_table, - vk_enum_to_str, vk_extensions, vk_cmd_queue], + vk_enum_to_str, vk_extensions, vk_cmd_queue, vk_physical_device_features], include_directories : [inc_include, inc_src, inc_gallium], dependencies : [vulkan_wsi_deps, idep_mesautil, idep_nir_headers], # For glsl_type_singleton diff --git a/src/vulkan/util/vk_device.c b/src/vulkan/util/vk_device.c index 256659f..4c14c68 100644 --- a/src/vulkan/util/vk_device.c +++ b/src/vulkan/util/vk_device.c @@ -26,6 +26,7 @@ #include "vk_common_entrypoints.h" #include "vk_instance.h" #include "vk_physical_device.h" +#include "vk_physical_device_features.h" #include "vk_queue.h" #include "vk_util.h" #include "util/hash_table.h" @@ -75,6 +76,12 @@ vk_device_init(struct vk_device *device, device->enabled_extensions.extensions[idx] = true; } + VkResult result = + vk_physical_device_check_device_features(physical_device, + pCreateInfo); + if (result != VK_SUCCESS) + return result; + p_atomic_set(&device->private_data_next_index, 0); list_inithead(&device->queues); diff --git a/src/vulkan/util/vk_physical_device_features.py b/src/vulkan/util/vk_physical_device_features.py new file mode 100644 index 0000000..55d5610 --- /dev/null +++ b/src/vulkan/util/vk_physical_device_features.py @@ -0,0 +1,266 @@ +# coding=utf-8 +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 +import os +import re +from collections import OrderedDict, namedtuple +import xml.etree.ElementTree as et + +from mako.template import Template + +# Mesa-local imports must be declared in meson variable +# '{file_without_suffix}_depend_files'. +from vk_extensions import * +from vk_dispatch_table_gen import get_entrypoints_from_xml, EntrypointParam + +TEMPLATE_H = Template(COPYRIGHT + """\ +/* This file generated from ${filename}, don't edit directly. */ + +#pragma once + +#define VK_PROTOTYPES +#include +#include "vk_physical_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +VkResult +vk_physical_device_check_device_features(struct vk_physical_device *physical_device, + const VkDeviceCreateInfo *pCreateInfo); + +#ifdef __cplusplus +} +#endif +""", output_encoding='utf-8') + +TEMPLATE_C = Template(COPYRIGHT + """ +/* This file generated from ${filename}, don't edit directly. */ + +#include "${header}" + +#define VK_PROTOTYPES +#include + +#include "vk_dispatch_table.h" +#include "vk_physical_device.h" +#include "vk_util.h" + +static VkResult +check_physical_device_features(const VkPhysicalDeviceFeatures *supported, + const VkPhysicalDeviceFeatures *enabled) +{ + const VkBool32 *supported_feature = (const VkBool32 *) supported; + const VkBool32 *enabled_feature = (const VkBool32 *) enabled; + unsigned num_features = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); + for (uint32_t i = 0; i < num_features; i++) { + if (enabled_feature[i] && !supported_feature[i]) + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + return VK_SUCCESS; +} + +VkResult +vk_physical_device_check_device_features(struct vk_physical_device *physical_device, + const VkDeviceCreateInfo *pCreateInfo) +{ + VkPhysicalDevice vk_physical_device = + vk_physical_device_to_handle(physical_device); + + /* Query the device what kind of features are supported. */ + VkPhysicalDeviceFeatures2 supported_features2 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + }; + +% for f in features: + ${f.name} supported_${f.name} = { .pNext = NULL }; +% endfor + + vk_foreach_struct_const(feat, pCreateInfo->pNext) { + VkBaseOutStructure *supported = NULL; + switch (feat->sType) { +% for f in features: + case ${f.vk_type}: + supported = (VkBaseOutStructure *) &supported_${f.name}; + break; +% endfor + default: + break; + } + + /* Not a feature struct. */ + if (!supported) + continue; + + /* Check for cycles in the list */ + if (supported->pNext != NULL || supported->sType != 0) + return VK_ERROR_UNKNOWN; + + supported->sType = feat->sType; + __vk_append_struct(&supported_features2, supported); + } + + physical_device->dispatch_table.GetPhysicalDeviceFeatures2( + vk_physical_device, &supported_features2); + + if (pCreateInfo->pEnabledFeatures) { + VkResult result = + check_physical_device_features(&supported_features2.features, + pCreateInfo->pEnabledFeatures); + if (result != VK_SUCCESS) + return result; + } + + /* Iterate through additional feature structs */ + vk_foreach_struct_const(feat, pCreateInfo->pNext) { + /* Check each feature boolean for given structure. */ + switch (feat->sType) { + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { + const VkPhysicalDeviceFeatures2 *features2 = (const void *)feat; + VkResult result = + check_physical_device_features(&supported_features2.features, + &features2->features); + if (result != VK_SUCCESS) + return result; + break; + } +% for f in features: + case ${f.vk_type} : { + ${f.name} *a = &supported_${f.name}; + ${f.name} *b = (${f.name} *) feat; +% for flag in f.vk_flags: + if (b->${flag} && !a->${flag}) + return VK_ERROR_FEATURE_NOT_PRESENT; +% endfor + break; + } +% endfor + default: + break; + } + } // for each extension structure + return VK_SUCCESS; +} + +""", output_encoding='utf-8') + +Feature = namedtuple('Feature', 'name vk_type vk_flags') + +def get_features(doc): + features = OrderedDict() + + provisional_structs = set() + + # we want to ignore struct types that are part of provisional extensions + for _extension in doc.findall('./extensions/extension'): + if _extension.attrib.get('provisional') != 'true': + continue + for p in _extension.findall('./require/type'): + provisional_structs.add(p.attrib.get('name')) + + # parse all struct types where structextends VkPhysicalDeviceFeatures2 + for _type in doc.findall('./types/type'): + if _type.attrib.get('category') != 'struct': + continue + if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo': + continue + if _type.attrib.get('name') in provisional_structs: + continue + + # find Vulkan structure type + for elem in _type: + if "STRUCTURE_TYPE" in str(elem.attrib): + s_type = elem.attrib.get('values') + + # collect a list of feature flags + flags = [] + + for p in _type.findall('./member'): + m_name = p.find('./name').text + if m_name == 'pNext': + pass + elif m_name == 'sType': + s_type = p.attrib.get('values') + else: + assert p.find('./type').text == 'VkBool32' + flags.append(m_name) + + feat = Feature(name=_type.attrib.get('name'), vk_type=s_type, vk_flags=flags) + features[_type.attrib.get('name')] = feat + + return features.values() + +def get_features_from_xml(xml_files): + features = [] + + for filename in xml_files: + doc = et.parse(filename) + features += get_features(doc) + + return features + + +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('--xml', + help='Vulkan API XML file.', + required=True, action='append', dest='xml_files') + args = parser.parse_args() + + features = get_features_from_xml(args.xml_files) + + assert os.path.dirname(args.out_c) == os.path.dirname(args.out_h) + + environment = { + 'header': os.path.basename(args.out_h), + 'filename': os.path.basename(__file__), + 'features': features, + } + + try: + with open(args.out_h, 'wb') as f: + guard = os.path.basename(args.out_h).replace('.', '_').upper() + f.write(TEMPLATE_H.render(guard=guard, **environment)) + with open(args.out_c, 'wb') 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 + if __debug__: + import sys + from mako import exceptions + sys.stderr.write(exceptions.text_error_template().render() + '\n') + sys.exit(1) + raise + +if __name__ == '__main__': + main() -- 2.7.4