|  | // 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", | 
|  | "./build/vk_swiftshader.dll", | 
|  | "./vk_swiftshader.dll" }; | 
|  | #		else | 
|  | return { "./build/Release_Win32/vk_swiftshader.dll", | 
|  | "./build/Release/vk_swiftshader.dll", | 
|  | "./build/RelWithDebInfo/vk_swiftshader.dll", | 
|  | "./build/vk_swiftshader.dll", | 
|  | "./vk_swiftshader.dll" }; | 
|  | #		endif | 
|  | #	else | 
|  | #		if defined(_WIN64) | 
|  | return { "./build/Debug_x64/vk_swiftshader.dll", | 
|  | "./build/Debug/vk_swiftshader.dll", | 
|  | "./build/vk_swiftshader.dll", | 
|  | "./vk_swiftshader.dll" }; | 
|  | #		else | 
|  | return { "./build/Debug_Win32/vk_swiftshader.dll", | 
|  | "./build/Debug/vk_swiftshader.dll", | 
|  | "./build/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; | 
|  | } |