1 // Copyright 2021 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "VulkanTester.hpp"
16 #include <cstdlib>
17 #include <filesystem>
18 #include <fstream>
19 #include <iostream>
20
21 namespace fs = std::filesystem;
22
23 // By default, load SwiftShader via loader
24 #ifndef LOAD_NATIVE_DRIVER
25 # define LOAD_NATIVE_DRIVER 0
26 #endif
27
28 #ifndef LOAD_SWIFTSHADER_DIRECTLY
29 # define LOAD_SWIFTSHADER_DIRECTLY 0
30 #endif
31
32 #if LOAD_NATIVE_DRIVER && LOAD_SWIFTSHADER_DIRECTLY
33 # error Enable only one of LOAD_NATIVE_DRIVER and LOAD_SWIFTSHADER_DIRECTLY
34 #endif
35
36 // By default, enable validation layers in DEBUG builds
37 #if !defined(ENABLE_VALIDATION_LAYERS) && !defined(NDEBUG)
38 # define ENABLE_VALIDATION_LAYERS 1
39 #endif
40
41 #if defined(_WIN32)
42 # define OS_WINDOWS 1
43 #elif defined(__APPLE__)
44 # include "dlfcn.h"
45 # define OS_MAC 1
46 #elif defined(__ANDROID__)
47 # include "dlfcn.h"
48 # define OS_ANDROID 1
49 #elif defined(__linux__)
50 # include "dlfcn.h"
51 # define OS_LINUX 1
52 #elif defined(__Fuchsia__)
53 # include <zircon/dlfcn.h>
54 # define OS_FUCHSIA 1
55 #else
56 # error Unimplemented platform
57 #endif
58
59 // TODO: move to its own header/cpp
60 // Wraps a single environment variable, allowing it to be set
61 // and automatically restored on destruction.
62 class ScopedSetEnvVar
63 {
64 public:
ScopedSetEnvVar(std::string name)65 ScopedSetEnvVar(std::string name)
66 : name(name)
67 {
68 assert(!name.empty());
69 }
70
ScopedSetEnvVar(std::string name,std::string value)71 ScopedSetEnvVar(std::string name, std::string value)
72 : name(name)
73 {
74 set(value);
75 }
76
~ScopedSetEnvVar()77 ~ScopedSetEnvVar()
78 {
79 restore();
80 }
81
set(std::string value)82 void set(std::string value)
83 {
84 restore();
85 if(auto ov = getEnv(name.data()))
86 {
87 oldValue = ov;
88 }
89 putEnv((name + std::string("=") + value).c_str());
90 }
91
restore()92 void restore()
93 {
94 if(!oldValue.empty())
95 {
96 putEnv((name + std::string("=") + oldValue).c_str());
97 oldValue.clear();
98 }
99 }
100
101 private:
putEnv(const char * env)102 void putEnv(const char *env)
103 {
104 // POSIX putenv needs 'env' to live beyond the call
105 envCopy = env;
106 #if OS_WINDOWS
107 [[maybe_unused]] auto r = ::_putenv(envCopy.c_str());
108 assert(r == 0);
109 #else
110 [[maybe_unused]] auto r = ::putenv(const_cast<char *>(envCopy.c_str()));
111 assert(r == 0);
112 #endif
113 }
114
getEnv(const char * name)115 const char *getEnv(const char *name)
116 {
117 return ::getenv(name);
118 }
119
120 std::string name;
121 std::string oldValue;
122 std::string envCopy;
123 };
124
125 // Generates a temporary icd.json file that sets library_path at the input driverPath,
126 // and sets VK_ICD_FILENAMES environment variable to this file, restoring the env var
127 // and deleting the temp file on destruction.
128 class ScopedSetIcdFilenames
129 {
130 public:
131 ScopedSetIcdFilenames() = default;
ScopedSetIcdFilenames(const char * driverPath)132 ScopedSetIcdFilenames(const char *driverPath)
133 {
134 std::ofstream fout(icdFileName);
135 assert(fout && "Failed to create generated icd file");
136 fout << R"raw({ "file_format_version": "1.0.0", "ICD": { "library_path": ")raw" << driverPath << R"raw(", "api_version": "1.0.5" } } )raw";
137 fout.close();
138
139 setEnvVar.set(icdFileName);
140 }
141
~ScopedSetIcdFilenames()142 ~ScopedSetIcdFilenames()
143 {
144 if(fs::exists("vk_swiftshader_generated_icd.json"))
145 {
146 fs::remove("vk_swiftshader_generated_icd.json");
147 }
148 }
149
150 private:
151 static constexpr const char *icdFileName = "vk_swiftshader_generated_icd.json";
152 ScopedSetEnvVar setEnvVar{ "VK_ICD_FILENAMES" };
153 };
154
155 namespace {
156
getDriverPaths()157 std::vector<const char *> getDriverPaths()
158 {
159 #if OS_WINDOWS
160 # if !defined(STANDALONE)
161 // The DLL is delay loaded (see BUILD.gn), so we can load
162 // the correct ones from Chrome's swiftshader subdirectory.
163 // HMODULE libvulkan = LoadLibraryA("swiftshader\\libvulkan.dll");
164 // EXPECT_NE((HMODULE)NULL, libvulkan);
165 // return true;
166 # error TODO: !STANDALONE
167 # elif defined(NDEBUG)
168 # if defined(_WIN64)
169 return { "./build/Release_x64/vk_swiftshader.dll",
170 "./build/Release/vk_swiftshader.dll",
171 "./build/RelWithDebInfo/vk_swiftshader.dll",
172 "./vk_swiftshader.dll" };
173 # else
174 return { "./build/Release_Win32/vk_swiftshader.dll",
175 "./build/Release/vk_swiftshader.dll",
176 "./build/RelWithDebInfo/vk_swiftshader.dll",
177 "./vk_swiftshader.dll" };
178 # endif
179 # else
180 # if defined(_WIN64)
181 return { "./build/Debug_x64/vk_swiftshader.dll",
182 "./build/Debug/vk_swiftshader.dll",
183 "./vk_swiftshader.dll" };
184 # else
185 return { "./build/Debug_Win32/vk_swiftshader.dll",
186 "./build/Debug/vk_swiftshader.dll",
187 "./vk_swiftshader.dll" };
188 # endif
189 # endif
190 #elif OS_MAC
191 return { "./build/Darwin/libvk_swiftshader.dylib",
192 "swiftshader/libvk_swiftshader.dylib",
193 "libvk_swiftshader.dylib" };
194 #elif OS_LINUX
195 return { "./build/Linux/libvk_swiftshader.so",
196 "swiftshader/libvk_swiftshader.so",
197 "./libvk_swiftshader.so",
198 "libvk_swiftshader.so" };
199 #elif OS_ANDROID || OS_FUCHSIA
200 return
201 {
202 "libvk_swiftshader.so"
203 }
204 #else
205 # error Unimplemented platform
206 return {};
207 #endif
208 }
209
fileExists(const char * path)210 bool fileExists(const char *path)
211 {
212 std::ifstream f(path);
213 return f.good();
214 }
215
findDriverPath()216 std::string findDriverPath()
217 {
218 for(auto &path : getDriverPaths())
219 {
220 if(fileExists(path))
221 return path;
222 }
223
224 #if(OS_LINUX || OS_ANDROID || OS_FUCHSIA)
225 // On Linux-based OSes, the lib path may be resolved by dlopen
226 for(auto &path : getDriverPaths())
227 {
228 auto lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
229 if(lib)
230 {
231 char libPath[2048] = { '\0' };
232 dlinfo(lib, RTLD_DI_ORIGIN, libPath);
233 dlclose(lib);
234 return std::string{ libPath } + "/" + path;
235 }
236 }
237 #endif
238
239 return {};
240 }
241
242 } // namespace
243
244 VulkanTester::VulkanTester() = default;
245
~VulkanTester()246 VulkanTester::~VulkanTester()
247 {
248 device.waitIdle();
249 device.destroy(nullptr);
250 if(debugReport) instance.destroy(debugReport);
251 instance.destroy(nullptr);
252 }
253
initialize()254 void VulkanTester::initialize()
255 {
256 dl = loadDriver();
257 assert(dl && dl->success());
258
259 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
260 VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
261
262 vk::InstanceCreateInfo instanceCreateInfo;
263 std::vector<const char *> extensionNames
264 {
265 VK_KHR_SURFACE_EXTENSION_NAME,
266 #if USE_HEADLESS_SURFACE
267 VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
268 #endif
269 #if defined(VK_USE_PLATFORM_WIN32_KHR)
270 VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
271 #endif
272 };
273 #if ENABLE_VALIDATION_LAYERS
274 extensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
275 #endif
276
277 std::vector<const char *> layerNames;
278 #if ENABLE_VALIDATION_LAYERS
279 auto addLayerIfAvailable = [](std::vector<const char *> &layers, const char *layer) {
280 static auto layerProperties = vk::enumerateInstanceLayerProperties();
281 if(std::find_if(layerProperties.begin(), layerProperties.end(), [layer](auto &lp) {
282 return strcmp(layer, lp.layerName) == 0;
283 }) != layerProperties.end())
284 {
285 // std::cout << "Enabled layer: " << layer << std::endl;
286 layers.push_back(layer);
287 }
288 };
289
290 addLayerIfAvailable(layerNames, "VK_LAYER_KHRONOS_validation");
291 addLayerIfAvailable(layerNames, "VK_LAYER_LUNARG_standard_validation");
292 #endif
293
294 instanceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
295 instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(extensionNames.size());
296 instanceCreateInfo.ppEnabledLayerNames = layerNames.data();
297 instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(layerNames.size());
298
299 instance = vk::createInstance(instanceCreateInfo, nullptr);
300 VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
301
302 #if ENABLE_VALIDATION_LAYERS
303 if(VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateDebugUtilsMessengerEXT)
304 {
305 vk::DebugUtilsMessengerCreateInfoEXT debugInfo;
306 debugInfo.messageSeverity =
307 // vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
308 vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
309 vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning;
310
311 debugInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
312 vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
313 vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
314
315 PFN_vkDebugUtilsMessengerCallbackEXT debugInfoCallback =
316 [](
317 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
318 VkDebugUtilsMessageTypeFlagsEXT messageTypes,
319 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
320 void *pUserData) -> VkBool32 {
321 // assert(false);
322 std::cerr << "[DebugInfoCallback] " << pCallbackData->pMessage << std::endl;
323 return VK_FALSE;
324 };
325
326 debugInfo.pfnUserCallback = debugInfoCallback;
327 debugReport = instance.createDebugUtilsMessengerEXT(debugInfo);
328 }
329 #endif
330
331 std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
332 assert(!physicalDevices.empty());
333 physicalDevice = physicalDevices[0];
334
335 const float defaultQueuePriority = 0.0f;
336 vk::DeviceQueueCreateInfo queueCreateInfo;
337 queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
338 queueCreateInfo.queueCount = 1;
339 queueCreateInfo.pQueuePriorities = &defaultQueuePriority;
340
341 std::vector<const char *> deviceExtensions = {
342 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
343 };
344
345 vk::DeviceCreateInfo deviceCreateInfo;
346 deviceCreateInfo.queueCreateInfoCount = 1;
347 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
348 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
349 deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
350
351 device = physicalDevice.createDevice(deviceCreateInfo, nullptr);
352
353 queue = device.getQueue(queueFamilyIndex, 0);
354 }
355
loadDriver()356 std::unique_ptr<vk::DynamicLoader> VulkanTester::loadDriver()
357 {
358 if(LOAD_NATIVE_DRIVER)
359 {
360 return std::make_unique<vk::DynamicLoader>();
361 }
362
363 auto driverPath = findDriverPath();
364 assert(!driverPath.empty());
365
366 if(LOAD_SWIFTSHADER_DIRECTLY)
367 {
368 return std::make_unique<vk::DynamicLoader>(driverPath);
369 }
370
371 // Load SwiftShader via loader
372
373 // Set VK_ICD_FILENAMES env var so it gets picked up by the loading of the ICD driver
374 setIcdFilenames = std::make_unique<ScopedSetIcdFilenames>(driverPath.c_str());
375
376 std::unique_ptr<vk::DynamicLoader> dl;
377 #ifndef VULKAN_HPP_NO_EXCEPTIONS
378 try
379 {
380 dl = std::make_unique<vk::DynamicLoader>();
381 }
382 catch(std::exception &ex)
383 {
384 std::cerr << "vk::DynamicLoader exception: " << ex.what() << std::endl;
385 std::cerr << "Falling back to loading SwiftShader directly (i.e. no validation layers)" << std::endl;
386 dl = std::make_unique<vk::DynamicLoader>(driverPath);
387 }
388 #else
389 dl = std::make_unique<vk::DynamicLoader>();
390 #endif
391
392 return dl;
393 }
394