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 "./build/vk_swiftshader.dll",
173 "./vk_swiftshader.dll" };
174 # else
175 return { "./build/Release_Win32/vk_swiftshader.dll",
176 "./build/Release/vk_swiftshader.dll",
177 "./build/RelWithDebInfo/vk_swiftshader.dll",
178 "./build/vk_swiftshader.dll",
179 "./vk_swiftshader.dll" };
180 # endif
181 # else
182 # if defined(_WIN64)
183 return { "./build/Debug_x64/vk_swiftshader.dll",
184 "./build/Debug/vk_swiftshader.dll",
185 "./build/vk_swiftshader.dll",
186 "./vk_swiftshader.dll" };
187 # else
188 return { "./build/Debug_Win32/vk_swiftshader.dll",
189 "./build/Debug/vk_swiftshader.dll",
190 "./build/vk_swiftshader.dll",
191 "./vk_swiftshader.dll" };
192 # endif
193 # endif
194 #elif OS_MAC
195 return { "./build/Darwin/libvk_swiftshader.dylib",
196 "swiftshader/libvk_swiftshader.dylib",
197 "libvk_swiftshader.dylib" };
198 #elif OS_LINUX
199 return { "./build/Linux/libvk_swiftshader.so",
200 "swiftshader/libvk_swiftshader.so",
201 "./libvk_swiftshader.so",
202 "libvk_swiftshader.so" };
203 #elif OS_ANDROID || OS_FUCHSIA
204 return
205 {
206 "libvk_swiftshader.so"
207 }
208 #else
209 # error Unimplemented platform
210 return {};
211 #endif
212 }
213
fileExists(const char * path)214 bool fileExists(const char *path)
215 {
216 std::ifstream f(path);
217 return f.good();
218 }
219
findDriverPath()220 std::string findDriverPath()
221 {
222 for(auto &path : getDriverPaths())
223 {
224 if(fileExists(path))
225 return path;
226 }
227
228 #if(OS_LINUX || OS_ANDROID || OS_FUCHSIA)
229 // On Linux-based OSes, the lib path may be resolved by dlopen
230 for(auto &path : getDriverPaths())
231 {
232 auto lib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
233 if(lib)
234 {
235 char libPath[2048] = { '\0' };
236 dlinfo(lib, RTLD_DI_ORIGIN, libPath);
237 dlclose(lib);
238 return std::string{ libPath } + "/" + path;
239 }
240 }
241 #endif
242
243 return {};
244 }
245
246 } // namespace
247
248 VulkanTester::VulkanTester() = default;
249
~VulkanTester()250 VulkanTester::~VulkanTester()
251 {
252 device.waitIdle();
253 device.destroy(nullptr);
254 if(debugReport) instance.destroy(debugReport);
255 instance.destroy(nullptr);
256 }
257
initialize()258 void VulkanTester::initialize()
259 {
260 dl = loadDriver();
261 assert(dl && dl->success());
262
263 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
264 VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
265
266 vk::InstanceCreateInfo instanceCreateInfo;
267 std::vector<const char *> extensionNames
268 {
269 VK_KHR_SURFACE_EXTENSION_NAME,
270 #if USE_HEADLESS_SURFACE
271 VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
272 #endif
273 #if defined(VK_USE_PLATFORM_WIN32_KHR)
274 VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
275 #endif
276 };
277 #if ENABLE_VALIDATION_LAYERS
278 extensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
279 #endif
280
281 std::vector<const char *> layerNames;
282 #if ENABLE_VALIDATION_LAYERS
283 auto addLayerIfAvailable = [](std::vector<const char *> &layers, const char *layer) {
284 static auto layerProperties = vk::enumerateInstanceLayerProperties();
285 if(std::find_if(layerProperties.begin(), layerProperties.end(), [layer](auto &lp) {
286 return strcmp(layer, lp.layerName) == 0;
287 }) != layerProperties.end())
288 {
289 // std::cout << "Enabled layer: " << layer << std::endl;
290 layers.push_back(layer);
291 }
292 };
293
294 addLayerIfAvailable(layerNames, "VK_LAYER_KHRONOS_validation");
295 addLayerIfAvailable(layerNames, "VK_LAYER_LUNARG_standard_validation");
296 #endif
297
298 instanceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
299 instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(extensionNames.size());
300 instanceCreateInfo.ppEnabledLayerNames = layerNames.data();
301 instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(layerNames.size());
302
303 instance = vk::createInstance(instanceCreateInfo, nullptr);
304 VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
305
306 #if ENABLE_VALIDATION_LAYERS
307 if(VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateDebugUtilsMessengerEXT)
308 {
309 vk::DebugUtilsMessengerCreateInfoEXT debugInfo;
310 debugInfo.messageSeverity =
311 // vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
312 vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
313 vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning;
314
315 debugInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
316 vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
317 vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
318
319 PFN_vkDebugUtilsMessengerCallbackEXT debugInfoCallback =
320 [](
321 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
322 VkDebugUtilsMessageTypeFlagsEXT messageTypes,
323 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
324 void *pUserData) -> VkBool32 {
325 // assert(false);
326 std::cerr << "[DebugInfoCallback] " << pCallbackData->pMessage << std::endl;
327 return VK_FALSE;
328 };
329
330 debugInfo.pfnUserCallback = debugInfoCallback;
331 debugReport = instance.createDebugUtilsMessengerEXT(debugInfo);
332 }
333 #endif
334
335 std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
336 assert(!physicalDevices.empty());
337 physicalDevice = physicalDevices[0];
338
339 const float defaultQueuePriority = 0.0f;
340 vk::DeviceQueueCreateInfo queueCreateInfo;
341 queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
342 queueCreateInfo.queueCount = 1;
343 queueCreateInfo.pQueuePriorities = &defaultQueuePriority;
344
345 std::vector<const char *> deviceExtensions = {
346 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
347 };
348
349 vk::DeviceCreateInfo deviceCreateInfo;
350 deviceCreateInfo.queueCreateInfoCount = 1;
351 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
352 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
353 deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
354
355 device = physicalDevice.createDevice(deviceCreateInfo, nullptr);
356
357 queue = device.getQueue(queueFamilyIndex, 0);
358 }
359
loadDriver()360 std::unique_ptr<vk::DynamicLoader> VulkanTester::loadDriver()
361 {
362 if(LOAD_NATIVE_DRIVER)
363 {
364 return std::make_unique<vk::DynamicLoader>();
365 }
366
367 auto driverPath = findDriverPath();
368 assert(!driverPath.empty());
369
370 if(LOAD_SWIFTSHADER_DIRECTLY)
371 {
372 return std::make_unique<vk::DynamicLoader>(driverPath);
373 }
374
375 // Load SwiftShader via loader
376
377 // Set VK_ICD_FILENAMES env var so it gets picked up by the loading of the ICD driver
378 setIcdFilenames = std::make_unique<ScopedSetIcdFilenames>(driverPath.c_str());
379
380 std::unique_ptr<vk::DynamicLoader> dl;
381 #ifndef VULKAN_HPP_NO_EXCEPTIONS
382 try
383 {
384 dl = std::make_unique<vk::DynamicLoader>();
385 }
386 catch(std::exception &ex)
387 {
388 std::cerr << "vk::DynamicLoader exception: " << ex.what() << std::endl;
389 std::cerr << "Falling back to loading SwiftShader directly (i.e. no validation layers)" << std::endl;
390 dl = std::make_unique<vk::DynamicLoader>(driverPath);
391 }
392 #else
393 dl = std::make_unique<vk::DynamicLoader>();
394 #endif
395
396 return dl;
397 }
398