mirror of
https://gitee.com/openharmony/third_party_vulkan-loader
synced 2024-11-30 10:51:28 +00:00
test: Add framework cmake and utilities
Create the CMake infrastructure for a testing framework. `test_util.h/.cpp` contains the core macros, wrappers, and utilities. framework_config.h.in is the header that other components include to get the full path to binaries on a system. Change-Id: I3e24a3ee4ff24da499a3bca63059e3b872d3981c
This commit is contained in:
parent
d7c14e5c2a
commit
2703d19221
@ -34,6 +34,7 @@ elseif(ANDROID)
|
||||
elseif(APPLE)
|
||||
add_definitions(-DVK_USE_PLATFORM_MACOS_MVK)
|
||||
elseif(UNIX AND NOT APPLE) # i.e.: Linux
|
||||
target_compile_options(vk_loader_validation_tests PUBLIC -fPIC)
|
||||
if(BUILD_WSI_XCB_SUPPORT)
|
||||
add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
|
||||
endif()
|
||||
@ -135,3 +136,41 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
add_subdirectory(layers)
|
||||
|
||||
option(TEST_USE_ADDRESS_SANITIZER "Linux only: Advanced memory checking" OFF)
|
||||
|
||||
add_subdirectory(framework)
|
||||
|
||||
add_executable(test_regression loader_testing_main.cpp loader_regression_tests.cpp loader_version_tests.cpp)
|
||||
target_link_libraries(test_regression PRIVATE testing_dependencies)
|
||||
|
||||
add_executable(test_wsi loader_testing_main.cpp loader_wsi_tests.cpp)
|
||||
target_link_libraries(test_wsi PRIVATE testing_dependencies)
|
||||
|
||||
if(UNIX AND NOT APPLE) # i.e. Linux
|
||||
if(BUILD_WSI_XCB_SUPPORT)
|
||||
target_include_directories(test_wsi PRIVATE ${XCB_INCLUDE_DIRS})
|
||||
target_link_libraries(test_wsi PRIVATE ${XCB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(BUILD_WSI_XLIB_SUPPORT)
|
||||
target_include_directories(test_wsi PRIVATE ${X11_INCLUDE_DIR})
|
||||
target_link_libraries(test_wsi PRIVATE ${X11_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(BUILD_WSI_WAYLAND_SUPPORT)
|
||||
target_include_directories(test_wsi PRIVATE ${WAYLAND_CLIENT_INCLUDE_DIR})
|
||||
target_link_libraries(test_wsi PRIVATE ${WAYLAND_CLIENT_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Copy loader and googletest (gtest) libs to test dir so the test executable can find them.
|
||||
add_custom_command(TARGET test_regression POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:gtest> $<TARGET_FILE_DIR:test_regression>)
|
||||
# Copy the loader shared lib (if built) to the test application directory so the test app finds it.
|
||||
if(TARGET vulkan)
|
||||
add_custom_command(TARGET test_regression POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:vulkan> $<TARGET_FILE_DIR:test_regression>)
|
||||
endif()
|
||||
endif()
|
83
tests/framework/CMakeLists.txt
Normal file
83
tests/framework/CMakeLists.txt
Normal file
@ -0,0 +1,83 @@
|
||||
# ~~~
|
||||
# Copyright (c) 2021 Valve Corporation
|
||||
# Copyright (c) 2021 LunarG, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ~~~
|
||||
|
||||
add_library(testing_framework_util STATIC test_util.cpp test_util.h)
|
||||
target_link_libraries(testing_framework_util PUBLIC Vulkan::Headers)
|
||||
if(UNIX)
|
||||
target_link_libraries(testing_framework_util PUBLIC ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
target_compile_options(testing_framework_util INTERFACE
|
||||
$<$<PLATFORM_ID:WIN32>:-DVK_USE_PLATFORM_WIN32_KHR>
|
||||
$<$<PLATFORM_ID:ANDROID>:-DVK_USE_PLATFORM_ANDROID_KHR>
|
||||
$<$<PLATFORM_ID:APPLE>:-DVK_USE_PLATFORM_MACOS_MVK>
|
||||
$<$<AND:$<PLATFORM_ID:UNIX>,$<NOT:$<PLATFORM_ID:APPLE>>>:
|
||||
$<$<BUILD_WSI_XCB_SUPPORT>:-DVK_USE_PLATFORM_XCB_KHR>
|
||||
$<$<BUILD_WSI_XLIB_SUPPORT>:-DVK_USE_PLATFORM_XLIB_KHR>
|
||||
$<$<BUILD_WSI_WAYLAND_SUPPORT>:-DVK_USE_PLATFORM_WAYLAND_KHR>>
|
||||
)
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_compile_options(testing_framework_util PUBLIC -fPIC)
|
||||
endif()
|
||||
target_include_directories(testing_framework_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (TEST_USE_ADDRESS_SANITIZER AND UNIX)
|
||||
target_compile_options(testing_framework_util PUBLIC -fsanitize=address)
|
||||
target_link_options(testing_framework_util PUBLIC -fsanitize=address)
|
||||
target_compile_options(vulkan PUBLIC -fsanitize=address)
|
||||
target_link_options(vulkan PUBLIC -fsanitize=address)
|
||||
endif()
|
||||
|
||||
add_subdirectory(shim)
|
||||
add_subdirectory(icd)
|
||||
add_subdirectory(layer)
|
||||
|
||||
#configure the framework_config.h.in file - used to locate all the binaries generated so that it can be used in the tests
|
||||
#setup framework_config_temp.h.in in the current binary directory
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/framework_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in")
|
||||
|
||||
# setup framework_config.h.in using framework_config_temp.h.in as a source
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$<CONFIG>.h" INPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in")
|
||||
|
||||
# copy framework_config_$<CONFIG> to the loader build directory
|
||||
add_custom_command(
|
||||
PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} "-E" "copy_if_different" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$<CONFIG>.h" "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h"
|
||||
VERBATIM
|
||||
PRE_BUILD
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$<CONFIG>.h"
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h"
|
||||
COMMENT "creating framework_config.h file ({event: PRE_BUILD}, {filename: framework_config.h })"
|
||||
)
|
||||
add_custom_target (generate_framework_config DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/framework_config.h")
|
||||
add_dependencies (testing_framework_util generate_framework_config)
|
||||
|
||||
add_library(test-environment STATIC test_environment.cpp test_environment.h)
|
||||
target_link_libraries(test-environment
|
||||
PUBLIC gtest
|
||||
testing_framework_util shim-library)
|
||||
target_include_directories(test-environment PUBLIC ${CMAKE_BINARY_DIR}/tests/framework)
|
||||
target_compile_definitions(test-environment PUBLIC "GTEST_LINKED_AS_SHARED_LIBRARY=1")
|
||||
|
||||
add_library(testing_dependencies INTERFACE)
|
||||
|
||||
target_compile_options(testing_dependencies INTERFACE $<$<PLATFORM_ID:WIN32>:${MSVC_LOADER_COMPILE_OPTIONS},/permissive->)
|
||||
target_compile_definitions(testing_dependencies INTERFACE
|
||||
# Workaround for TR1 deprecation in Visual Studio 15.5 until Google Test is updated
|
||||
$<$<PLATFORM_ID:WIN32>:-DWIN32_LEAN_AND_MEAN,-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING>)
|
||||
|
||||
target_link_libraries(testing_dependencies INTERFACE Vulkan::Headers testing_framework_util test-environment)
|
31
tests/framework/README.md
Normal file
31
tests/framework/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
## Loader Testing Framework
|
||||
|
||||
The loader testing framework is a mocking environment which fakes many of the global systems that vulkan requires to run. This allows the writing of tests against a known driver, layer, and system configuration.
|
||||
|
||||
The framework consists of:
|
||||
* Test ICD and Test Layer - Programmable components which can have specific behavior, capabilities, features, extensions, and allow querying of various actions the loader takes
|
||||
* Shim - Platform specific functionality that isolates the loader from the rest of the system
|
||||
* Linux - Redirects of filesystem access
|
||||
* Windows - Overrides registry with framework component and has dll injection for
|
||||
* Manifest Writer - Programmatically create layer & ICD manifests and put them in a folder the framework will use
|
||||
* Tests - Written with GoogleTest, these are the specific test cases written using the various components
|
||||
* Utility - Glue code like environment variable, paths, boilerplate helpers
|
||||
|
||||
### Test ICD and Layer
|
||||
The layer and ICD interface have several combinations of functionality that is determined by the presence or lack of exported functions in a shared library. To accomplish the testing of these combinations, multiple binaries are created using the same C++ file. These exports are controlled by macro defines which are set through CMake.
|
||||
|
||||
To make it easy to run tests from the command line, extensive use CMake is made to put the paths of all the binaries into header files that tests can directly reference. These are the various `config.h.in` files scattered throughout the framework.
|
||||
Note: Due to hard coding of paths, moving the project to another folder without rebuilding will cause the tests to break.
|
||||
|
||||
Because the test ICDs and layers are loaded by the loader, the framework loads them directly and pulls the exported backdoor function which acts as the communication channel between the framework and the test ICDs & layers.
|
||||
|
||||
### Shim
|
||||
|
||||
Because the loader makes many calls to various OS functionality, the framework intercepts certain calls and makes a few of its own calls to OS functionality to allow proper isolation of the loader from the system it is running on.
|
||||
This allows multiple tests to be run in isolation.
|
||||
|
||||
### Running
|
||||
|
||||
CMake Build options: set `BUILD_TESTS` to enable tests, and `TEST_USE_ADDRESS_SANITIZER` to enable Address Sanitizer inside the testing framework
|
||||
|
||||
Run the executables as normal
|
47
tests/framework/framework_config.h.in
Normal file
47
tests/framework/framework_config.h.in
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The Khronos Group Inc.
|
||||
* Copyright (c) 2021 Valve Corporation
|
||||
* Copyright (c) 2021 LunarG, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and/or associated documentation files (the "Materials"), to
|
||||
* deal in the Materials without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Materials, and to permit persons to whom the Materials are
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice(s) and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Materials.
|
||||
*
|
||||
* THE MATERIALS ARE 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 MATERIALS OR THE
|
||||
* USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*
|
||||
* Author: Charles Giessen <charles@lunarg.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define FRAMEWORK_BUILD_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
|
||||
#define FRAMEWORK_VULKAN_LIBRARY_PATH "$<TARGET_FILE:vulkan>"
|
||||
|
||||
#define SHIM_LIBRARY_NAME "$<TARGET_FILE:shim-library>"
|
||||
|
||||
#define TEST_ICD_BUILD_LOCATION "$<TARGET_FILE_DIR:test_icd_export_none>"
|
||||
|
||||
#define TEST_ICD_PATH_EXPORT_NONE "$<TARGET_FILE:test_icd_export_none>"
|
||||
#define TEST_ICD_PATH_EXPORT_ICD_GIPA "$<TARGET_FILE:test_icd_export_icd_gipa>"
|
||||
#define TEST_ICD_PATH_EXPORT_NEGOTIATE_INTERFACE_VERSION "$<TARGET_FILE:test_icd_export_negotiate_interface_version>"
|
||||
|
||||
#define TEST_ICD_PATH_VERSION_2 "$<TARGET_FILE:test_icd_version_2>"
|
||||
|
||||
// assumes version 2 exports
|
||||
#define TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA "$<TARGET_FILE:test_icd_version_2_export_icd_gpdpa>"
|
||||
|
||||
// Assumes version 2 exports
|
||||
#define TEST_ICD_PATH_VERSION_2_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES "$<TARGET_FILE:test_icd_version_2_export_icd_enumerate_adapter_physical_devices>"
|
564
tests/framework/test_util.cpp
Normal file
564
tests/framework/test_util.cpp
Normal file
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The Khronos Group Inc.
|
||||
* Copyright (c) 2021 Valve Corporation
|
||||
* Copyright (c) 2021 LunarG, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and/or associated documentation files (the "Materials"), to
|
||||
* deal in the Materials without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Materials, and to permit persons to whom the Materials are
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice(s) and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Materials.
|
||||
*
|
||||
* THE MATERIALS ARE 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 MATERIALS OR THE
|
||||
* USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*
|
||||
* Author: Charles Giessen <charles@lunarg.com>
|
||||
*/
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
bool set_env_var(std::string const& name, std::string const& value) { return SetEnvironmentVariableA(name.c_str(), value.c_str()); }
|
||||
bool remove_env_var(std::string const& name) { return SetEnvironmentVariableA(name.c_str(), nullptr); }
|
||||
#define ENV_VAR_BUFFER_SIZE 4096
|
||||
std::string get_env_var(std::string const& name) {
|
||||
std::string value;
|
||||
value.resize(ENV_VAR_BUFFER_SIZE);
|
||||
DWORD in_size = 0;
|
||||
DWORD ret = GetEnvironmentVariable(name.c_str(), (LPSTR)value.c_str(), ENV_VAR_BUFFER_SIZE);
|
||||
if (0 == in_size) {
|
||||
ret = GetLastError();
|
||||
if (ERROR_ENVVAR_NOT_FOUND == ret) {
|
||||
std::cerr << "Environment variable does not exist.\n";
|
||||
}
|
||||
return std::string();
|
||||
} else if (ENV_VAR_BUFFER_SIZE < ret) {
|
||||
std::cerr << "Not enough space to write environment variable" << name << "\n";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
bool set_env_var(std::string const& name, std::string const& value) { return setenv(name.c_str(), value.c_str(), 1); }
|
||||
bool remove_env_var(std::string const& name) { return unsetenv(name.c_str()); }
|
||||
std::string get_env_var(std::string const& name) {
|
||||
char* ret = getenv(name.c_str());
|
||||
if (ret == nullptr) return std::string();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ManifestICD::get_manifest_str() const {
|
||||
std::string out;
|
||||
out += "{\n";
|
||||
out += " \"file_format_version\": \"1.0.0\",\n";
|
||||
out += " \"ICD\": {\n";
|
||||
out += " \"library_path\": \"" + lib_path + "\",\n";
|
||||
out += " \"api_version\": \"" + version_to_string(api_version) + "\"\n";
|
||||
out += " }\n";
|
||||
out += "}\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string ManifestLayer::LayerDescription::Extension::get_manifest_str() const {
|
||||
std::string out;
|
||||
out += "{ \"name\":\"" + name + "\",\n\t\t\t\"spec_version\":\"" + std::to_string(spec_version) + "\"";
|
||||
if (entrypoints.size() > 0) {
|
||||
out += ",\n\t\t\t\"entrypoints\": [";
|
||||
for (size_t i = 0; i < entrypoints.size(); i++) {
|
||||
if (i > 0) out += ", ";
|
||||
out += "\"" + entrypoints.at(i) + "\"";
|
||||
}
|
||||
out += "]";
|
||||
}
|
||||
out += "\t\t\t}";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string ManifestLayer::LayerDescription::get_manifest_str() const {
|
||||
std::string out;
|
||||
out += "\t{\n";
|
||||
out += "\t\t\"name\":\"" + name + "\",\n";
|
||||
out += "\t\t\"type\":\"" + get_type_str(type) + "\",\n";
|
||||
out += "\t\t\"library_path\": \"" + lib_path + "\",\n";
|
||||
out += "\t\t\"api_version\": \"" + version_to_string(api_version) + "\",\n";
|
||||
out += "\t\t\"implementation_version\":\"" + std::to_string(implementation_version) + "\",\n";
|
||||
out += "\t\t\"description\": \"" + description + "\"";
|
||||
if (functions.size() > 0) {
|
||||
out += ",\n\t\t{";
|
||||
for (size_t i = 0; i < functions.size(); i++) {
|
||||
if (i > 0) out += ",";
|
||||
out += "\n\t\t\t" + functions.at(i).get_manifest_str();
|
||||
}
|
||||
out += "\n\t\t}";
|
||||
}
|
||||
if (instance_extensions.size() > 0) {
|
||||
out += ",\n\t\t{";
|
||||
for (size_t i = 0; i < instance_extensions.size(); i++) {
|
||||
if (i > 0) out += ",";
|
||||
out += "\n\t\t\t" + instance_extensions.at(i).get_manifest_str();
|
||||
}
|
||||
out += "\n\t\t}";
|
||||
}
|
||||
if (device_extensions.size() > 0) {
|
||||
out += ",\n\t\t{";
|
||||
for (size_t i = 0; i < device_extensions.size(); i++) {
|
||||
if (i > 0) out += ",";
|
||||
out += "\n\t\t\t" + device_extensions.at(i).get_manifest_str();
|
||||
}
|
||||
out += "\n\t\t}";
|
||||
}
|
||||
if (enable_environment.size() > 0) {
|
||||
out += ",\n\t\t{ \"" + enable_environment + "\": \"1\"";
|
||||
out += "\n\t\t}";
|
||||
}
|
||||
if (disable_environment.size() > 0) {
|
||||
out += ",\n\t\t{ \"" + disable_environment + "\": \"1\"";
|
||||
out += "\n\t\t}";
|
||||
}
|
||||
if (component_layers.size() > 0) {
|
||||
out += ",\n\t\t\"component_layers\": [";
|
||||
for (size_t i = 0; i < component_layers.size(); i++) {
|
||||
if (i > 0) out += ", ";
|
||||
out += "\"" + component_layers.at(i) + "\"";
|
||||
}
|
||||
out += "]\n\t\t}";
|
||||
}
|
||||
if (pre_instance_functions.size() > 0) {
|
||||
out += ",\n\t\t\"pre_instance_functions\": [";
|
||||
for (size_t i = 0; i < pre_instance_functions.size(); i++) {
|
||||
if (i > 0) out += ", ";
|
||||
out += "\"" + pre_instance_functions.at(i) + "\"";
|
||||
}
|
||||
out += "]\n\t\t}";
|
||||
}
|
||||
out += "\n\t}";
|
||||
return out;
|
||||
}
|
||||
|
||||
VkLayerProperties ManifestLayer::LayerDescription::get_layer_properties() const {
|
||||
VkLayerProperties properties{};
|
||||
strncpy(properties.layerName, name.c_str(), 256);
|
||||
strncpy(properties.description, description.c_str(), 256);
|
||||
properties.implementationVersion = implementation_version;
|
||||
properties.specVersion = api_version;
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string ManifestLayer::get_manifest_str() const {
|
||||
std::string out;
|
||||
out += "{\n";
|
||||
out += "\t\"file_format_version\": \"1.0.0\",\n";
|
||||
if (layers.size() == 1) {
|
||||
out += "\t\"layer\": ";
|
||||
out += layers.at(0).get_manifest_str() + "\n";
|
||||
} else {
|
||||
out += "\"\tlayers\": [";
|
||||
for (size_t i = 0; i < layers.size(); i++) {
|
||||
if (i > 0) out += ",";
|
||||
out += "\n" + layers.at(0).get_manifest_str();
|
||||
}
|
||||
out += "\n]";
|
||||
}
|
||||
out += "}\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace fs {
|
||||
std::string make_native(std::string const& in_path) {
|
||||
std::string out;
|
||||
#if defined(WIN32)
|
||||
for (auto& c : in_path) {
|
||||
if (c == '/')
|
||||
out += "\\";
|
||||
else
|
||||
out += c;
|
||||
}
|
||||
#elif defined(__linux__) || defined(APPLE)
|
||||
for (size_t i = 0; i < in_path.size(); i++) {
|
||||
if (i + 1 < in_path.size() && in_path[i] == '\\' && in_path[i + 1] == '\\') {
|
||||
out += '/';
|
||||
i++;
|
||||
} else
|
||||
out += in_path[i];
|
||||
}
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
// Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings
|
||||
std::string fixup_backslashes_in_path(std::string const& in_path) {
|
||||
std::string out;
|
||||
for (auto& c : in_path) {
|
||||
if (c == '\\')
|
||||
out += "\\\\";
|
||||
else
|
||||
out += c;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
path& path::operator+=(path const& in) {
|
||||
contents += in.contents;
|
||||
return *this;
|
||||
}
|
||||
path& path::operator+=(std::string const& in) {
|
||||
contents += in;
|
||||
return *this;
|
||||
}
|
||||
path& path::operator+=(const char* in) {
|
||||
contents += std::string{in};
|
||||
return *this;
|
||||
}
|
||||
path& path::operator/=(path const& in) {
|
||||
if (contents.back() != path_separator && in.contents.front() != path_separator) contents += path_separator;
|
||||
contents += in.contents;
|
||||
return *this;
|
||||
}
|
||||
path& path::operator/=(std::string const& in) {
|
||||
if (contents.back() != path_separator && in.front() != path_separator) contents += path_separator;
|
||||
contents += in;
|
||||
return *this;
|
||||
}
|
||||
path& path::operator/=(const char* in) {
|
||||
std::string in_str{in};
|
||||
if (contents.back() != path_separator && in_str.front() != path_separator) contents += path_separator;
|
||||
contents += in_str;
|
||||
return *this;
|
||||
}
|
||||
path path::operator+(path const& in) const {
|
||||
path new_path = contents;
|
||||
new_path += in;
|
||||
return new_path;
|
||||
}
|
||||
path path::operator+(std::string const& in) const {
|
||||
path new_path = contents;
|
||||
new_path += in;
|
||||
return new_path;
|
||||
}
|
||||
path path::operator+(const char* in) const {
|
||||
path new_path(contents);
|
||||
new_path += in;
|
||||
return new_path;
|
||||
}
|
||||
|
||||
path path::operator/(path const& in) const {
|
||||
path new_path = contents;
|
||||
new_path /= in;
|
||||
return new_path;
|
||||
}
|
||||
path path::operator/(std::string const& in) const {
|
||||
path new_path = contents;
|
||||
new_path /= in;
|
||||
return new_path;
|
||||
}
|
||||
path path::operator/(const char* in) const {
|
||||
path new_path(contents);
|
||||
new_path /= in;
|
||||
return new_path;
|
||||
}
|
||||
|
||||
path path::parent_path() const {
|
||||
auto last_div = contents.rfind(path_separator);
|
||||
if (last_div == std::string::npos) return "";
|
||||
return path(contents.substr(0, last_div));
|
||||
}
|
||||
bool path::has_parent_path() const {
|
||||
auto last_div = contents.rfind(path_separator);
|
||||
return last_div != std::string::npos;
|
||||
}
|
||||
path path::filename() const {
|
||||
auto last_div = contents.rfind(path_separator);
|
||||
return path(contents.substr(last_div + 1, contents.size() - last_div + 1));
|
||||
}
|
||||
|
||||
path path::extension() const {
|
||||
auto last_div = contents.rfind(path_separator);
|
||||
auto ext_div = contents.rfind('.');
|
||||
// Make sure to not get the special `.` and `..`, as well as any filename that being with a dot, like .profile
|
||||
if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) return path("");
|
||||
path temp = path(contents.substr(ext_div, contents.size() - ext_div + 1));
|
||||
|
||||
return path(contents.substr(ext_div, contents.size() - ext_div + 1));
|
||||
}
|
||||
|
||||
path path::stem() const {
|
||||
auto last_div = contents.rfind(path_separator);
|
||||
auto ext_div = contents.rfind('.');
|
||||
if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) {
|
||||
return path(contents.substr(last_div + 1, contents.size() - last_div + 1));
|
||||
}
|
||||
return path(contents.substr(last_div + 1, ext_div - last_div - 1));
|
||||
}
|
||||
|
||||
path& path::replace_filename(path const& replacement) {
|
||||
*this = parent_path() / replacement.str();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// internal implementation helper for per-platform creating & destroying folders
|
||||
int create_folder(path const& path) {
|
||||
#if defined(WIN32)
|
||||
return _mkdir(path.c_str());
|
||||
#else
|
||||
mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int delete_folder(path const& folder) {
|
||||
#if defined(WIN32)
|
||||
// TODO
|
||||
return 0;
|
||||
#else
|
||||
DIR* dir = opendir(folder.c_str());
|
||||
if (!dir) {
|
||||
return 0;
|
||||
}
|
||||
int ret = 0;
|
||||
dirent* file;
|
||||
while (!ret && (file = readdir(dir))) {
|
||||
int ret2 = -1;
|
||||
|
||||
/* Skip the names "." and ".." as we don't want to recurse on them. */
|
||||
if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
|
||||
|
||||
path file_path = folder / file->d_name;
|
||||
struct stat statbuf;
|
||||
if (!stat(file_path.c_str(), &statbuf)) {
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
ret2 = delete_folder(file_path);
|
||||
else
|
||||
ret2 = unlink(file_path.c_str());
|
||||
}
|
||||
|
||||
ret = ret2;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (!ret) ret = rmdir(folder.c_str());
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
FolderManager::FolderManager(path root_path, std::string name, DebugMode debug) : debug(debug), folder(root_path / name) {
|
||||
create_folder(folder);
|
||||
}
|
||||
FolderManager::~FolderManager() {
|
||||
for (auto& file : files) {
|
||||
if (debug >= DebugMode::log) std::cout << "Removing manifest " << file << " at " << (folder + file).str() << "\n";
|
||||
if (debug != DebugMode::no_delete) {
|
||||
std::remove((folder + file).c_str());
|
||||
}
|
||||
}
|
||||
if (debug != DebugMode::no_delete) {
|
||||
delete_folder(folder);
|
||||
}
|
||||
if (debug >= DebugMode::log) {
|
||||
std::cout << "Deleting folder " << folder.str() << "\n";
|
||||
}
|
||||
}
|
||||
path FolderManager::write(std::string const& name, ManifestICD const& icd_manifest) {
|
||||
path out_path = folder / name;
|
||||
auto found = std::find(files.begin(), files.end(), name);
|
||||
if (found != files.end()) {
|
||||
if (debug >= DebugMode::log) std::cout << "Writing icd manifest to " << name << "\n";
|
||||
} else {
|
||||
if (debug >= DebugMode::log) std::cout << "Creating icd manifest " << name << " at " << out_path.str() << "\n";
|
||||
files.emplace_back(name);
|
||||
}
|
||||
auto file = std::ofstream(out_path.str());
|
||||
file << icd_manifest.get_manifest_str() << std::endl;
|
||||
return out_path;
|
||||
}
|
||||
path FolderManager::write(std::string const& name, ManifestLayer const& layer_manifest) {
|
||||
path out_path = folder / name;
|
||||
auto found = std::find(files.begin(), files.end(), name);
|
||||
if (found != files.end()) {
|
||||
if (debug >= DebugMode::log) std::cout << "Writing layer manifest to " << name << "\n";
|
||||
} else {
|
||||
if (debug >= DebugMode::log) std::cout << "Creating layer manifest " << name << " at " << out_path.str() << "\n";
|
||||
files.emplace_back(name);
|
||||
}
|
||||
auto file = std::ofstream(out_path.str());
|
||||
file << layer_manifest.get_manifest_str() << std::endl;
|
||||
return out_path;
|
||||
}
|
||||
// close file handle, delete file, remove `name` from managed file list.
|
||||
void FolderManager::remove(std::string const& name) {
|
||||
auto found = std::find(files.begin(), files.end(), name);
|
||||
if (found != files.end()) {
|
||||
if (debug >= DebugMode::log) std::cout << "Removing manifest " << name << " at " << (folder + name).str() << "\n";
|
||||
if (debug != DebugMode::no_delete) {
|
||||
std::remove((folder + name).c_str());
|
||||
files.erase(found);
|
||||
}
|
||||
} else {
|
||||
if (debug >= DebugMode::log) std::cout << "Couldn't remove manifest " << name << " at " << (folder + name).str() << ".\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
||||
VulkanFunctions::VulkanFunctions() : loader(FRAMEWORK_VULKAN_LIBRARY_PATH) {
|
||||
// clang-format off
|
||||
fp_vkGetInstanceProcAddr = loader.get_symbol<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
fp_vkEnumerateInstanceExtensionProperties = loader.get_symbol<PFN_vkEnumerateInstanceExtensionProperties>("vkEnumerateInstanceExtensionProperties");
|
||||
fp_vkEnumerateInstanceLayerProperties = loader.get_symbol<PFN_vkEnumerateInstanceLayerProperties>("vkEnumerateInstanceLayerProperties");
|
||||
fp_vkEnumerateInstanceVersion = loader.get_symbol<PFN_vkEnumerateInstanceVersion>("vkEnumerateInstanceVersion");
|
||||
fp_vkCreateInstance = loader.get_symbol<PFN_vkCreateInstance>("vkCreateInstance");
|
||||
fp_vkDestroyInstance = loader.get_symbol<PFN_vkDestroyInstance>("vkDestroyInstance");
|
||||
fp_vkEnumeratePhysicalDevices = loader.get_symbol<PFN_vkEnumeratePhysicalDevices>("vkEnumeratePhysicalDevices");
|
||||
fp_vkGetPhysicalDeviceFeatures = loader.get_symbol<PFN_vkGetPhysicalDeviceFeatures>("vkGetPhysicalDeviceFeatures");
|
||||
fp_vkGetPhysicalDeviceFeatures2 = loader.get_symbol<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2");
|
||||
fp_vkGetPhysicalDeviceFormatProperties = loader.get_symbol<PFN_vkGetPhysicalDeviceFormatProperties>("vkGetPhysicalDeviceFormatProperties");
|
||||
fp_vkGetPhysicalDeviceImageFormatProperties = loader.get_symbol<PFN_vkGetPhysicalDeviceImageFormatProperties>("vkGetPhysicalDeviceImageFormatProperties");
|
||||
fp_vkGetPhysicalDeviceProperties = loader.get_symbol<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties");
|
||||
fp_vkGetPhysicalDeviceProperties2 = loader.get_symbol<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2");
|
||||
fp_vkGetPhysicalDeviceQueueFamilyProperties = loader.get_symbol<PFN_vkGetPhysicalDeviceQueueFamilyProperties>("vkGetPhysicalDeviceQueueFamilyProperties");
|
||||
fp_vkGetPhysicalDeviceQueueFamilyProperties2 = loader.get_symbol<PFN_vkGetPhysicalDeviceQueueFamilyProperties2>("vkGetPhysicalDeviceQueueFamilyProperties2");
|
||||
fp_vkGetPhysicalDeviceMemoryProperties = loader.get_symbol<PFN_vkGetPhysicalDeviceMemoryProperties>("vkGetPhysicalDeviceMemoryProperties");
|
||||
fp_vkGetPhysicalDeviceFormatProperties2 = loader.get_symbol<PFN_vkGetPhysicalDeviceFormatProperties2>("vkGetPhysicalDeviceFormatProperties2");
|
||||
fp_vkGetPhysicalDeviceMemoryProperties2 = loader.get_symbol<PFN_vkGetPhysicalDeviceMemoryProperties2>("vkGetPhysicalDeviceMemoryProperties2");
|
||||
fp_vkGetPhysicalDeviceSurfaceSupportKHR = loader.get_symbol<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>("vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||
fp_vkGetPhysicalDeviceSurfaceFormatsKHR = loader.get_symbol<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>("vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||
fp_vkGetPhysicalDeviceSurfacePresentModesKHR = loader.get_symbol<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>("vkGetPhysicalDeviceSurfacePresentModesKHR");
|
||||
fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = loader.get_symbol<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||
fp_vkEnumerateDeviceExtensionProperties = loader.get_symbol<PFN_vkEnumerateDeviceExtensionProperties>("vkEnumerateDeviceExtensionProperties");
|
||||
fp_vkEnumerateDeviceLayerProperties = loader.get_symbol<PFN_vkEnumerateDeviceLayerProperties>("vkEnumerateDeviceLayerProperties");
|
||||
|
||||
fp_vkDestroySurfaceKHR = loader.get_symbol<PFN_vkDestroySurfaceKHR>("vkDestroySurfaceKHR");
|
||||
fp_vkGetDeviceProcAddr = loader.get_symbol<PFN_vkGetDeviceProcAddr>("vkGetDeviceProcAddr");
|
||||
fp_vkCreateDevice = loader.get_symbol<PFN_vkCreateDevice>("vkCreateDevice");
|
||||
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
fp_vkCreateAndroidSurfaceKHR = loader.get_symbol<PFN_vkCreateAndroidSurfaceKHR>("vkCreateAndroidSurfaceKHR");
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
fp_vkCreateMetalSurfaceEXT = loader.get_symbol<PFN_vkCreateMetalSurfaceEXT>("vkCreateMetalSurfaceEXT")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
fp_vkCreateWaylandSurfaceKHR = loader.get_symbol<PFN_vkCreateWaylandSurfaceKHR>("vkCreateWaylandSurfaceKHR");
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
fp_vkCreateXcbSurfaceKHR = loader.get_symbol<PFN_vkCreateXcbSurfaceKHR>("vkCreateXcbSurfaceKHR");
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
fp_vkCreateXlibSurfaceKHR = loader.get_symbol<PFN_vkCreateXlibSurfaceKHR>("vkCreateXlibSurfaceKHR");
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
fp_vkCreateWin32SurfaceKHR = loader.get_symbol<PFN_vkCreateWin32SurfaceKHR>("vkCreateWin32SurfaceKHR");
|
||||
#endif
|
||||
fp_vkDestroyDevice = loader.get_symbol<PFN_vkDestroyDevice>("vkDestroyDevice");
|
||||
fp_vkGetDeviceQueue = loader.get_symbol<PFN_vkGetDeviceQueue>("vkGetDeviceQueue");
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
InstanceCreateInfo::InstanceCreateInfo() {
|
||||
inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
}
|
||||
|
||||
VkInstanceCreateInfo* InstanceCreateInfo::get() noexcept {
|
||||
app_info.pApplicationName = app_name.c_str();
|
||||
app_info.pEngineName = engine_name.c_str();
|
||||
app_info.applicationVersion = app_version;
|
||||
app_info.engineVersion = engine_version;
|
||||
app_info.apiVersion = api_version;
|
||||
inst_info.pApplicationInfo = &app_info;
|
||||
inst_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
|
||||
inst_info.ppEnabledLayerNames = enabled_layers.data();
|
||||
inst_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
inst_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
return &inst_info;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::set_application_name(std::string app_name) {
|
||||
this->app_name = app_name;
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::set_engine_name(std::string engine_name) {
|
||||
this->engine_name = engine_name;
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::set_app_version(uint32_t app_version) {
|
||||
this->app_version = app_version;
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::set_engine_version(uint32_t engine_version) {
|
||||
this->engine_version = engine_version;
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::set_api_version(uint32_t api_version) {
|
||||
this->api_version = api_version;
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::add_layer(const char* layer_name) {
|
||||
enabled_layers.push_back(layer_name);
|
||||
return *this;
|
||||
}
|
||||
InstanceCreateInfo& InstanceCreateInfo::add_extension(const char* ext_name) {
|
||||
enabled_extensions.push_back(ext_name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; }
|
||||
DeviceQueueCreateInfo& DeviceQueueCreateInfo::add_priority(float priority) {
|
||||
priorities.push_back(priority);
|
||||
return *this;
|
||||
}
|
||||
DeviceQueueCreateInfo& DeviceQueueCreateInfo::set_props(VkQueueFamilyProperties props) {
|
||||
queue.queueCount = props.queueCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VkDeviceCreateInfo* DeviceCreateInfo::get() noexcept {
|
||||
dev.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
|
||||
dev.ppEnabledLayerNames = enabled_layers.data();
|
||||
dev.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
dev.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
uint32_t index = 0;
|
||||
for (auto& queue : queue_infos) queue.queueFamilyIndex = index++;
|
||||
|
||||
dev.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size());
|
||||
dev.pQueueCreateInfos = queue_infos.data();
|
||||
return &dev;
|
||||
}
|
||||
DeviceCreateInfo& DeviceCreateInfo::add_layer(const char* layer_name) {
|
||||
enabled_layers.push_back(layer_name);
|
||||
|
||||
return *this;
|
||||
}
|
||||
DeviceCreateInfo& DeviceCreateInfo::add_extension(const char* ext_name) {
|
||||
enabled_extensions.push_back(ext_name);
|
||||
|
||||
return *this;
|
||||
}
|
||||
DeviceCreateInfo& DeviceCreateInfo::add_device_queue(DeviceQueueCreateInfo queue_info_detail) {
|
||||
queue_info_details.push_back(queue_info_detail);
|
||||
return *this;
|
||||
}
|
||||
|
||||
VkResult CreateInst(InstWrapper& inst, InstanceCreateInfo& inst_info) {
|
||||
return inst.functions->fp_vkCreateInstance(inst_info.get(), nullptr, &inst.inst);
|
||||
}
|
||||
|
||||
VkResult CreatePhysDevs(InstWrapper& inst, uint32_t phys_dev_count, std::vector<VkPhysicalDevice>& physical_devices) {
|
||||
physical_devices.resize(phys_dev_count);
|
||||
uint32_t physical_count = phys_dev_count;
|
||||
return inst.functions->fp_vkEnumeratePhysicalDevices(inst.inst, &physical_count, physical_devices.data());
|
||||
}
|
||||
|
||||
VkResult CreatePhysDev(InstWrapper& inst, VkPhysicalDevice& physical_device) {
|
||||
uint32_t physical_count = 1;
|
||||
return inst.functions->fp_vkEnumeratePhysicalDevices(inst.inst, &physical_count, &physical_device);
|
||||
}
|
656
tests/framework/test_util.h
Normal file
656
tests/framework/test_util.h
Normal file
@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The Khronos Group Inc.
|
||||
* Copyright (c) 2021 Valve Corporation
|
||||
* Copyright (c) 2021 LunarG, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and/or associated documentation files (the "Materials"), to
|
||||
* deal in the Materials without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Materials, and to permit persons to whom the Materials are
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice(s) and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Materials.
|
||||
*
|
||||
* THE MATERIALS ARE 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 MATERIALS OR THE
|
||||
* USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*
|
||||
* Author: Charles Giessen <charles@lunarg.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Contains all the utilities needed to make the framework and tests work.
|
||||
* Contains:
|
||||
* All the standard library includes and main platform specific includes
|
||||
* Dll export macro
|
||||
* Manifest ICD & Layer structs
|
||||
* path abstraction class - modelled after C++17's filesystem::path
|
||||
* FolderManager - manages the contents of a folder, cleaning up when needed
|
||||
* per-platform library loading - mirrors the vk_loader_platform
|
||||
* LibraryWrapper - RAII wrapper for a library
|
||||
* DispatchableHandle - RAII wrapper for vulkan dispatchable handle objects
|
||||
* ostream overload for VkResult - prettifies googletest output
|
||||
* VulkanFunctions - loads vulkan functions for tests to use
|
||||
* Instance & Device create info helpers
|
||||
* InstWrapper & DeviceWrapper - for easier test writing
|
||||
* operator == overloads for many vulkan structs - more concise tests
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Following items are needed for C++ to work with PRIxLEAST64
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vk_icd.h>
|
||||
|
||||
#include "framework_config.h"
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define FRAMEWORK_EXPORT __attribute__((visibility("default")))
|
||||
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
|
||||
#define FRAMEWORK_EXPORT __attribute__((visibility("default")))
|
||||
#elif defined(WIN32)
|
||||
#define FRAMEWORK_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FRAMEWORK_EXPORT
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
bool set_env_var(std::string const& name, std::string const& value);
|
||||
bool remove_env_var(std::string const& name);
|
||||
#define ENV_VAR_BUFFER_SIZE 4096
|
||||
std::string get_env_var(std::string const& name);
|
||||
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
bool set_env_var(std::string const& name, std::string const& value);
|
||||
bool remove_env_var(std::string const& name);
|
||||
std::string get_env_var(std::string const& name);
|
||||
#endif
|
||||
|
||||
enum class DebugMode {
|
||||
none,
|
||||
log, // log all folder and file creation & deletion
|
||||
no_delete // Will not delete create folders & files, but will report 'deleting them' to show when something *should* of been
|
||||
// deleted
|
||||
};
|
||||
|
||||
inline std::string version_to_string(uint32_t version) {
|
||||
return std::to_string(VK_VERSION_MAJOR(version)) + "." + std::to_string(VK_VERSION_MINOR(version)) + "." +
|
||||
std::to_string(VK_VERSION_PATCH(version));
|
||||
}
|
||||
|
||||
struct ManifestICD {
|
||||
uint32_t api_version = VK_MAKE_VERSION(1, 0, 0);
|
||||
std::string lib_path;
|
||||
|
||||
std::string get_manifest_str() const;
|
||||
};
|
||||
|
||||
struct ManifestLayer {
|
||||
struct LayerDescription {
|
||||
enum class Type { INSTANCE, GLOBAL, DEVICE };
|
||||
std::string get_type_str(Type type) const {
|
||||
if (type == Type::GLOBAL)
|
||||
return "GLOBAL";
|
||||
else if (type == Type::DEVICE)
|
||||
return "DEVICE";
|
||||
else // default
|
||||
return "INSTANCE";
|
||||
}
|
||||
struct FunctionOverride {
|
||||
std::string vk_func;
|
||||
std::string override_name;
|
||||
std::string get_manifest_str() const { return std::string("{ \"") + vk_func + "\":\"" + override_name + "\" }"; }
|
||||
};
|
||||
struct Extension {
|
||||
std::string name;
|
||||
uint32_t spec_version = 0;
|
||||
std::vector<std::string> entrypoints;
|
||||
std::string get_manifest_str() const;
|
||||
};
|
||||
std::string name;
|
||||
Type type = Type::INSTANCE;
|
||||
std::string lib_path;
|
||||
uint32_t api_version = VK_MAKE_VERSION(1, 0, 0);
|
||||
uint32_t implementation_version = 0;
|
||||
std::string description;
|
||||
std::vector<FunctionOverride> functions;
|
||||
std::vector<Extension> instance_extensions;
|
||||
std::vector<Extension> device_extensions;
|
||||
std::string enable_environment;
|
||||
std::string disable_environment;
|
||||
std::vector<std::string> component_layers;
|
||||
std::vector<std::string> pre_instance_functions;
|
||||
|
||||
std::string get_manifest_str() const;
|
||||
VkLayerProperties get_layer_properties() const;
|
||||
};
|
||||
uint32_t file_format_major = 1;
|
||||
uint32_t file_format_minor = 1;
|
||||
uint32_t file_format_patch = 2;
|
||||
std::vector<LayerDescription> layers;
|
||||
|
||||
std::string get_manifest_str() const;
|
||||
};
|
||||
|
||||
struct Extension {
|
||||
std::string extensionName;
|
||||
uint32_t specVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
|
||||
Extension(std::string extensionName, uint32_t specVersion = VK_MAKE_VERSION(1, 0, 0))
|
||||
: extensionName(extensionName), specVersion(specVersion) {}
|
||||
|
||||
VkExtensionProperties get() const noexcept {
|
||||
VkExtensionProperties props{};
|
||||
std::strncpy(props.extensionName, extensionName.c_str(), VK_MAX_EXTENSION_NAME_SIZE);
|
||||
props.specVersion = specVersion;
|
||||
return props;
|
||||
}
|
||||
};
|
||||
|
||||
struct MockQueueFamilyProperties {
|
||||
MockQueueFamilyProperties(VkQueueFamilyProperties properties, bool support_present = false)
|
||||
: properties(properties), support_present(support_present) {}
|
||||
VkQueueFamilyProperties properties{};
|
||||
bool support_present = false;
|
||||
VkQueueFamilyProperties get() const noexcept { return properties; }
|
||||
};
|
||||
|
||||
namespace fs {
|
||||
std::string make_native(std::string const&);
|
||||
std::string fixup_backslashes_in_path(std::string const& in_path);
|
||||
struct path {
|
||||
private:
|
||||
#if defined(WIN32)
|
||||
static const char path_separator = '\\';
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
static const char path_separator = '/';
|
||||
#endif
|
||||
|
||||
public:
|
||||
path() {}
|
||||
path(std::string const& in) : contents(make_native(in)) {}
|
||||
path(const char* in) : contents(make_native(std::string(in))) {}
|
||||
|
||||
// concat paths without directoryseperator
|
||||
path& operator+=(path const& in);
|
||||
path& operator+=(std::string const& in);
|
||||
path& operator+=(const char* in);
|
||||
|
||||
// append paths with directoryseperator
|
||||
path& operator/=(path const& in);
|
||||
path& operator/=(std::string const& in);
|
||||
path& operator/=(const char* in);
|
||||
|
||||
// concat paths without directory seperator
|
||||
path operator+(path const& in) const;
|
||||
path operator+(std::string const& in) const;
|
||||
path operator+(const char* in) const;
|
||||
|
||||
// append paths with directory seperator
|
||||
path operator/(path const& in) const;
|
||||
path operator/(std::string const& in) const;
|
||||
path operator/(const char* in) const;
|
||||
|
||||
// accesors
|
||||
path parent_path() const;
|
||||
bool has_parent_path() const;
|
||||
path filename() const;
|
||||
path extension() const;
|
||||
path stem() const;
|
||||
|
||||
// modifiers
|
||||
path& replace_filename(path const& replacement);
|
||||
|
||||
// get c style string
|
||||
const char* c_str() const { return contents.c_str(); }
|
||||
// get C++ style string
|
||||
std::string const& str() const { return contents; }
|
||||
std::string& str() { return contents; }
|
||||
size_t size() const { return contents.size();};
|
||||
|
||||
private:
|
||||
std::string contents;
|
||||
};
|
||||
|
||||
class FolderManager {
|
||||
public:
|
||||
explicit FolderManager(path root_path, std::string name, DebugMode debug = DebugMode::none);
|
||||
~FolderManager();
|
||||
path write(std::string const& name, ManifestICD const& icd_manifest);
|
||||
path write(std::string const& name, ManifestLayer const& layer_manifest);
|
||||
|
||||
// close file handle, delete file, remove `name` from managed file list.
|
||||
void remove(std::string const& name);
|
||||
|
||||
// location of the managed folder
|
||||
path location() const { return folder; }
|
||||
|
||||
FolderManager(FolderManager const&) = delete;
|
||||
FolderManager& operator=(FolderManager const&) = delete;
|
||||
|
||||
private:
|
||||
DebugMode debug;
|
||||
path folder;
|
||||
std::vector<std::string> files;
|
||||
};
|
||||
|
||||
inline void copy_file(path const& origin, path const& destination) {
|
||||
std::ifstream src(origin.str(), std::ios::binary);
|
||||
std::ofstream dst(destination.str(), std::ios::binary);
|
||||
dst << src.rdbuf();
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef HMODULE loader_platform_dl_handle;
|
||||
static loader_platform_dl_handle loader_platform_open_library(const char* lib_path) {
|
||||
// Try loading the library the original way first.
|
||||
loader_platform_dl_handle lib_handle = LoadLibrary(lib_path);
|
||||
if (lib_handle == NULL && GetLastError() == ERROR_MOD_NOT_FOUND) {
|
||||
// If that failed, then try loading it with broader search folders.
|
||||
lib_handle = LoadLibraryEx(lib_path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
|
||||
}
|
||||
return lib_handle;
|
||||
}
|
||||
static char* loader_platform_open_library_error(const char* libPath) {
|
||||
static char errorMsg[164];
|
||||
(void)snprintf(errorMsg, 163, "Failed to open dynamic library \"%s\" with error %lu", libPath, GetLastError());
|
||||
return errorMsg;
|
||||
}
|
||||
static void loader_platform_close_library(loader_platform_dl_handle library) { FreeLibrary(library); }
|
||||
static void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
|
||||
assert(library);
|
||||
assert(name);
|
||||
return (void*)GetProcAddress(library, name);
|
||||
}
|
||||
static char* loader_platform_get_proc_address_error(const char* name) {
|
||||
static char errorMsg[120];
|
||||
(void)snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name);
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
typedef void* loader_platform_dl_handle;
|
||||
static inline loader_platform_dl_handle loader_platform_open_library(const char* libPath) {
|
||||
return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL);
|
||||
}
|
||||
static inline const char* loader_platform_open_library_error(const char* libPath) { return dlerror(); }
|
||||
static inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); }
|
||||
static inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
|
||||
assert(library);
|
||||
assert(name);
|
||||
return dlsym(library, name);
|
||||
}
|
||||
static inline const char* loader_platform_get_proc_address_error(const char* name) { return dlerror(); }
|
||||
#endif
|
||||
|
||||
struct LibraryWrapper {
|
||||
explicit LibraryWrapper() noexcept {}
|
||||
explicit LibraryWrapper(fs::path const& lib_path) noexcept : lib_path(lib_path) {
|
||||
lib_handle = loader_platform_open_library(lib_path.c_str());
|
||||
if (lib_handle == NULL) {
|
||||
fprintf(stderr, "Unable to open library %s: %s\n", lib_path.c_str(),
|
||||
loader_platform_open_library_error(lib_path.c_str()));
|
||||
assert(lib_handle != NULL && "Must be able to open library");
|
||||
}
|
||||
}
|
||||
~LibraryWrapper() noexcept {
|
||||
if (lib_handle != NULL) {
|
||||
loader_platform_close_library(lib_handle);
|
||||
lib_handle = nullptr;
|
||||
}
|
||||
}
|
||||
LibraryWrapper(LibraryWrapper const& wrapper) = delete;
|
||||
LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete;
|
||||
LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) { wrapper.lib_handle = nullptr; }
|
||||
LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept {
|
||||
if (this != &wrapper) {
|
||||
if (lib_handle != nullptr) {
|
||||
loader_platform_close_library(lib_handle);
|
||||
}
|
||||
lib_handle = wrapper.lib_handle;
|
||||
lib_path = wrapper.lib_path;
|
||||
wrapper.lib_handle = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
T get_symbol(const char* symbol_name) const {
|
||||
assert(lib_handle != NULL && "Cannot get symbol with null library handle");
|
||||
T symbol = reinterpret_cast<T>(loader_platform_get_proc_address(lib_handle, symbol_name));
|
||||
if (symbol == NULL) {
|
||||
fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, loader_platform_get_proc_address_error(symbol_name));
|
||||
assert(symbol != NULL && "Must be able to get symbol");
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return lib_handle != nullptr; }
|
||||
|
||||
loader_platform_dl_handle lib_handle = nullptr;
|
||||
fs::path lib_path;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FRAMEWORK_EXPORT DispatchableHandle {
|
||||
DispatchableHandle() {
|
||||
auto ptr_handle = new VK_LOADER_DATA;
|
||||
set_loader_magic_value(ptr_handle);
|
||||
handle = reinterpret_cast<T>(ptr_handle);
|
||||
}
|
||||
~DispatchableHandle() {
|
||||
delete reinterpret_cast<VK_LOADER_DATA*>(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
DispatchableHandle(DispatchableHandle const&) = delete;
|
||||
DispatchableHandle& operator=(DispatchableHandle const&) = delete;
|
||||
DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
|
||||
DispatchableHandle& operator=(DispatchableHandle&& other) noexcept {
|
||||
if (this != &other) {
|
||||
delete reinterpret_cast<VK_LOADER_DATA*>(handle);
|
||||
handle = other.handle;
|
||||
other.handle = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
bool operator==(T base_handle) { return base_handle == handle; }
|
||||
bool operator!=(T base_handle) { return base_handle != handle; }
|
||||
|
||||
T handle = nullptr;
|
||||
};
|
||||
|
||||
// Stream operator for VkResult so GTEST will print out error codes as strings (automatically)
|
||||
inline std::ostream& operator<<(std::ostream& os, const VkResult& result) {
|
||||
switch (result) {
|
||||
case (VK_SUCCESS):
|
||||
return os << "VK_SUCCESS";
|
||||
case (VK_NOT_READY):
|
||||
return os << "VK_NOT_READY";
|
||||
case (VK_TIMEOUT):
|
||||
return os << "VK_TIMEOUT";
|
||||
case (VK_EVENT_SET):
|
||||
return os << "VK_EVENT_SET";
|
||||
case (VK_EVENT_RESET):
|
||||
return os << "VK_EVENT_RESET";
|
||||
case (VK_INCOMPLETE):
|
||||
return os << "VK_INCOMPLETE";
|
||||
case (VK_ERROR_OUT_OF_HOST_MEMORY):
|
||||
return os << "VK_ERROR_OUT_OF_HOST_MEMORY";
|
||||
case (VK_ERROR_OUT_OF_DEVICE_MEMORY):
|
||||
return os << "VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
||||
case (VK_ERROR_INITIALIZATION_FAILED):
|
||||
return os << "VK_ERROR_INITIALIZATION_FAILED";
|
||||
case (VK_ERROR_DEVICE_LOST):
|
||||
return os << "VK_ERROR_DEVICE_LOST";
|
||||
case (VK_ERROR_MEMORY_MAP_FAILED):
|
||||
return os << "VK_ERROR_MEMORY_MAP_FAILED";
|
||||
case (VK_ERROR_LAYER_NOT_PRESENT):
|
||||
return os << "VK_ERROR_LAYER_NOT_PRESENT";
|
||||
case (VK_ERROR_EXTENSION_NOT_PRESENT):
|
||||
return os << "VK_ERROR_EXTENSION_NOT_PRESENT";
|
||||
case (VK_ERROR_FEATURE_NOT_PRESENT):
|
||||
return os << "VK_ERROR_FEATURE_NOT_PRESENT";
|
||||
case (VK_ERROR_INCOMPATIBLE_DRIVER):
|
||||
return os << "VK_ERROR_INCOMPATIBLE_DRIVER";
|
||||
case (VK_ERROR_TOO_MANY_OBJECTS):
|
||||
return os << "VK_ERROR_TOO_MANY_OBJECTS";
|
||||
case (VK_ERROR_FORMAT_NOT_SUPPORTED):
|
||||
return os << "VK_ERROR_FORMAT_NOT_SUPPORTED";
|
||||
case (VK_ERROR_FRAGMENTED_POOL):
|
||||
return os << "VK_ERROR_FRAGMENTED_POOL";
|
||||
case (VK_ERROR_UNKNOWN):
|
||||
return os << "VK_ERROR_UNKNOWN";
|
||||
case (VK_ERROR_OUT_OF_POOL_MEMORY):
|
||||
return os << "VK_ERROR_OUT_OF_POOL_MEMORY";
|
||||
case (VK_ERROR_INVALID_EXTERNAL_HANDLE):
|
||||
return os << "VK_ERROR_INVALID_EXTERNAL_HANDLE";
|
||||
case (VK_ERROR_FRAGMENTATION):
|
||||
return os << "VK_ERROR_FRAGMENTATION";
|
||||
case (VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS):
|
||||
return os << "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
|
||||
case (VK_ERROR_SURFACE_LOST_KHR):
|
||||
return os << "VK_ERROR_SURFACE_LOST_KHR";
|
||||
case (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR):
|
||||
return os << "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
|
||||
case (VK_SUBOPTIMAL_KHR):
|
||||
return os << "VK_SUBOPTIMAL_KHR";
|
||||
case (VK_ERROR_OUT_OF_DATE_KHR):
|
||||
return os << "VK_ERROR_OUT_OF_DATE_KHR";
|
||||
case (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR):
|
||||
return os << "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
|
||||
case (VK_ERROR_VALIDATION_FAILED_EXT):
|
||||
return os << "VK_ERROR_VALIDATION_FAILED_EXT";
|
||||
case (VK_ERROR_INVALID_SHADER_NV):
|
||||
return os << "VK_ERROR_INVALID_SHADER_NV";
|
||||
case (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT):
|
||||
return os << "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
|
||||
case (VK_ERROR_NOT_PERMITTED_EXT):
|
||||
return os << "VK_ERROR_NOT_PERMITTED_EXT";
|
||||
case (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT):
|
||||
return os << "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
|
||||
case (VK_THREAD_IDLE_KHR):
|
||||
return os << "VK_THREAD_IDLE_KHR";
|
||||
case (VK_THREAD_DONE_KHR):
|
||||
return os << "VK_THREAD_DONE_KHR";
|
||||
case (VK_OPERATION_DEFERRED_KHR):
|
||||
return os << "VK_OPERATION_DEFERRED_KHR";
|
||||
case (VK_OPERATION_NOT_DEFERRED_KHR):
|
||||
return os << "VK_OPERATION_NOT_DEFERRED_KHR";
|
||||
case (VK_PIPELINE_COMPILE_REQUIRED_EXT):
|
||||
return os << "VK_PIPELINE_COMPILE_REQUIRED_EXT";
|
||||
case (VK_RESULT_MAX_ENUM):
|
||||
return os << "VK_RESULT_MAX_ENUM";
|
||||
}
|
||||
return os << static_cast<int32_t>(result);
|
||||
}
|
||||
|
||||
struct VulkanFunctions {
|
||||
LibraryWrapper loader;
|
||||
|
||||
// Pre-Instance
|
||||
PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr;
|
||||
PFN_vkEnumerateInstanceExtensionProperties fp_vkEnumerateInstanceExtensionProperties = nullptr;
|
||||
PFN_vkEnumerateInstanceLayerProperties fp_vkEnumerateInstanceLayerProperties = nullptr;
|
||||
PFN_vkEnumerateInstanceVersion fp_vkEnumerateInstanceVersion = nullptr;
|
||||
PFN_vkCreateInstance fp_vkCreateInstance = nullptr;
|
||||
|
||||
// Instance
|
||||
PFN_vkDestroyInstance fp_vkDestroyInstance = nullptr;
|
||||
PFN_vkEnumeratePhysicalDevices fp_vkEnumeratePhysicalDevices = nullptr;
|
||||
PFN_vkGetPhysicalDeviceFeatures fp_vkGetPhysicalDeviceFeatures = nullptr;
|
||||
PFN_vkGetPhysicalDeviceFeatures2 fp_vkGetPhysicalDeviceFeatures2 = nullptr;
|
||||
PFN_vkGetPhysicalDeviceFormatProperties fp_vkGetPhysicalDeviceFormatProperties = nullptr;
|
||||
PFN_vkGetPhysicalDeviceImageFormatProperties fp_vkGetPhysicalDeviceImageFormatProperties = nullptr;
|
||||
PFN_vkGetPhysicalDeviceProperties fp_vkGetPhysicalDeviceProperties = nullptr;
|
||||
PFN_vkGetPhysicalDeviceProperties2 fp_vkGetPhysicalDeviceProperties2 = nullptr;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties fp_vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fp_vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr;
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties fp_vkGetPhysicalDeviceMemoryProperties = nullptr;
|
||||
PFN_vkGetPhysicalDeviceFormatProperties2 fp_vkGetPhysicalDeviceFormatProperties2 = nullptr;
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties2 fp_vkGetPhysicalDeviceMemoryProperties2 = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fp_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fp_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fp_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fp_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
|
||||
PFN_vkEnumerateDeviceExtensionProperties fp_vkEnumerateDeviceExtensionProperties = nullptr;
|
||||
PFN_vkEnumerateDeviceLayerProperties fp_vkEnumerateDeviceLayerProperties = nullptr;
|
||||
|
||||
PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr;
|
||||
PFN_vkCreateDevice fp_vkCreateDevice = nullptr;
|
||||
|
||||
// WSI
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
PFN_vkCreateAndroidSurfaceKHR fp_vkCreateAndroidSurfaceKHR;
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
PFN_vkCreateMetalSurfaceEXT fp_vkCreateMetalSurfaceEXT;
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
PFN_vkCreateWaylandSurfaceKHR fp_vkCreateWaylandSurfaceKHR;
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
PFN_vkCreateXcbSurfaceKHR fp_vkCreateXcbSurfaceKHR;
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
PFN_vkCreateXlibSurfaceKHR fp_vkCreateXlibSurfaceKHR;
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
PFN_vkCreateWin32SurfaceKHR fp_vkCreateWin32SurfaceKHR;
|
||||
#endif
|
||||
PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = nullptr;
|
||||
|
||||
// device functions
|
||||
PFN_vkDestroyDevice fp_vkDestroyDevice = nullptr;
|
||||
PFN_vkGetDeviceQueue fp_vkGetDeviceQueue = nullptr;
|
||||
|
||||
VulkanFunctions();
|
||||
};
|
||||
|
||||
struct InstanceCreateInfo {
|
||||
VkInstanceCreateInfo inst_info{};
|
||||
VkApplicationInfo app_info{};
|
||||
std::string app_name;
|
||||
std::string engine_name;
|
||||
uint32_t app_version = 0;
|
||||
uint32_t engine_version = 0;
|
||||
uint32_t api_version = VK_MAKE_VERSION(1, 0, 0);
|
||||
std::vector<const char*> enabled_layers;
|
||||
std::vector<const char*> enabled_extensions;
|
||||
|
||||
InstanceCreateInfo();
|
||||
|
||||
VkInstanceCreateInfo* get() noexcept;
|
||||
InstanceCreateInfo& set_application_name(std::string app_name);
|
||||
InstanceCreateInfo& set_engine_name(std::string engine_name);
|
||||
InstanceCreateInfo& set_app_version(uint32_t app_version);
|
||||
InstanceCreateInfo& set_engine_version(uint32_t engine_version);
|
||||
InstanceCreateInfo& set_api_version(uint32_t api_version);
|
||||
InstanceCreateInfo& add_layer(const char* layer_name);
|
||||
InstanceCreateInfo& add_extension(const char* ext_name);
|
||||
};
|
||||
|
||||
struct DeviceQueueCreateInfo {
|
||||
VkDeviceQueueCreateInfo queue{};
|
||||
std::vector<float> priorities;
|
||||
DeviceQueueCreateInfo();
|
||||
DeviceQueueCreateInfo& add_priority(float priority);
|
||||
DeviceQueueCreateInfo& set_props(VkQueueFamilyProperties props);
|
||||
};
|
||||
|
||||
struct DeviceCreateInfo {
|
||||
VkDeviceCreateInfo dev{};
|
||||
std::vector<const char*> enabled_extensions;
|
||||
std::vector<const char*> enabled_layers;
|
||||
std::vector<DeviceQueueCreateInfo> queue_info_details;
|
||||
std::vector<VkDeviceQueueCreateInfo> queue_infos;
|
||||
|
||||
VkDeviceCreateInfo* get() noexcept;
|
||||
DeviceCreateInfo& add_layer(const char* layer_name);
|
||||
DeviceCreateInfo& add_extension(const char* ext_name);
|
||||
DeviceCreateInfo& add_device_queue(DeviceQueueCreateInfo queue_info_detail);
|
||||
};
|
||||
|
||||
struct InstWrapper {
|
||||
InstWrapper(VulkanFunctions& functions) noexcept : functions(&functions) {}
|
||||
InstWrapper(VulkanFunctions& functions, VkInstance inst) noexcept : functions(&functions), inst(inst) {}
|
||||
~InstWrapper() {
|
||||
if (inst != VK_NULL_HANDLE) functions->fp_vkDestroyInstance(inst, nullptr);
|
||||
}
|
||||
|
||||
// Immoveable object
|
||||
InstWrapper(InstWrapper const&) = delete;
|
||||
InstWrapper& operator=(InstWrapper const&) = delete;
|
||||
InstWrapper(InstWrapper&&) = delete;
|
||||
InstWrapper& operator=(InstWrapper&&) = delete;
|
||||
|
||||
// Convenience
|
||||
operator VkInstance() { return inst; }
|
||||
VulkanFunctions* operator->() { return functions; }
|
||||
|
||||
VulkanFunctions* functions = nullptr;
|
||||
VkInstance inst = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
VkResult CreateInst(InstWrapper& inst, InstanceCreateInfo& inst_info);
|
||||
VkResult CreatePhysDevs(InstWrapper& inst, uint32_t phys_dev_count, std::vector<VkPhysicalDevice>& physical_devices);
|
||||
VkResult CreatePhysDev(InstWrapper& inst, VkPhysicalDevice& physical_device);
|
||||
|
||||
struct DeviceWrapper {
|
||||
DeviceWrapper(){};
|
||||
DeviceWrapper(VulkanFunctions& functions, VkDevice dev) : functions(&functions), dev(dev){};
|
||||
~DeviceWrapper() { functions->fp_vkDestroyDevice(dev, nullptr); }
|
||||
|
||||
// Immoveable object
|
||||
DeviceWrapper(DeviceWrapper const&) = delete;
|
||||
DeviceWrapper& operator=(DeviceWrapper const&) = delete;
|
||||
DeviceWrapper(DeviceWrapper&&) = delete;
|
||||
DeviceWrapper& operator=(DeviceWrapper&&) = delete;
|
||||
|
||||
// Convenience
|
||||
operator VkDevice() { return dev; }
|
||||
VulkanFunctions* operator->() { return functions; }
|
||||
|
||||
VulkanFunctions* functions = nullptr;
|
||||
VkDevice dev = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
inline bool operator==(const VkExtent3D& a, const VkExtent3D& b) {
|
||||
return a.width == b.width && a.height == b.height && a.depth == b.depth;
|
||||
}
|
||||
inline bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); }
|
||||
|
||||
inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) {
|
||||
return a.minImageTransferGranularity == b.minImageTransferGranularity && a.queueCount == b.queueCount &&
|
||||
a.queueFlags == b.queueFlags && a.timestampValidBits == b.timestampValidBits;
|
||||
}
|
||||
inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); }
|
||||
|
||||
inline bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) {
|
||||
return strncmp(a.layerName, b.layerName, 256) == 0 && strncmp(a.description, b.description, 256) == 0 &&
|
||||
a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion;
|
||||
}
|
||||
inline bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); }
|
||||
|
||||
inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) {
|
||||
return strncmp(a.extensionName, b.extensionName, 256) == 0 && a.specVersion == b.specVersion;
|
||||
}
|
||||
inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); }
|
Loading…
Reference in New Issue
Block a user