blob: 18ded5818dca314c47b7bc1af3b3287719fae695 [file] [log] [blame]
// Copyright 2021 The SwiftShader Authors. All Rights Reserved.
//
// 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 "VulkanTester.hpp"
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
// By default, load SwiftShader via loader
#ifndef LOAD_NATIVE_DRIVER
# define LOAD_NATIVE_DRIVER 0
#endif
#ifndef LOAD_SWIFTSHADER_DIRECTLY
# define LOAD_SWIFTSHADER_DIRECTLY 0
#endif
#if LOAD_NATIVE_DRIVER && LOAD_SWIFTSHADER_DIRECTLY
# error Enable only one of LOAD_NATIVE_DRIVER and LOAD_SWIFTSHADER_DIRECTLY
#endif
// By default, enable validation layers in DEBUG builds
#if !defined(ENABLE_VALIDATION_LAYERS) && !defined(NDEBUG)
# define ENABLE_VALIDATION_LAYERS 1
#endif
#if defined(_WIN32)
# define OS_WINDOWS 1
#elif defined(__APPLE__)
# include "dlfcn.h"
# define OS_MAC 1
#elif defined(__ANDROID__)
# include "dlfcn.h"
# define OS_ANDROID 1
#elif defined(__linux__)
# include "dlfcn.h"
# define OS_LINUX 1
#elif defined(__Fuchsia__)
# include <zircon/dlfcn.h>
# define OS_FUCHSIA 1
#else
# error Unimplemented platform
#endif
// TODO: move to its own header/cpp
// Wraps a single environment variable, allowing it to be set
// and automatically restored on destruction.
class ScopedSetEnvVar
{
public:
ScopedSetEnvVar(std::string name)
: name(name)
{
assert(!name.empty());
}
ScopedSetEnvVar(std::string name, std::string value)
: name(name)
{
set(value);
}
~ScopedSetEnvVar()
{
restore();
}
void set(std::string value)
{
restore();
if(auto ov = getEnv(name.data()))
{
oldValue = ov;
}
putEnv((name + std::string("=") + value).c_str());
}
void restore()
{
if(!oldValue.empty())
{
putEnv((name + std::string("=") + oldValue).c_str());
oldValue.clear();
}
}
private:
void putEnv(const char *env)
{
// POSIX putenv needs 'env' to live beyond the call
envCopy = env;
#if OS_WINDOWS
[[maybe_unused]] auto r = ::_putenv(envCopy.c_str());
assert(r == 0);
#else
[[maybe_unused]] auto r = ::putenv(const_cast<char *>(envCopy.c_str()));
assert(r == 0);
#endif
}
const char *getEnv(const char *name)
{
return ::getenv(name);
}
std::string name;
std::string oldValue;
std::string envCopy;
};
// Generates a temporary icd.json file that sets library_path at the input driverPath,
// and sets VK_ICD_FILENAMES environment variable to this file, restoring the env var
// and deleting the temp file on destruction.
class ScopedSetIcdFilenames
{
public:
ScopedSetIcdFilenames() = default;
ScopedSetIcdFilenames(const char *driverPath)
{
std::ofstream fout(icdFileName);
assert(fout && "Failed to create generated icd file");
fout << R"raw({ "file_format_version": "1.0.0", "ICD": { "library_path": ")raw" << driverPath << R"raw(", "api_version": "1.0.5" } } )raw";
fout.close();
setEnvVar.set(icdFileName);
}
~ScopedSetIcdFilenames()
{
if(fs::exists("vk_swiftshader_generated_icd.json"))
{
fs::remove("vk_swiftshader_generated_icd.json");
}
}
private:
static constexpr const char *icdFileName = "vk_swiftshader_generated_icd.json";
ScopedSetEnvVar setEnvVar{ "VK_ICD_FILENAMES" };
};
namespace {
std::vector<const char *> getDriverPaths()
{
#if OS_WINDOWS
# if !defined(STANDALONE)
// The DLL is delay loaded (see BUILD.gn), so we can load
// the correct ones from Chrome's swiftshader subdirectory.
// HMODULE libvulkan = LoadLibraryA("swiftshader\\libvulkan.dll");
// EXPECT_NE((HMODULE)NULL, libvulkan);
// return true;
# error TODO: !STANDALONE
# elif defined(NDEBUG)
# if defined(_WIN64)
return { "./build/Release_x64/vk_swiftshader.dll",
"./build/Release/vk_swiftshader.dll",
"./build/RelWithDebInfo/vk_swiftshader.dll",
"./vk_swiftshader.dll" };
# else
return { "./build/Release_Win32/vk_swiftshader.dll",
"./build/Release/vk_swiftshader.dll",
"./build/RelWithDebInfo/vk_swiftshader.dll",
"./vk_swiftshader.dll" };
# endif
# else
# if defined(_WIN64)
return { "./build/Debug_x64/vk_swiftshader.dll",
"./build/Debug/vk_swiftshader.dll",
"./vk_swiftshader.dll" };
# else
return { "./build/Debug_Win32/vk_swiftshader.dll",
"./build/Debug/vk_swiftshader.dll",
"./vk_swiftshader.dll" };
# endif
# endif
#elif OS_MAC
return { "./build/Darwin/libvk_swiftshader.dylib",
"swiftshader/libvk_swiftshader.dylib",
"libvk_swiftshader.dylib" };
#elif OS_LINUX
return { "./build/Linux/libvk_swiftshader.so",
"swiftshader/libvk_swiftshader.so",
"./libvk_swiftshader.so",
"libvk_swiftshader.so" };
#elif OS_ANDROID || OS_FUCHSIA
return
{
"libvk_swiftshader.so"
}
#else
# error Unimplemented platform
return {};
#endif
}
bool fileExists(const char *path)
{
std::ifstream f(path);
return f.good();
}
std::string findDriverPath()
{
for(auto &path : getDriverPaths())
{
if(fileExists(path))
return path;
}
#if(OS_LINUX || OS_ANDROID || OS_FUCHSIA)
// On Linux-based OSes, the lib path may be resolved by dlopen
for(auto &path : getDriverPaths())
{
auto lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
if(lib)
{
char libPath[2048] = { '\0' };
dlinfo(lib, RTLD_DI_ORIGIN, libPath);
dlclose(lib);
return std::string{ libPath } + "/" + path;
}
}
#endif
return {};
}
} // namespace
VulkanTester::VulkanTester() = default;
VulkanTester::~VulkanTester()
{
device.waitIdle();
device.destroy(nullptr);
if(debugReport) instance.destroy(debugReport);
instance.destroy(nullptr);
}
void VulkanTester::initialize()
{
dl = loadDriver();
assert(dl && dl->success());
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
vk::InstanceCreateInfo instanceCreateInfo;
std::vector<const char *> extensionNames
{
VK_KHR_SURFACE_EXTENSION_NAME,
#if USE_HEADLESS_SURFACE
VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
#endif
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
#endif
};
#if ENABLE_VALIDATION_LAYERS
extensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
std::vector<const char *> layerNames;
#if ENABLE_VALIDATION_LAYERS
auto addLayerIfAvailable = [](std::vector<const char *> &layers, const char *layer) {
static auto layerProperties = vk::enumerateInstanceLayerProperties();
if(std::find_if(layerProperties.begin(), layerProperties.end(), [layer](auto &lp) {
return strcmp(layer, lp.layerName) == 0;
}) != layerProperties.end())
{
// std::cout << "Enabled layer: " << layer << std::endl;
layers.push_back(layer);
}
};
addLayerIfAvailable(layerNames, "VK_LAYER_KHRONOS_validation");
addLayerIfAvailable(layerNames, "VK_LAYER_LUNARG_standard_validation");
#endif
instanceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(extensionNames.size());
instanceCreateInfo.ppEnabledLayerNames = layerNames.data();
instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(layerNames.size());
instance = vk::createInstance(instanceCreateInfo, nullptr);
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
#if ENABLE_VALIDATION_LAYERS
if(VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateDebugUtilsMessengerEXT)
{
vk::DebugUtilsMessengerCreateInfoEXT debugInfo;
debugInfo.messageSeverity =
// vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning;
debugInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
PFN_vkDebugUtilsMessengerCallbackEXT debugInfoCallback =
[](
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData) -> VkBool32 {
// assert(false);
std::cerr << "[DebugInfoCallback] " << pCallbackData->pMessage << std::endl;
return VK_FALSE;
};
debugInfo.pfnUserCallback = debugInfoCallback;
debugReport = instance.createDebugUtilsMessengerEXT(debugInfo);
}
#endif
std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
assert(!physicalDevices.empty());
physicalDevice = physicalDevices[0];
const float defaultQueuePriority = 0.0f;
vk::DeviceQueueCreateInfo queueCreateInfo;
queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &defaultQueuePriority;
std::vector<const char *> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
vk::DeviceCreateInfo deviceCreateInfo;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
device = physicalDevice.createDevice(deviceCreateInfo, nullptr);
queue = device.getQueue(queueFamilyIndex, 0);
}
std::unique_ptr<vk::DynamicLoader> VulkanTester::loadDriver()
{
if(LOAD_NATIVE_DRIVER)
{
return std::make_unique<vk::DynamicLoader>();
}
auto driverPath = findDriverPath();
assert(!driverPath.empty());
if(LOAD_SWIFTSHADER_DIRECTLY)
{
return std::make_unique<vk::DynamicLoader>(driverPath);
}
// Load SwiftShader via loader
// Set VK_ICD_FILENAMES env var so it gets picked up by the loading of the ICD driver
setIcdFilenames = std::make_unique<ScopedSetIcdFilenames>(driverPath.c_str());
std::unique_ptr<vk::DynamicLoader> dl;
#ifndef VULKAN_HPP_NO_EXCEPTIONS
try
{
dl = std::make_unique<vk::DynamicLoader>();
}
catch(std::exception &ex)
{
std::cerr << "vk::DynamicLoader exception: " << ex.what() << std::endl;
std::cerr << "Falling back to loading SwiftShader directly (i.e. no validation layers)" << std::endl;
dl = std::make_unique<vk::DynamicLoader>(driverPath);
}
#else
dl = std::make_unique<vk::DynamicLoader>();
#endif
return dl;
}