Update to latest headers & fix errors & cleanup generation script

VulkanSC was added to vk.xml and broke the generation script. This was an
easy fix, just needed to specify which API the version information should
use. Still, took the time to cleanup the code by running pylint and fixing
anything it warned about.
This commit is contained in:
Charles Giessen 2023-09-19 14:32:22 -06:00 committed by Charles Giessen
parent 61f77612c7
commit 04ec13b48d
9 changed files with 1432 additions and 1679 deletions

View File

@ -7,6 +7,7 @@ option(CATCH_BUILD_TESTING "" OFF)
option(CATCH_ENABLE_WERROR "" OFF)
option(CATCH_INSTALL_DOCS "" OFF)
option(CATCH_INSTALL_HELPERS "" OFF)
option(CATCH_INSTALL_EXTRAS "" ON)
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) #remove Catch2 target spam
include(FetchContent)
@ -19,6 +20,7 @@ FetchContent_MakeAvailable(glfw)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2
GIT_TAG v2.13.7
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(Catch2)

View File

@ -1,2 +1,2 @@
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION 1.3.240)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v1.3.240)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION 1.3.264)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v1.3.264)

View File

@ -31,6 +31,17 @@
# Command Line Arguments
# [--auto] Don't ask for input from the command line
import sys
import os
import subprocess
import copy
import codecs
import re
from string import Template
import urllib.request
import pkg_resources
# Exclusions
exclusions = [
'vkGetDeviceProcAddr',
@ -48,47 +59,37 @@ excluded_alias_types = [
]
# Check for/install xmltodict
import sys
import os
import subprocess
import pkg_resources
import copy
import codecs
import re
from string import Template
installed = {pkg.key for pkg in pkg_resources.working_set}
xmltodict_missing = {'xmltodict'} - installed
# Install xmltodict
if xmltodict_missing:
if '--auto' not in sys.argv:
val = input("xmltodict is required to run this script. Would you like to install? (y/n): ")
val = input('xmltodict is required to run this script. Would you like to install? (y/n): ')
else:
val = "y"
if(val.lower() == "y"):
val = 'y'
if val.lower() == 'y':
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'xmltodict'])
except subprocess.CalledProcessError as error:
print("Failed to install xmltodict due to error:")
print('Failed to install xmltodict due to error:')
print(error)
if '--auto' not in sys.argv:
input("Press Enter to continue...")
input('Press Enter to continue...')
sys.exit()
else:
sys.exit()
# Fetch fresh vk.xml from Khronos repo
import urllib.request
import xmltodict
try:
response = urllib.request.urlopen('https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/registry/vk.xml')
except urllib.error.URLError as error:
print("Failed to download vk.xml due to error:")
print('Failed to download vk.xml due to error:')
print(error.reason)
if '-q' not in sys.argv:
input("Press Enter to continue...")
input('Press Enter to continue...')
sys.exit()
vk_xml_raw = response.read()
@ -112,10 +113,15 @@ for command_node in commands_node:
command_name = command_node['proto']['name']
new_command_params = copy.deepcopy(command_params)
new_command_params['return_type'] = command_node['proto']['type']
if type(command_node['param']) is list:
new_command_params['args'] = command_node['param']
if isinstance(command_node['param'], list):
params = command_node['param']
else:
new_command_params['args'] = [command_node['param']]
params = [command_node['param']]
new_command_params['args'] = []
for param in params:
# if the api attribute does exist, make sure it is for vulkan
if not '@api' in param or param['@api'] == 'vulkan':
new_command_params['args'].append(param)
if not command_name in exclusions:
if new_command_params['args'][0]['type'] in ['VkDevice', 'VkCommandBuffer', 'VkQueue']:
device_commands[command_name] = new_command_params
@ -124,10 +130,10 @@ for command_node in commands_node:
aliases[command_node['@alias']] = command_node['@name']
# Push the alias name as a device function if the alias exists in device commands
for alias in aliases:
for alias_name, alias in aliases.items():
if alias in device_commands:
device_commands[aliases[alias]] = copy.deepcopy(device_commands[alias])
device_commands[aliases[alias]]['is_alias'] = True
device_commands[alias] = copy.deepcopy(device_commands[alias_name])
device_commands[alias]['is_alias'] = True
# Add requirements for core PFN's
features_node = vk_xml['registry']['feature']
@ -136,7 +142,7 @@ for feature_node in features_node:
for require_node in feature_node['require']:
for param_node in require_node:
if param_node == 'command':
if type(require_node[param_node]) is not list:
if not isinstance(require_node[param_node], list):
require_node[param_node] = [require_node[param_node]]
for param in require_node[param_node]:
if param['@name'] in device_commands:
@ -151,13 +157,13 @@ for extension_node in extensions_node:
require_nodes = extension_node['require']
for require_node in require_nodes:
requirements = [extension_name]
if type(require_node) is not str:
if not isinstance(require_node, str):
if 'command' in require_node.keys():
if '@feature' in require_node.keys():
requirements.append(require_node['@feature'])
if '@extension' in require_node.keys():
requirements.extend(require_node['@extension'].split(','))
if type(require_node['command']) is not list:
if not isinstance(require_node['command'], list):
require_node['command'] = [require_node['command']]
for command_node in require_node['command']:
if command_node['@name'] in device_commands:
@ -166,7 +172,7 @@ for extension_node in extensions_node:
else:
device_commands[command_node['@name']]['requirements'] += [requirements]
elif require_node == 'command':
if type(require_nodes['command']) is not list:
if not isinstance(require_nodes['command'], list):
require_nodes['command'] = [require_nodes['command']]
for command_node in require_nodes['command']:
if command_node['@name'] in device_commands:
@ -176,18 +182,18 @@ for extension_node in extensions_node:
device_commands[command_node['@name']]['requirements'] += [requirements]
# Generate macro templates
for command in device_commands:
for command_name, command in device_commands.items():
macro = ''
requirements_collection = device_commands[command]['requirements']
requirements_collection = device_commands[command_name]['requirements']
collection_count = len(requirements_collection)
if collection_count > 0:
macro = '#if '
while(collection_count > 0):
while collection_count > 0:
for requirements in requirements_collection:
requirements_count = len(requirements)
macro += '('
for requirement in requirements:
macro += 'defined({})'.format(requirement)
macro += f'defined({requirement})'
requirements_count -= 1
if requirements_count > 0:
macro += ' && '
@ -199,26 +205,27 @@ for command in device_commands:
macro += '\n$body#endif\n'
else:
macro = '$body'
device_commands[command]['macro_template'] = Template(macro)
device_commands[command_name]['macro_template'] = Template(macro)
# License
license = '/* \n'
license += ' * Copyright © 2021 Cody Goodson (contact@vibimanx.com)\n'
license += ' * Copyright © 2022 Charles Giessen (charles@lunarg.com)\n'
license += ' * \n'
license += ' * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n'
license += ' * documentation files (the “Software”), to deal in the Software without restriction, including without\n'
license += ' * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n'
license += ' * of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n'
license += ' * \n'
license += ' * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n'
license += ' * \n'
license += ' * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\n'
license += ' * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n'
license += ' * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n'
license += ' * 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.\n'
license += ' * \n'
license += ' */\n\n'
dispatch_license = '''/*
* Copyright © 2021 Cody Goodson (contact@vibimanx.com)
* Copyright © 2022 Charles Giessen (charles@lunarg.com)
*
* 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 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.
*
*/
'''
# Info
info = '// This file is a part of VkBootstrap\n'
@ -239,17 +246,17 @@ proxy_template = Template('\t$return_type $proxy_name($args_full) const noexcept
fp_decl_template = Template('\t$pfn_name $fp_name = nullptr;\n')
pfn_load_template = Template('\t\t$fp_name = reinterpret_cast<$pfn_name>(procAddr(device, "$command_name"));\n')
for command in device_commands:
for command_name, command in device_commands.items():
params = device_commands[command]
# easy stuff out of the way
return_type = params['return_type']
if(return_type != 'void'):
if return_type != 'void':
opt_return = 'return '
else:
opt_return = ''
proxy_name = command[2].lower() + command[3:]
fp_name = 'fp_' + command
pfn_name = 'PFN_' + command
proxy_name = command_name[2].lower() + command_name[3:]
fp_name = 'fp_' + command_name
pfn_name = 'PFN_' + command_name
# Now for args
arg_template = Template('$front_mods$arg_type$back_mods$arg_name$array')
@ -330,25 +337,25 @@ body += '} // namespace vkb'
# find the version used to generate the code
for type_node in types_node:
if 'name' in type_node and type_node['name'] == 'VK_HEADER_VERSION_COMPLETE':
complete_header_version = type_node["#text"]
if 'name' in type_node and type_node['name'] == 'VK_HEADER_VERSION':
if '@api' in type_node and type_node['@api'] == 'vulkan' and 'name' in type_node and type_node['name'] == 'VK_HEADER_VERSION_COMPLETE':
complete_header_version = type_node['#text']
if '@api' in type_node and type_node['@api'] == 'vulkan' and 'name' in type_node and type_node['name'] == 'VK_HEADER_VERSION':
vk_header_version = type_node['#text']
find_number_fields = re.compile('[0-9]+')
version_fields = find_number_fields.findall(complete_header_version)
header_version_field = find_number_fields.findall(vk_header_version)[0]
version_tag = f'{version_fields[1]}.{version_fields[2]}.{header_version_field}'
header = license + info + body
header = dispatch_license + info + body
path_to_src = os.path.join('src')
if not os.path.exists(path_to_src):
path_to_src = os.path.join('..', 'src')
if not os.path.exists(path_to_src):
print("Couldn't find source folder. Is the current directory wrong?")
print('Could not find source folder. Is the current directory wrong?')
sys.exit()
header_file = codecs.open(os.path.join(path_to_src,"VkBootstrapDispatch.h"), "w", "utf-8")
header_file = codecs.open(os.path.join(path_to_src,'VkBootstrapDispatch.h'), 'w', 'utf-8')
header_file.write(header)
header_file.close()
@ -356,13 +363,13 @@ path_to_gen = os.path.join('gen')
if not os.path.exists(path_to_gen):
path_to_gen = os.path.join('..', 'gen')
if not os.path.exists(path_to_gen):
print("Couldn't find gen folder. Is the current directory wrong?")
print('Could not find gen folder. Is the current directory wrong?')
sys.exit()
# Generate a CMake file that contains the header version used.
cmake_version_file = codecs.open(os.path.join(path_to_gen,"CurrentBuildVulkanVersion.cmake"), "w", "utf-8")
cmake_version_file = codecs.open(os.path.join(path_to_gen,'CurrentBuildVulkanVersion.cmake'), 'w', 'utf-8')
cmake_version_file.write(f'set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION {version_tag})\n')
cmake_version_file.write(f'set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v{version_tag})\n')
cmake_version_file.close()
print("Generation finished.")
print('Generation finished.')

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,19 @@
add_executable(vk-bootstrap-test main.cpp bootstrap_tests.cpp error_code_tests.cpp unit_tests.cpp)
add_executable(vk-bootstrap-test
bootstrap_tests.cpp
error_code_tests.cpp
unit_tests.cpp)
target_link_libraries(vk-bootstrap-test
PRIVATE
vk-bootstrap
vk-bootstrap-vulkan-headers
vk-bootstrap-compiler-warnings
glfw
Catch2
Catch2::Catch2WithMain
)
# This should be how to make CTest aware of the tests, but unfortunately it fails to link. The tests run fine on their own, so this is left as a TODO
# list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras)
# include(CTest)
# include(Catch)
# catch_discover_tests(vk-bootstrap-test)

View File

@ -1,6 +1,6 @@
#include "common.h"
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
vkb::Instance get_instance(uint32_t minor_version = 0) {
auto instance_ret = vkb::InstanceBuilder().request_validation_layers().require_api_version(1, minor_version).build();
@ -542,7 +542,7 @@ TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_h
auto instance = get_instance();
// Check if we can get instance functions.
PFN_vkVoidFunction instanceFunction = instance.fp_vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT"); // validation layers should be provided.
PFN_vkVoidFunction instanceFunction = instance.fp_vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"); // validation layers should be provided.
REQUIRE(instanceFunction != NULL);
auto window = create_window_glfw("Conversion operators");

View File

@ -1,98 +1,88 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "VkBootstrap.h"
TEST_CASE ("is_error_code_enum", "[VkBootstrap.error_code]") {
STATIC_REQUIRE (std::is_error_code_enum<vkb::InstanceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::PhysicalDeviceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::QueueError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::DeviceError>::value);
STATIC_REQUIRE (std::is_error_code_enum<vkb::SwapchainError>::value);
TEST_CASE("is_error_code_enum", "[VkBootstrap.error_code]") {
STATIC_REQUIRE(std::is_error_code_enum<vkb::InstanceError>::value);
STATIC_REQUIRE(std::is_error_code_enum<vkb::PhysicalDeviceError>::value);
STATIC_REQUIRE(std::is_error_code_enum<vkb::QueueError>::value);
STATIC_REQUIRE(std::is_error_code_enum<vkb::DeviceError>::value);
STATIC_REQUIRE(std::is_error_code_enum<vkb::SwapchainError>::value);
}
TEST_CASE ("make_error_code", "[VkBootstrap.error_code]") {
GIVEN ("An InstanceError") {
TEST_CASE("make_error_code", "[VkBootstrap.error_code]") {
GIVEN("An InstanceError") {
const auto error = vkb::InstanceError::vulkan_unavailable;
WHEN ("Creating an error code from it") {
std::error_code ec = make_error_code (error);
THEN ("The error code is equal to the original error") { REQUIRE (ec == error); }
WHEN("Creating an error code from it") {
std::error_code ec = make_error_code(error);
THEN("The error code is equal to the original error") { REQUIRE(ec == error); }
THEN ("The error code is not equal to an unrelated error") {
REQUIRE (ec != vkb::InstanceError::failed_create_instance);
THEN("The error code is not equal to an unrelated error") {
REQUIRE(ec != vkb::InstanceError::failed_create_instance);
}
THEN ("We can get the error message") {
REQUIRE (ec.message () == "vulkan_unavailable");
}
THEN("We can get the error message") { REQUIRE(ec.message() == "vulkan_unavailable"); }
}
}
GIVEN ("A PhysicalDeviceError") {
GIVEN("A PhysicalDeviceError") {
const auto error = vkb::PhysicalDeviceError::no_physical_devices_found;
WHEN ("Creating an error code from it") {
std::error_code ec = make_error_code (error);
THEN ("The error code is equal to the original error") { REQUIRE (ec == error); }
WHEN("Creating an error code from it") {
std::error_code ec = make_error_code(error);
THEN("The error code is equal to the original error") { REQUIRE(ec == error); }
THEN ("The error code is not equal to an unrelated error") {
REQUIRE (ec != vkb::InstanceError::failed_create_instance);
THEN("The error code is not equal to an unrelated error") {
REQUIRE(ec != vkb::InstanceError::failed_create_instance);
}
THEN ("We can get the error message") {
REQUIRE (ec.message () == "no_physical_devices_found");
}
THEN("We can get the error message") { REQUIRE(ec.message() == "no_physical_devices_found"); }
}
}
GIVEN ("A QueueError") {
GIVEN("A QueueError") {
const auto error = vkb::QueueError::invalid_queue_family_index;
WHEN ("Creating an error code from it") {
std::error_code ec = make_error_code (error);
THEN ("The error code is equal to the original error") { REQUIRE (ec == error); }
WHEN("Creating an error code from it") {
std::error_code ec = make_error_code(error);
THEN("The error code is equal to the original error") { REQUIRE(ec == error); }
THEN ("The error code is not equal to an unrelated error") {
REQUIRE (ec != vkb::InstanceError::failed_create_instance);
THEN("The error code is not equal to an unrelated error") {
REQUIRE(ec != vkb::InstanceError::failed_create_instance);
}
THEN ("We can get the error message") {
REQUIRE (ec.message () == "invalid_queue_family_index");
}
THEN("We can get the error message") { REQUIRE(ec.message() == "invalid_queue_family_index"); }
}
}
GIVEN ("A DeviceError") {
GIVEN("A DeviceError") {
const auto error = vkb::DeviceError::failed_create_device;
WHEN ("Creating an error code from it") {
std::error_code ec = make_error_code (error);
THEN ("The error code is equal to the original error") { REQUIRE (ec == error); }
WHEN("Creating an error code from it") {
std::error_code ec = make_error_code(error);
THEN("The error code is equal to the original error") { REQUIRE(ec == error); }
THEN ("The error code is not equal to an unrelated error") {
REQUIRE (ec != vkb::InstanceError::failed_create_instance);
THEN("The error code is not equal to an unrelated error") {
REQUIRE(ec != vkb::InstanceError::failed_create_instance);
}
THEN ("We can get the error message") {
REQUIRE (ec.message () == "failed_create_device");
}
THEN("We can get the error message") { REQUIRE(ec.message() == "failed_create_device"); }
}
}
GIVEN ("A SwapchainError") {
GIVEN("A SwapchainError") {
const auto error = vkb::SwapchainError::failed_create_swapchain;
WHEN ("Creating an error code from it") {
std::error_code ec = make_error_code (error);
THEN ("The error code is equal to the original error") { REQUIRE (ec == error); }
WHEN("Creating an error code from it") {
std::error_code ec = make_error_code(error);
THEN("The error code is equal to the original error") { REQUIRE(ec == error); }
THEN ("The error code is not equal to an unrelated error") {
REQUIRE (ec != vkb::InstanceError::failed_create_instance);
THEN("The error code is not equal to an unrelated error") {
REQUIRE(ec != vkb::InstanceError::failed_create_instance);
}
THEN ("We can get the error message") {
REQUIRE (ec.message () == "failed_create_swapchain");
}
THEN("We can get the error message") { REQUIRE(ec.message() == "failed_create_swapchain"); }
}
}
}

View File

@ -1,2 +0,0 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

View File

@ -1,4 +1,4 @@
#include <catch2/catch.hpp>
#include <catch2/catch_test_macros.hpp>
#include "VkBootstrap.cpp"