You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
10 KiB
394 lines
10 KiB
// 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
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
std::vector<const char *> layerNames;
|
|
#if ENABLE_VALIDATION_LAYERS
|
|
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;
|
|
}
|