blob: 5f9355c05acfa806eef3464652f024de1492b7f8 [file] [log] [blame]
/*
* 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>
*/
/*
* The test_environment is what combines the icd, layer, and shim library into a single object that
* test fixtures can create and use. Responsible for loading the libraries and establishing the
* channels for tests to talk with the icd's and layers.
*/
#pragma once
// Must include gtest first to guard against Xlib colliding due to redefinitions of "None" and "Bool"
#ifdef _MSC_VER
#pragma warning(push)
/*
MSVC warnings 4251 and 4275 have to do with potential dll-interface mismatch
between library (gtest) and users. Since we build the gtest library
as part of the test build we know that the dll-interface will match and
can disable these warnings.
*/
#pragma warning(disable : 4251)
#pragma warning(disable : 4275)
#endif
// GTest and Xlib collide due to redefinitions of "None" and "Bool"
#ifdef VK_USE_PLATFORM_XLIB_KHR
#pragma push_macro("None")
#pragma push_macro("Bool")
#undef None
#undef Bool
#endif
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
// Use the NDK's header on Android
#include "gtest/gtest.h"
#include "test_util.h"
#include "shim/shim.h"
#include "icd/physical_device.h"
#include "icd/test_icd.h"
#include "layer/test_layer.h"
// handle checking
template <typename T>
void handle_assert_has_value(T const& handle) {
ASSERT_TRUE(handle != VK_NULL_HANDLE);
}
template <typename T>
void handle_assert_null(T const& handle) {
ASSERT_TRUE(handle == VK_NULL_HANDLE);
}
template <typename T>
void handle_assert_has_values(std::vector<T> const& handles) {
for (auto const& handle : handles) {
ASSERT_TRUE(handle != VK_NULL_HANDLE);
}
}
template <typename T>
void handle_assert_no_values(std::vector<T> const& handles) {
for (auto const& handle : handles) {
ASSERT_TRUE(handle == VK_NULL_HANDLE);
}
}
template <typename T>
void handle_assert_no_values(size_t length, T handles[]) {
for (size_t i = 0; i < length; i++) {
ASSERT_TRUE(handles[i] == VK_NULL_HANDLE);
}
}
template <typename T>
void handle_assert_equal(T const& left, T const& right) {
ASSERT_EQ(left, right);
}
template <typename T>
void handle_assert_equal(std::vector<T> const& left, std::vector<T> const& right) {
ASSERT_EQ(left.size(), right.size());
for (size_t i = 0; i < left.size(); i++) {
ASSERT_EQ(left[i], right[i]);
}
}
template <typename T>
void handle_assert_equal(size_t count, T left[], T right[]) {
for (size_t i = 0; i < count; i++) {
ASSERT_EQ(left[i], right[i]);
}
}
// InstWrapper & DeviceWrapper - used to make creating instances & devices easier test writing
struct InstWrapper {
InstWrapper(VulkanFunctions& functions, VkAllocationCallbacks* callbacks = nullptr) noexcept;
InstWrapper(VulkanFunctions& functions, VkInstance inst, VkAllocationCallbacks* callbacks = nullptr) noexcept;
~InstWrapper() noexcept;
// Move-nly object
InstWrapper(InstWrapper const&) = delete;
InstWrapper& operator=(InstWrapper const&) = delete;
InstWrapper(InstWrapper&& other) noexcept;
InstWrapper& operator=(InstWrapper&&) noexcept;
// Construct this VkInstance using googletest to assert if it succeeded
void CheckCreate(VkResult result_to_check = VK_SUCCESS);
// Convenience
operator VkInstance() { return inst; }
VulkanFunctions* operator->() { return functions; }
FromVoidStarFunc load(const char* func_name) { return FromVoidStarFunc(functions->vkGetInstanceProcAddr(inst, func_name)); }
// Enumerate physical devices using googletest to assert if it succeeded
std::vector<VkPhysicalDevice> GetPhysDevs(VkResult result_to_check = VK_SUCCESS); // query all physical devices
std::vector<VkPhysicalDevice> GetPhysDevs(uint32_t phys_dev_count,
VkResult result_to_check = VK_SUCCESS); // query only phys_dev_count devices
// Enumerate a single physical device using googletest to assert if it succeeded
VkPhysicalDevice GetPhysDev(VkResult result_to_check = VK_SUCCESS);
VulkanFunctions* functions = nullptr;
VkInstance inst = VK_NULL_HANDLE;
VkAllocationCallbacks* callbacks = nullptr;
InstanceCreateInfo create_info{};
};
std::vector<VkExtensionProperties> EnumerateDeviceExtensions(InstWrapper const& inst, VkPhysicalDevice physical_device);
struct DeviceWrapper {
DeviceWrapper(InstWrapper& inst_wrapper, VkAllocationCallbacks* callbacks = nullptr) noexcept;
DeviceWrapper(VulkanFunctions& functions, VkDevice device, VkAllocationCallbacks* callbacks = nullptr) noexcept;
~DeviceWrapper() noexcept;
// Move-only object
DeviceWrapper(DeviceWrapper const&) = delete;
DeviceWrapper& operator=(DeviceWrapper const&) = delete;
DeviceWrapper(DeviceWrapper&&) noexcept;
DeviceWrapper& operator=(DeviceWrapper&&) noexcept;
// Construct this VkDevice using googletest to assert if it succeeded
void CheckCreate(VkPhysicalDevice physical_device, VkResult result_to_check = VK_SUCCESS);
// Convenience
operator VkDevice() { return dev; }
VulkanFunctions* operator->() { return functions; }
FromVoidStarFunc load(const char* func_name) { return FromVoidStarFunc(functions->vkGetDeviceProcAddr(dev, func_name)); }
VulkanFunctions* functions = nullptr;
VkDevice dev = VK_NULL_HANDLE;
VkAllocationCallbacks* callbacks = nullptr;
DeviceCreateInfo create_info{};
};
struct DebugUtilsLogger {
static VkBool32 VKAPI_PTR DebugUtilsMessengerLoggerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
DebugUtilsLogger* debug = reinterpret_cast<DebugUtilsLogger*>(pUserData);
debug->returned_output += pCallbackData->pMessage;
debug->returned_output += '\n';
return VK_FALSE;
}
DebugUtilsLogger(VkDebugUtilsMessageSeverityFlagsEXT severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
returned_output.reserve(4096); // output can be very noisy, reserving should help prevent many small allocations
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info.pNext = nullptr;
create_info.messageSeverity = severity;
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info.pfnUserCallback = DebugUtilsMessengerLoggerCallback;
create_info.pUserData = this;
}
// Immoveable object
DebugUtilsLogger(DebugUtilsLogger const&) = delete;
DebugUtilsLogger& operator=(DebugUtilsLogger const&) = delete;
DebugUtilsLogger(DebugUtilsLogger&&) = delete;
DebugUtilsLogger& operator=(DebugUtilsLogger&&) = delete;
// Find a string in the log output
bool find(std::string const& search_text) const { return returned_output.find(search_text) != std::string::npos; }
// Clear the log
void clear() { returned_output.clear(); }
VkDebugUtilsMessengerCreateInfoEXT* get() noexcept { return &create_info; }
VkDebugUtilsMessengerCreateInfoEXT create_info{};
std::string returned_output;
};
struct DebugUtilsWrapper {
DebugUtilsWrapper() noexcept {}
DebugUtilsWrapper(InstWrapper& inst_wrapper,
VkDebugUtilsMessageSeverityFlagsEXT severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
VkAllocationCallbacks* callbacks = nullptr)
: logger(severity), inst(inst_wrapper.inst), callbacks(callbacks) {
vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkCreateDebugUtilsMessengerEXT"));
vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkDestroyDebugUtilsMessengerEXT"));
};
~DebugUtilsWrapper() noexcept {
if (messenger) {
vkDestroyDebugUtilsMessengerEXT(inst, messenger, callbacks);
}
}
// Immoveable object
DebugUtilsWrapper(DebugUtilsWrapper const&) = delete;
DebugUtilsWrapper& operator=(DebugUtilsWrapper const&) = delete;
DebugUtilsWrapper(DebugUtilsWrapper&&) = delete;
DebugUtilsWrapper& operator=(DebugUtilsWrapper&&) = delete;
bool find(std::string const& search_text) { return logger.find(search_text); }
VkDebugUtilsMessengerCreateInfoEXT* get() noexcept { return logger.get(); }
DebugUtilsLogger logger;
VkInstance inst;
VkAllocationCallbacks* callbacks = nullptr;
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr;
VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
};
VkResult CreateDebugUtilsMessenger(DebugUtilsWrapper& debug_utils);
// Helper that adds the debug utils extension name and sets the pNext chain up
// NOTE: Ignores existing pNext chains
void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsLogger& logger);
void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsWrapper& wrapper);
struct PlatformShimWrapper {
PlatformShimWrapper(DebugMode debug_mode = DebugMode::none) noexcept;
~PlatformShimWrapper() noexcept;
PlatformShimWrapper(PlatformShimWrapper const&) = delete;
PlatformShimWrapper& operator=(PlatformShimWrapper const&) = delete;
// Convenience
PlatformShim* operator->() { return platform_shim; }
LibraryWrapper shim_library;
PlatformShim* platform_shim;
DebugMode debug_mode = DebugMode::none;
};
struct TestICDHandle {
TestICDHandle() noexcept;
TestICDHandle(fs::path const& icd_path) noexcept;
TestICD& reset_icd() noexcept;
TestICD& get_test_icd() noexcept;
fs::path get_icd_full_path() noexcept;
// Must use statically
LibraryWrapper icd_library;
GetTestICDFunc proc_addr_get_test_icd;
GetNewTestICDFunc proc_addr_reset_icd;
};
struct TestLayerHandle {
TestLayerHandle() noexcept;
TestLayerHandle(fs::path const& icd_path) noexcept;
TestLayer& reset_layer() noexcept;
TestLayer& get_test_layer() noexcept;
fs::path get_layer_full_path() noexcept;
// Must use statically
LibraryWrapper layer_library;
GetTestLayerFunc proc_addr_get_test_layer;
GetNewTestLayerFunc proc_addr_reset_layer;
};
struct TestICDDetails {
TestICDDetails(fs::path icd_path, uint32_t api_version = VK_API_VERSION_1_0) noexcept
: icd_path(icd_path), api_version(api_version) {}
BUILDER_VALUE(TestICDDetails, fs::path, icd_path, {});
BUILDER_VALUE(TestICDDetails, uint32_t, api_version, VK_API_VERSION_1_0);
BUILDER_VALUE(TestICDDetails, std::string, json_name, "test_icd");
BUILDER_VALUE(TestICDDetails, bool, use_env_var_icd_filenames, false);
BUILDER_VALUE(TestICDDetails, bool, use_add_env_var_icd_filenames, false);
BUILDER_VALUE(TestICDDetails, bool, is_fake, false);
};
struct TestLayerDetails {
TestLayerDetails(ManifestLayer layer_manifest, const std::string& json_name) noexcept
: layer_manifest(layer_manifest), json_name(json_name) {}
BUILDER_VALUE(TestLayerDetails, ManifestLayer, layer_manifest, {});
BUILDER_VALUE(TestLayerDetails, std::string, json_name, "test_layer");
BUILDER_VALUE(TestLayerDetails, fs::FolderManager*, destination_folder, nullptr);
BUILDER_VALUE(TestLayerDetails, bool, add_to_regular_search_paths, true);
BUILDER_VALUE(TestLayerDetails, bool, is_fake, false);
};
struct FrameworkEnvironment {
FrameworkEnvironment(DebugMode debug_mode = DebugMode::none, bool override_icds = false,
bool override_explicit_layers = false) noexcept;
void add_icd(TestICDDetails icd_details) noexcept;
void add_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
void add_implicit_layer(TestLayerDetails layer_details) noexcept;
void add_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
void add_explicit_layer(TestLayerDetails layer_details) noexcept;
void add_fake_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
void add_fake_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
TestICD& get_test_icd(int index = 0) noexcept;
TestICD& reset_icd(int index = 0) noexcept;
fs::path get_test_icd_path(int index = 0) noexcept;
TestLayer& get_test_layer(int index = 0) noexcept;
TestLayer& reset_layer(int index = 0) noexcept;
fs::path get_test_layer_path(int index = 0) noexcept;
PlatformShimWrapper platform_shim;
fs::FolderManager null_folder;
fs::FolderManager icd_folder;
fs::FolderManager explicit_layer_folder;
fs::FolderManager implicit_layer_folder;
DebugUtilsLogger debug_log;
VulkanFunctions vulkan_functions;
std::vector<TestICDHandle> icds;
std::vector<TestLayerHandle> layers;
std::string env_var_vk_icd_filenames;
std::string add_env_var_vk_icd_filenames;
private:
void add_layer_impl(TestLayerDetails layer_details, fs::FolderManager& folder_manager, ManifestCategory category);
};