xref: /aosp_15_r20/external/swiftshader/tests/VulkanWrapper/VulkanTester.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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