blob: ba3ca9bf64697ea147ae41b3c3ba2dbfe0fc011b [file] [log] [blame]
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 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.
*/
#include "test_framework.h"
#include "vk_layer_config.h"
#include "generated/vk_function_pointers.h"
#include <glslang/Public/ShaderLang.h>
#include CONFIG_HEADER_FILE
#include <filesystem>
struct SwapchainBuffers {
VkImage image;
VkCommandBuffer cmd;
VkImageView view;
};
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
// NOTE: Android doesn't use environment variables like desktop does!
//
// Certain VK_* environment variables accept lists.
// Return a vector of std::string containing each member in the list.
//
// EX input:
// export VK_DRIVER_FILES=/intel.json:/amd.json
// set VK_DRIVER_FILES=\nvidia.json;\mesa.json
static std::vector<std::string> GetVkEnvironmentVariable(const char *env_var) {
const std::string str = GetEnvironment(env_var);
if (str.empty()) {
return {};
}
// Loader uses standard OS path separators per platform
constexpr char delimiter =
#ifdef _WIN32
';';
#else
':';
#endif
std::vector<std::string> items;
std::string::size_type start = 0;
std::string::size_type pos = str.find_first_of(delimiter, start);
std::string::size_type length = pos;
while (pos != std::string::npos) {
// emplace uses std::substr which takes length from start
items.emplace_back(str, start, length);
start = pos + 1;
pos = str.find_first_of(delimiter, start);
length = pos - start;
}
items.emplace_back(str, start);
return items;
}
static void CheckAndSetEnvironmentVariables() {
for (const char *env_var : {"VK_DRIVER_FILES", "VK_ICD_FILENAMES"}) {
const std::vector<std::string> driver_files = GetVkEnvironmentVariable(env_var);
for (const std::string &driver_file : driver_files) {
const std::filesystem::path icd_file(driver_file);
// TODO: Error check relative paths (platform dependent)
if (icd_file.is_relative()) {
continue;
}
std::string user_provided;
user_provided += "\n\n";
user_provided += env_var;
user_provided += " = ";
user_provided += driver_file;
if (std::filesystem::is_directory(icd_file)) {
if (!std::filesystem::exists(icd_file)) {
std::cerr << "Invalid " << env_var << "! Directory doesn't exist!" << user_provided << std::endl;
std::exit(EXIT_FAILURE);
}
bool contains_json = false;
for (auto const &dir_entry : std::filesystem::directory_iterator{icd_file}) {
if (dir_entry.path().extension() == ".json") {
contains_json = true;
}
}
if (!contains_json) {
std::cerr << "Invalid " << env_var << "! " << env_var << " must contain a json file!" << user_provided
<< std::endl;
std::exit(EXIT_FAILURE);
}
} else {
if (!std::filesystem::exists(icd_file)) {
std::cerr << "Invalid " << env_var << "! File doesn't exist!" << user_provided << std::endl;
std::exit(EXIT_FAILURE);
}
if (icd_file.extension() != ".json") {
std::cerr << "Invalid " << env_var << "! " << env_var << " must be a json file!" << user_provided << std::endl;
std::exit(EXIT_FAILURE);
}
}
}
}
bool found_json = false;
bool vk_layer_env_vars_present = false;
std::ostringstream error_log; // Build up error log in case the validation json cannot be found
for (const char *env_var : {"VK_LAYER_PATH", "VK_ADD_LAYER_PATH"}) {
const std::vector<std::string> vk_layer_paths = GetVkEnvironmentVariable(env_var);
if (!vk_layer_paths.empty()) {
vk_layer_env_vars_present = true;
}
for (const std::string &vk_layer_path : vk_layer_paths) {
const std::filesystem::path layer_path(vk_layer_path);
if (!std::filesystem::exists(layer_path)) {
error_log << "Invalid " << env_var << "! " << layer_path << " doesn't exist." << std::endl;
continue;
}
if (std::filesystem::is_directory(layer_path)) {
for (auto const &dir_entry : std::filesystem::directory_iterator{layer_path}) {
if (dir_entry.path().filename() == "VkLayer_khronos_validation.json") {
if (std::filesystem::exists(dir_entry)) {
found_json = true;
break;
} else {
error_log << "Invalid " << env_var << "! " << dir_entry << " doen not exist!" << std::endl;
}
}
}
if (!found_json) {
error_log << "Invalid " << env_var << "! " << layer_path
<< " is a directory but doesn't contain a VkLayer_khronos_validation.json file!" << std::endl;
}
} else {
if (layer_path.filename() == "VkLayer_khronos_validation.json") {
found_json = true;
break;
} else {
error_log << "Invalid " << env_var << "! The filename of path" << layer_path
<< " is not VkLayer_khronos_validation.json!" << std::endl;
}
}
if (found_json) {
break;
}
}
}
if (!found_json) {
if (vk_layer_env_vars_present) {
std::cerr << error_log.str() << std::endl;
std::cerr << "Automatically setting VK_LAYER_PATH to " << VALIDATION_LAYERS_BUILD_PATH << std::endl;
}
SetEnvironment("VK_LAYER_PATH", VALIDATION_LAYERS_BUILD_PATH);
}
}
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
bool WaylandContext::Init() {
display = wl_display_connect(nullptr);
if (!display) {
return false;
}
auto global = [](void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) {
(void)version;
const std::string_view interface_str = interface;
if (interface_str == "wl_compositor") {
auto compositor = reinterpret_cast<wl_compositor **>(data);
*compositor = reinterpret_cast<wl_compositor *>(wl_registry_bind(registry, id, &wl_compositor_interface, 1));
}
};
auto global_remove = [](void *data, struct wl_registry *registry, uint32_t id) {
(void)data;
(void)registry;
(void)id;
};
registry = wl_display_get_registry(display);
if (!registry) {
return false;
}
const wl_registry_listener registry_listener = {global, global_remove};
wl_registry_add_listener(registry, &registry_listener, &compositor);
wl_display_dispatch(display);
if (!compositor) {
return false;
}
surface = wl_compositor_create_surface(compositor);
if (!surface) {
return false;
}
const uint32_t version = wl_surface_get_version(surface);
if (version == 0) {
return false;
}
return true;
}
void WaylandContext::Release() {
wl_surface_destroy(surface);
surface = nullptr;
wl_compositor_destroy(compositor);
compositor = nullptr;
wl_registry_destroy(registry);
registry = nullptr;
wl_display_disconnect(display);
display = nullptr;
}
#endif
// Set up environment for GLSL compiler
// Must be done once per process
void TestEnvironment::SetUp() {
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
// Helps ensure common developer environment variables are set correctly
CheckAndSetEnvironmentVariables();
#endif
// Initialize GLSL to SPV compiler utility
glslang::InitializeProcess();
vk::InitCore("vulkan");
}
void TestEnvironment::TearDown() { glslang::FinalizeProcess(); }
void VkTestFramework::InitArgs(int *argc, char *argv[]) {
for (int i = 1; i < *argc; ++i) {
const std::string_view current_argument = argv[i];
if (current_argument == "--print-vu") {
m_print_vu = true;
} else if (current_argument == "--syncval-disable-core") {
m_syncval_disable_core = true;
} else if (current_argument == "--gpuav-disable-core") {
m_gpuav_disable_core = true;
} else if (current_argument == "--device-index" && ((i + 1) < *argc)) {
m_phys_device_index = std::atoi(argv[++i]);
} else if ((current_argument == "--help") || (current_argument == "-h")) {
printf("\nOther options:\n");
printf(
"\t--print-vu\n"
"\t\tPrints all VUs - help see what new VU will look like.\n");
printf(
"\t--syncval-disable-core\n"
"\t\tDisable core validation when running syncval tests.\n"
"\t\tBy default both CoreChecks and syncval validation is enabled.\n");
printf(
"\t--gpuav-disable-core\n"
"\t\tDisable core validation when running gpu-av tests.\n"
"\t\tBy default both CoreChecks and gpu-av is enabled.\n");
printf(
"\t--device-index <physical device index>\n"
"\t\tIndex into VkPhysicalDevice array returned from vkEnumeratePhysicalDevices.\n"
"\t\tThe default behavior is to automatically choose \"the most reasonable device.\"\n"
"\t\tAn invalid index (i.e., outside the range [0, *pPhysicalDeviceCount)) will result in the default "
"behavior\n");
exit(0);
} else {
printf("\nUnrecognized option: %s\n", argv[i]);
printf("\nUse --help or -h for option list.\n");
exit(0);
}
}
}
void VkTestFramework::Finish() {}