third_party_vulkan-loader/tests/framework/test_util.h
Charles Giessen b53e82543e Expand unknown function handling tests
Add tests for unknown device functions queried with both vkGetDeviceProcAddr
and vkGetInstanceProcAddr. Also loops enough times to exhaust the available
unknown function supply.

Had to fix the vk_layerGetPhysicalDeviceProcAddr in TestLayer to make the tests
pass.

MacOS currently skips tests which use vkGetInstanceProcAddr. This is due to the
need for unknown functions being lower priority and so do not warrent the time
needed to write the assembly necessary to resolve the issue fully.
2022-02-15 10:48:53 -07:00

856 lines
37 KiB
C++

/*
* 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
* 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 <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <memory>
#include <functional>
#include <cassert>
#include <cstring>
#include <ctime>
#include <stdio.h>
#include <stdint.h>
#if defined(WIN32)
#include <direct.h>
#include <windows.h>
#include <strsafe.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
// Prevent macro collisions from <sys/types.h>
#undef major
#undef minor
#endif
#include <vulkan/vulkan.h>
#include <vulkan/vk_icd.h>
#include <vulkan/vk_layer.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
/*
* Common Environment Variable operations
* These operate on the actual environemnt, they are not shims.
* set_env_var - sets the env-var with `name` to `value`.
* remove_env_var - unsets the env-var `name`. Different than set_env_var(name, "");
* get_env_var - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the
* env-var
*/
#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, bool report_failure = true);
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
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, bool report_failure = true);
#endif
// Windows specific error handling logic
#if defined(WIN32)
const long ERROR_SETENV_FAILED = 10543; // chosen at random, attempts to not conflict
const long ERROR_REMOVEDIRECTORY_FAILED = 10544; // chosen at random, attempts to not conflict
const char* win_api_error_str(LSTATUS status);
void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = "");
#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
};
struct ManifestICD; // forward declaration for FolderManager::write
struct ManifestLayer; // forward declaration for FolderManager::write
#ifdef _WIN32
// Environment variable list separator - not for filesystem paths
const char OS_ENV_VAR_LIST_SEPARATOR = ';';
#else
// Environment variable list separator - not for filesystem paths
const char OS_ENV_VAR_LIST_SEPARATOR = ':';
#endif
namespace fs {
std::string make_native(std::string const&);
struct path {
private:
#if defined(WIN32)
static const char path_separator = '\\';
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
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(); };
// equality
bool operator==(path const& other) const noexcept { return contents == other.contents; }
bool operator!=(path const& other) const noexcept { return !(*this == other); }
private:
std::string contents;
};
std::string fixup_backslashes_in_path(std::string const& in_path);
fs::path fixup_backslashes_in_path(fs::path const& in_path);
int create_folder(path const& path);
int delete_folder(path const& folder);
class FolderManager {
public:
explicit FolderManager(path root_path, std::string name, DebugMode debug = DebugMode::none);
~FolderManager();
FolderManager(FolderManager const&) = delete;
FolderManager& operator=(FolderManager const&) = delete;
path write_manifest(std::string const& name, std::string const& contents);
// close file handle, delete file, remove `name` from managed file list.
void remove(std::string const& name);
// copy file into this folder with name `new_name`. Returns the full path of the file that was copied
path copy_file(path const& file, std::string const& new_name);
// location of the managed folder
path location() const { return folder; }
private:
DebugMode debug;
path folder;
std::vector<std::string> files;
};
} // namespace fs
// copy the contents of a std::string into a char array and add a null terminator at the end
// src - std::string to read from
// dst - char array to write to
// size_dst - number of characters in the dst array
inline void copy_string_to_char_array(std::string const& src, char* dst, size_t size_dst) {
// Creates a spurious C4996 Warning in VS 2015 - ignore it
#if defined(WIN32)
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
dst[src.copy(dst, size_dst - 1)] = 0;
#if defined(WIN32)
#pragma warning(pop)
#endif
}
#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;
}
inline 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;
}
inline void loader_platform_close_library(loader_platform_dl_handle library) { FreeLibrary(library); }
inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
assert(library);
assert(name);
return (void*)GetProcAddress(library, name);
}
inline 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__) || defined(__FreeBSD__)
typedef void* loader_platform_dl_handle;
inline loader_platform_dl_handle loader_platform_open_library(const char* libPath) {
return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL);
}
inline const char* loader_platform_open_library_error(const char* libPath) { return dlerror(); }
inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); }
inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
assert(library);
assert(name);
return dlsym(library, name);
}
inline const char* loader_platform_get_proc_address_error(const char* name) { return dlerror(); }
#endif
class FromVoidStarFunc {
private:
void* function;
public:
FromVoidStarFunc(void* function) : function(function) {}
FromVoidStarFunc(PFN_vkVoidFunction function) : function(reinterpret_cast<void*>(function)) {}
template <typename T>
operator T() {
return reinterpret_cast<T>(function);
}
};
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;
}
FromVoidStarFunc get_symbol(const char* symbol_name) const {
assert(lib_handle != nullptr && "Cannot get symbol with null library handle");
void* symbol = loader_platform_get_proc_address(lib_handle, symbol_name);
if (symbol == nullptr) {
fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, loader_platform_get_proc_address_error(symbol_name));
assert(symbol != nullptr && "Must be able to get symbol");
}
return FromVoidStarFunc(symbol);
}
explicit operator bool() const noexcept { return lib_handle != nullptr; }
loader_platform_dl_handle lib_handle = nullptr;
fs::path lib_path;
};
template <typename T>
PFN_vkVoidFunction to_vkVoidFunction(T func) {
return reinterpret_cast<PFN_vkVoidFunction>(func);
}
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);
}
bool string_eq(const char* a, const char* b) noexcept;
bool string_eq(const char* a, const char* b, size_t len) noexcept;
inline std::string version_to_string(uint32_t version) {
std::string out = std::to_string(VK_API_VERSION_MAJOR(version)) + "." + std::to_string(VK_API_VERSION_MINOR(version)) + "." +
std::to_string(VK_API_VERSION_PATCH(version));
if (VK_API_VERSION_VARIANT(version) != 0) out += std::to_string(VK_API_VERSION_VARIANT(version)) + "." + out;
return out;
}
// Macro to ease the definition of variables with builder member functions
// class_name = class the member variable is apart of
// type = type of the variable
// name = name of the variable
// default_value = value to default initialize, use {} if nothing else makes sense
#define BUILDER_VALUE(class_name, type, name, default_value) \
type name = default_value; \
class_name& set_##name(type const& name) { \
this->name = name; \
return *this; \
}
// Macro to ease the definition of vectors with builder member functions
// class_name = class the member variable is apart of
// type = type of the variable
// name = name of the variable
// singular_name = used for the `add_singluar_name` member function
#define BUILDER_VECTOR(class_name, type, name, singular_name) \
std::vector<type> name; \
class_name& add_##singular_name(type const& singular_name) { \
this->name.push_back(singular_name); \
return *this; \
} \
class_name& add_##singular_name##s(std::vector<type> const& singular_name) { \
for (auto& elem : singular_name) this->name.push_back(elem); \
return *this; \
}
// Like BUILDER_VECTOR but for move only types - where passing in means giving up ownership
#define BUILDER_VECTOR_MOVE_ONLY(class_name, type, name, singular_name) \
std::vector<type> name; \
class_name& add_##singular_name(type&& singular_name) { \
this->name.push_back(std::move(singular_name)); \
return *this; \
}
struct ManifestVersion {
BUILDER_VALUE(ManifestVersion, uint32_t, major, 1)
BUILDER_VALUE(ManifestVersion, uint32_t, minor, 0)
BUILDER_VALUE(ManifestVersion, uint32_t, patch, 0)
ManifestVersion() noexcept {};
ManifestVersion(uint32_t major, uint32_t minor, uint32_t patch) noexcept : major(major), minor(minor), patch(patch){};
std::string get_version_str() const noexcept {
return std::string("\"file_format_version\": \"") + std::to_string(major) + "." + std::to_string(minor) + "." +
std::to_string(patch) + "\",";
}
};
// ManifestICD builder
struct ManifestICD {
BUILDER_VALUE(ManifestICD, ManifestVersion, file_format_version, ManifestVersion())
BUILDER_VALUE(ManifestICD, uint32_t, api_version, 0)
BUILDER_VALUE(ManifestICD, std::string, lib_path, {})
std::string get_manifest_str() const;
};
// ManifestLayer builder
struct ManifestLayer {
struct LayerDescription {
enum class Type { INSTANCE, GLOBAL, DEVICE };
std::string get_type_str(Type layer_type) const {
if (layer_type == Type::GLOBAL)
return "GLOBAL";
else if (layer_type == Type::DEVICE)
return "DEVICE";
else // default
return "INSTANCE";
}
struct FunctionOverride {
BUILDER_VALUE(FunctionOverride, std::string, vk_func, {})
BUILDER_VALUE(FunctionOverride, std::string, override_name, {})
std::string get_manifest_str() const { return std::string("\"") + vk_func + "\":\"" + override_name + "\""; }
};
struct Extension {
Extension() noexcept {}
Extension(std::string name, uint32_t spec_version = 0, std::vector<std::string> entrypoints = {}) noexcept
: name(name), spec_version(spec_version), entrypoints(entrypoints) {}
std::string name;
uint32_t spec_version = 0;
std::vector<std::string> entrypoints;
std::string get_manifest_str() const;
};
BUILDER_VALUE(LayerDescription, std::string, name, {})
BUILDER_VALUE(LayerDescription, Type, type, Type::INSTANCE)
BUILDER_VALUE(LayerDescription, fs::path, lib_path, {})
BUILDER_VALUE(LayerDescription, uint32_t, api_version, VK_API_VERSION_1_0)
BUILDER_VALUE(LayerDescription, uint32_t, implementation_version, 0)
BUILDER_VALUE(LayerDescription, std::string, description, {})
BUILDER_VECTOR(LayerDescription, FunctionOverride, functions, function)
BUILDER_VECTOR(LayerDescription, Extension, instance_extensions, instance_extension)
BUILDER_VECTOR(LayerDescription, Extension, device_extensions, device_extension)
BUILDER_VALUE(LayerDescription, std::string, enable_environment, {})
BUILDER_VALUE(LayerDescription, std::string, disable_environment, {})
BUILDER_VECTOR(LayerDescription, std::string, component_layers, component_layer)
BUILDER_VECTOR(LayerDescription, std::string, blacklisted_layers, blacklisted_layer)
BUILDER_VECTOR(LayerDescription, std::string, override_paths, override_path)
BUILDER_VECTOR(LayerDescription, FunctionOverride, pre_instance_functions, pre_instance_function)
std::string get_manifest_str() const;
VkLayerProperties get_layer_properties() const;
};
BUILDER_VALUE(ManifestLayer, ManifestVersion, file_format_version, {})
BUILDER_VECTOR(ManifestLayer, LayerDescription, layers, layer)
std::string get_manifest_str() const;
};
struct Extension {
BUILDER_VALUE(Extension, std::string, extensionName, {})
BUILDER_VALUE(Extension, uint32_t, specVersion, VK_API_VERSION_1_0)
Extension(std::string extensionName, uint32_t specVersion = VK_API_VERSION_1_0)
: extensionName(extensionName), specVersion(specVersion) {}
VkExtensionProperties get() const noexcept {
VkExtensionProperties props{};
copy_string_to_char_array(extensionName, &props.extensionName[0], VK_MAX_EXTENSION_NAME_SIZE);
props.specVersion = specVersion;
return props;
}
};
struct MockQueueFamilyProperties {
BUILDER_VALUE(MockQueueFamilyProperties, VkQueueFamilyProperties, properties, {})
BUILDER_VALUE(MockQueueFamilyProperties, bool, support_present, false)
MockQueueFamilyProperties() {}
MockQueueFamilyProperties(VkQueueFamilyProperties properties, bool support_present = false)
: properties(properties), support_present(support_present) {}
VkQueueFamilyProperties get() const noexcept { return properties; }
};
struct VulkanFunctions {
LibraryWrapper loader;
// Pre-Instance
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties = nullptr;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = nullptr;
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion = nullptr;
PFN_vkCreateInstance vkCreateInstance = nullptr;
// Instance
PFN_vkDestroyInstance vkDestroyInstance = nullptr;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = nullptr;
PFN_vkEnumeratePhysicalDeviceGroups vkEnumeratePhysicalDeviceGroups = nullptr;
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures = nullptr;
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2 = nullptr;
PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties = nullptr;
PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2 = nullptr;
PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties = nullptr;
PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 vkGetPhysicalDeviceSparseImageFormatProperties2 = nullptr;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = nullptr;
PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2 = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties2 vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2 = nullptr;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties = nullptr;
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties = nullptr;
PFN_vkGetPhysicalDeviceExternalBufferProperties vkGetPhysicalDeviceExternalBufferProperties = nullptr;
PFN_vkGetPhysicalDeviceExternalFenceProperties vkGetPhysicalDeviceExternalFenceProperties = nullptr;
PFN_vkGetPhysicalDeviceExternalSemaphoreProperties vkGetPhysicalDeviceExternalSemaphoreProperties = nullptr;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr;
PFN_vkCreateDevice vkCreateDevice = nullptr;
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr;
// WSI
PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT = nullptr;
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR = nullptr;
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR = nullptr;
PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR = nullptr;
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR = nullptr;
PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR = nullptr;
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR = nullptr;
PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR = nullptr;
PFN_vkGetPhysicalDeviceDisplayProperties2KHR vkGetPhysicalDeviceDisplayProperties2KHR = nullptr;
PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR vkGetPhysicalDeviceDisplayPlaneProperties2KHR = nullptr;
PFN_vkGetDisplayModeProperties2KHR vkGetDisplayModeProperties2KHR = nullptr;
PFN_vkGetDisplayPlaneCapabilities2KHR vkGetDisplayPlaneCapabilities2KHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR = nullptr;
#ifdef VK_USE_PLATFORM_ANDROID_KHR
PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR = nullptr;
#endif // VK_USE_PLATFORM_ANDROID_KHR
#ifdef VK_USE_PLATFORM_DIRECTFB_EXT
PFN_vkCreateDirectFBSurfaceEXT vkCreateDirectFBSurfaceEXT = nullptr;
PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT vkGetPhysicalDeviceDirectFBPresentationSupportEXT = nullptr;
#endif // VK_USE_PLATFORM_DIRECTFB_EXT
#ifdef VK_USE_PLATFORM_FUCHSIA
PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr;
#endif // VK_USE_PLATFORM_FUCHSIA
#ifdef VK_USE_PLATFORM_GGP
PFN_vkCreateStreamDescriptorSurfaceGGP vkCreateStreamDescriptorSurfaceGGP = nullptr;
#endif // VK_USE_PLATFORM_GGP
#ifdef VK_USE_PLATFORM_IOS_MVK
PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK = nullptr;
#endif // VK_USE_PLATFORM_IOS_MVK
#ifdef VK_USE_PLATFORM_MACOS_MVK
PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = nullptr;
#endif // VK_USE_PLATFORM_MACOS_MVK
#ifdef VK_USE_PLATFORM_METAL_EXT
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT = nullptr;
#endif // VK_USE_PLATFORM_METAL_EXT
#ifdef VK_USE_PLATFORM_SCREEN_QNX
PFN_vkCreateScreenSurfaceQNX vkCreateScreenSurfaceQNX = nullptr;
PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX vkGetPhysicalDeviceScreenPresentationSupportQNX = nullptr;
#endif // VK_USE_PLATFORM_SCREEN_QNX
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = nullptr;
#endif // VK_USE_PLATFORM_WAYLAND_KHR
#ifdef VK_USE_PLATFORM_XCB_KHR
PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = nullptr;
#endif // VK_USE_PLATFORM_XCB_KHR
#ifdef VK_USE_PLATFORM_XLIB_KHR
PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = nullptr;
#endif // VK_USE_PLATFORM_XLIB_KHR
#ifdef VK_USE_PLATFORM_WIN32_KHR
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = nullptr;
#endif // VK_USE_PLATFORM_WIN32_KHR
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = nullptr;
// device functions
PFN_vkDestroyDevice vkDestroyDevice = nullptr;
PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr;
VulkanFunctions();
FromVoidStarFunc load(VkInstance inst, const char* func_name) {
return FromVoidStarFunc(vkGetInstanceProcAddr(inst, func_name));
}
FromVoidStarFunc load(VkDevice device, const char* func_name) {
return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name));
}
};
struct DeviceFunctions {
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr;
PFN_vkDestroyDevice vkDestroyDevice = nullptr;
PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr;
PFN_vkCreateCommandPool vkCreateCommandPool = nullptr;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = nullptr;
PFN_vkDestroyCommandPool vkDestroyCommandPool = nullptr;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = nullptr;
DeviceFunctions() = default;
DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device);
FromVoidStarFunc load(VkDevice device, const char* func_name) const {
return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name));
}
};
struct InstanceCreateInfo {
BUILDER_VALUE(InstanceCreateInfo, VkInstanceCreateInfo, instance_info, {})
BUILDER_VALUE(InstanceCreateInfo, VkApplicationInfo, application_info, {})
BUILDER_VALUE(InstanceCreateInfo, std::string, app_name, {})
BUILDER_VALUE(InstanceCreateInfo, std::string, engine_name, {})
BUILDER_VALUE(InstanceCreateInfo, uint32_t, app_version, 0)
BUILDER_VALUE(InstanceCreateInfo, uint32_t, engine_version, 0)
BUILDER_VALUE(InstanceCreateInfo, uint32_t, api_version, VK_API_VERSION_1_0)
BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_layers, layer)
BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_extensions, extension)
// tell the get() function to not provide `application_info`
BUILDER_VALUE(InstanceCreateInfo, bool, fill_in_application_info, true)
InstanceCreateInfo();
VkInstanceCreateInfo* get() noexcept;
InstanceCreateInfo& set_api_version(uint32_t major, uint32_t minor, uint32_t patch);
};
struct DeviceQueueCreateInfo {
BUILDER_VALUE(DeviceQueueCreateInfo, VkDeviceQueueCreateInfo, queue_create_info, {})
BUILDER_VECTOR(DeviceQueueCreateInfo, float, priorities, priority)
DeviceQueueCreateInfo();
VkDeviceQueueCreateInfo get() noexcept;
};
struct DeviceCreateInfo {
BUILDER_VALUE(DeviceCreateInfo, VkDeviceCreateInfo, dev, {})
BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_extensions, extension)
BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_layers, layer)
BUILDER_VECTOR(DeviceCreateInfo, DeviceQueueCreateInfo, queue_info_details, device_queue)
VkDeviceCreateInfo* get() noexcept;
private:
std::vector<VkDeviceQueueCreateInfo> device_queue_infos;
};
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 string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) &&
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 string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion;
}
inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); }
struct VulkanFunction {
std::string name;
void* function;
};
template <typename T, size_t U>
bool check_permutation(std::initializer_list<const char*> expected, std::array<T, U> const& returned) {
if (expected.size() != returned.size()) return false;
for (uint32_t i = 0; i < expected.size(); i++) {
bool found = false;
for (auto& elem : returned) {
if (string_eq(*(expected.begin() + i), elem.layerName)) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
template <typename T, size_t U>
bool check_permutation(std::initializer_list<const char*> expected, std::vector<T> const& returned) {
if (expected.size() != returned.size()) return false;
for (uint32_t i = 0; i < expected.size(); i++) {
bool found = false;
for (auto& elem : returned) {
if (string_eq(*(expected.begin() + i), elem.layerName)) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}