• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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