1 /*
2 * Copyright (c) 2021 The Khronos Group Inc.
3 * Copyright (c) 2021 Valve Corporation
4 * Copyright (c) 2021 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and/or associated documentation files (the "Materials"), to
8 * deal in the Materials without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Materials, and to permit persons to whom the Materials are
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice(s) and this permission notice shall be included in
14 * all copies or substantial portions of the Materials.
15 *
16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 *
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23 * USE OR OTHER DEALINGS IN THE MATERIALS.
24 *
25 * Author: Charles Giessen <charles@lunarg.com>
26 */
27
28 /*
29 * The test_environment is what combines the icd, layer, and shim library into a single object that
30 * test fixtures can create and use. Responsible for loading the libraries and establishing the
31 * channels for tests to talk with the icd's and layers.
32 */
33 #pragma once
34
35 // Must include gtest first to guard against Xlib colliding due to redefinitions of "None" and "Bool"
36
37 #ifdef _MSC_VER
38 #pragma warning(push)
39 /*
40 MSVC warnings 4251 and 4275 have to do with potential dll-interface mismatch
41 between library (gtest) and users. Since we build the gtest library
42 as part of the test build we know that the dll-interface will match and
43 can disable these warnings.
44 */
45 #pragma warning(disable : 4251)
46 #pragma warning(disable : 4275)
47 #endif
48
49 // GTest and Xlib collide due to redefinitions of "None" and "Bool"
50 #ifdef VK_USE_PLATFORM_XLIB_KHR
51 #pragma push_macro("None")
52 #pragma push_macro("Bool")
53 #undef None
54 #undef Bool
55 #endif
56
57 #ifdef _WIN32
58 #ifndef NOMINMAX
59 #define NOMINMAX
60 #endif
61 #endif
62
63 // Use the NDK's header on Android
64 #include "gtest/gtest.h"
65
66 #include "test_util.h"
67
68 #include "shim/shim.h"
69
70 #include "icd/physical_device.h"
71 #include "icd/test_icd.h"
72
73 #include "layer/test_layer.h"
74
75 // handle checking
76 template <typename T>
handle_assert_has_value(T const & handle)77 void handle_assert_has_value(T const& handle) {
78 ASSERT_TRUE(handle != VK_NULL_HANDLE);
79 }
80 template <typename T>
handle_assert_null(T const & handle)81 void handle_assert_null(T const& handle) {
82 ASSERT_TRUE(handle == VK_NULL_HANDLE);
83 }
84 template <typename T>
handle_assert_has_values(std::vector<T> const & handles)85 void handle_assert_has_values(std::vector<T> const& handles) {
86 for (auto const& handle : handles) {
87 ASSERT_TRUE(handle != VK_NULL_HANDLE);
88 }
89 }
90 template <typename T>
handle_assert_no_values(std::vector<T> const & handles)91 void handle_assert_no_values(std::vector<T> const& handles) {
92 for (auto const& handle : handles) {
93 ASSERT_TRUE(handle == VK_NULL_HANDLE);
94 }
95 }
96 template <typename T>
handle_assert_no_values(size_t length,T handles[])97 void handle_assert_no_values(size_t length, T handles[]) {
98 for (size_t i = 0; i < length; i++) {
99 ASSERT_TRUE(handles[i] == VK_NULL_HANDLE);
100 }
101 }
102 template <typename T>
handle_assert_equal(T const & left,T const & right)103 void handle_assert_equal(T const& left, T const& right) {
104 ASSERT_EQ(left, right);
105 }
106 template <typename T>
handle_assert_equal(std::vector<T> const & left,std::vector<T> const & right)107 void handle_assert_equal(std::vector<T> const& left, std::vector<T> const& right) {
108 ASSERT_EQ(left.size(), right.size());
109 for (size_t i = 0; i < left.size(); i++) {
110 ASSERT_EQ(left[i], right[i]);
111 }
112 }
113 template <typename T>
handle_assert_equal(size_t count,T left[],T right[])114 void handle_assert_equal(size_t count, T left[], T right[]) {
115 for (size_t i = 0; i < count; i++) {
116 ASSERT_EQ(left[i], right[i]);
117 }
118 }
119
120 // InstWrapper & DeviceWrapper - used to make creating instances & devices easier test writing
121 struct InstWrapper {
122 InstWrapper(VulkanFunctions& functions, VkAllocationCallbacks* callbacks = nullptr) noexcept;
123 InstWrapper(VulkanFunctions& functions, VkInstance inst, VkAllocationCallbacks* callbacks = nullptr) noexcept;
124 ~InstWrapper() noexcept;
125
126 // Move-nly object
127 InstWrapper(InstWrapper const&) = delete;
128 InstWrapper& operator=(InstWrapper const&) = delete;
129 InstWrapper(InstWrapper&& other) noexcept;
130 InstWrapper& operator=(InstWrapper&&) noexcept;
131
132 // Construct this VkInstance using googletest to assert if it succeeded
133 void CheckCreate(VkResult result_to_check = VK_SUCCESS);
134
135 // Convenience
VkInstanceInstWrapper136 operator VkInstance() { return inst; }
137 VulkanFunctions* operator->() { return functions; }
138
loadInstWrapper139 FromVoidStarFunc load(const char* func_name) { return FromVoidStarFunc(functions->vkGetInstanceProcAddr(inst, func_name)); }
140
141 // Enumerate physical devices using googletest to assert if it succeeded
142 std::vector<VkPhysicalDevice> GetPhysDevs(VkResult result_to_check = VK_SUCCESS); // query all physical devices
143 std::vector<VkPhysicalDevice> GetPhysDevs(uint32_t phys_dev_count,
144 VkResult result_to_check = VK_SUCCESS); // query only phys_dev_count devices
145 // Enumerate a single physical device using googletest to assert if it succeeded
146 VkPhysicalDevice GetPhysDev(VkResult result_to_check = VK_SUCCESS);
147
148 VulkanFunctions* functions = nullptr;
149 VkInstance inst = VK_NULL_HANDLE;
150 VkAllocationCallbacks* callbacks = nullptr;
151 InstanceCreateInfo create_info{};
152 };
153
154 std::vector<VkExtensionProperties> EnumerateDeviceExtensions(InstWrapper const& inst, VkPhysicalDevice physical_device);
155
156 struct DeviceWrapper {
157 DeviceWrapper(InstWrapper& inst_wrapper, VkAllocationCallbacks* callbacks = nullptr) noexcept;
158 DeviceWrapper(VulkanFunctions& functions, VkDevice device, VkAllocationCallbacks* callbacks = nullptr) noexcept;
159 ~DeviceWrapper() noexcept;
160
161 // Move-only object
162 DeviceWrapper(DeviceWrapper const&) = delete;
163 DeviceWrapper& operator=(DeviceWrapper const&) = delete;
164 DeviceWrapper(DeviceWrapper&&) noexcept;
165 DeviceWrapper& operator=(DeviceWrapper&&) noexcept;
166
167 // Construct this VkDevice using googletest to assert if it succeeded
168 void CheckCreate(VkPhysicalDevice physical_device, VkResult result_to_check = VK_SUCCESS);
169
170 // Convenience
VkDeviceDeviceWrapper171 operator VkDevice() { return dev; }
VkDeviceDeviceWrapper172 operator VkDevice() const { return dev; }
173 VulkanFunctions* operator->() { return functions; }
174
loadDeviceWrapper175 FromVoidStarFunc load(const char* func_name) { return FromVoidStarFunc(functions->vkGetDeviceProcAddr(dev, func_name)); }
176
177 VulkanFunctions* functions = nullptr;
178 VkDevice dev = VK_NULL_HANDLE;
179 VkAllocationCallbacks* callbacks = nullptr;
180 DeviceCreateInfo create_info{};
181 };
182
183 struct DebugUtilsLogger {
DebugUtilsMessengerLoggerCallbackDebugUtilsLogger184 static VkBool32 VKAPI_PTR DebugUtilsMessengerLoggerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
185 VkDebugUtilsMessageTypeFlagsEXT messageTypes,
186 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
187 void* pUserData) {
188 DebugUtilsLogger* debug = reinterpret_cast<DebugUtilsLogger*>(pUserData);
189 debug->returned_output += pCallbackData->pMessage;
190 debug->returned_output += '\n';
191 return VK_FALSE;
192 }
193 DebugUtilsLogger(VkDebugUtilsMessageSeverityFlagsEXT severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
194 VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
195 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
196 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
197 returned_output.reserve(4096); // output can be very noisy, reserving should help prevent many small allocations
198 create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
199 create_info.pNext = nullptr;
200 create_info.messageSeverity = severity;
201 create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
202 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
203 create_info.pfnUserCallback = DebugUtilsMessengerLoggerCallback;
204 create_info.pUserData = this;
205 }
206
207 // Immoveable object
208 DebugUtilsLogger(DebugUtilsLogger const&) = delete;
209 DebugUtilsLogger& operator=(DebugUtilsLogger const&) = delete;
210 DebugUtilsLogger(DebugUtilsLogger&&) = delete;
211 DebugUtilsLogger& operator=(DebugUtilsLogger&&) = delete;
212 // Find a string in the log output
findDebugUtilsLogger213 bool find(std::string const& search_text) const { return returned_output.find(search_text) != std::string::npos; }
214 // Clear the log
clearDebugUtilsLogger215 void clear() { returned_output.clear(); }
getDebugUtilsLogger216 VkDebugUtilsMessengerCreateInfoEXT* get() noexcept { return &create_info; }
217 VkDebugUtilsMessengerCreateInfoEXT create_info{};
218 std::string returned_output;
219 };
220
221 struct DebugUtilsWrapper {
DebugUtilsWrapperDebugUtilsWrapper222 DebugUtilsWrapper() noexcept {}
223 DebugUtilsWrapper(InstWrapper& inst_wrapper,
224 VkDebugUtilsMessageSeverityFlagsEXT severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
225 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
226 VkAllocationCallbacks* callbacks = nullptr)
loggerDebugUtilsWrapper227 : logger(severity), inst(inst_wrapper.inst), callbacks(callbacks) {
228 vkCreateDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
229 inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkCreateDebugUtilsMessengerEXT"));
230 vkDestroyDebugUtilsMessengerEXT = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
231 inst_wrapper.functions->vkGetInstanceProcAddr(inst_wrapper.inst, "vkDestroyDebugUtilsMessengerEXT"));
232 };
~DebugUtilsWrapperDebugUtilsWrapper233 ~DebugUtilsWrapper() noexcept {
234 if (messenger) {
235 vkDestroyDebugUtilsMessengerEXT(inst, messenger, callbacks);
236 }
237 }
238 // Immoveable object
239 DebugUtilsWrapper(DebugUtilsWrapper const&) = delete;
240 DebugUtilsWrapper& operator=(DebugUtilsWrapper const&) = delete;
241 DebugUtilsWrapper(DebugUtilsWrapper&&) = delete;
242 DebugUtilsWrapper& operator=(DebugUtilsWrapper&&) = delete;
243
findDebugUtilsWrapper244 bool find(std::string const& search_text) { return logger.find(search_text); }
getDebugUtilsWrapper245 VkDebugUtilsMessengerCreateInfoEXT* get() noexcept { return logger.get(); }
246
247 DebugUtilsLogger logger;
248 VkInstance inst = VK_NULL_HANDLE;
249 VkAllocationCallbacks* callbacks = nullptr;
250 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr;
251 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr;
252 VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
253 };
254
255 VkResult CreateDebugUtilsMessenger(DebugUtilsWrapper& debug_utils);
256
257 // Helper that adds the debug utils extension name and sets the pNext chain up
258 // NOTE: Ignores existing pNext chains
259 void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsLogger& logger);
260 void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsWrapper& wrapper);
261
262 struct FrameworkEnvironment; // forward declaration
263
264 struct PlatformShimWrapper {
265 PlatformShimWrapper(std::vector<fs::FolderManager>* folders, bool enable_log) noexcept;
266 ~PlatformShimWrapper() noexcept;
267 PlatformShimWrapper(PlatformShimWrapper const&) = delete;
268 PlatformShimWrapper& operator=(PlatformShimWrapper const&) = delete;
269
270 // Convenience
271 PlatformShim* operator->() { return platform_shim; }
272
273 LibraryWrapper shim_library;
274 PlatformShim* platform_shim;
275 };
276
277 struct TestICDHandle {
278 TestICDHandle() noexcept;
279 TestICDHandle(fs::path const& icd_path) noexcept;
280 TestICD& reset_icd() noexcept;
281 TestICD& get_test_icd() noexcept;
282 fs::path get_icd_full_path() noexcept;
283 fs::path get_icd_manifest_path() noexcept;
284
285 // Must use statically
286 LibraryWrapper icd_library;
287 GetTestICDFunc proc_addr_get_test_icd = nullptr;
288 GetNewTestICDFunc proc_addr_reset_icd = nullptr;
289 fs::path manifest_path;
290 };
291 struct TestLayerHandle {
292 TestLayerHandle() noexcept;
293 TestLayerHandle(fs::path const& layer_path) noexcept;
294 TestLayer& reset_layer() noexcept;
295 TestLayer& get_test_layer() noexcept;
296 fs::path get_layer_full_path() noexcept;
297 fs::path get_layer_manifest_path() noexcept;
298
299 // Must use statically
300 LibraryWrapper layer_library;
301 GetTestLayerFunc proc_addr_get_test_layer = nullptr;
302 GetNewTestLayerFunc proc_addr_reset_layer = nullptr;
303 fs::path manifest_path;
304 };
305
306 enum class ManifestDiscoveryType {
307 generic, // put the manifest in the regular locations
308 none, // don't add to regular locations - eg D3DKMT
309 env_var, // use the corresponding env-var for it
310 add_env_var, // use the corresponding add-env-var for it
311 override_folder, // add to a special folder for the override layer to use
312 windows_app_package, // let the app package search find it
313 };
314
315 struct TestICDDetails {
TestICDDetailsTestICDDetails316 TestICDDetails(ManifestICD icd_manifest) noexcept : icd_manifest(icd_manifest) {}
317 TestICDDetails(fs::path icd_path, uint32_t api_version = VK_API_VERSION_1_0) noexcept {
318 icd_manifest.set_lib_path(icd_path.str()).set_api_version(api_version);
319 }
320 BUILDER_VALUE(TestICDDetails, ManifestICD, icd_manifest, {});
321 BUILDER_VALUE(TestICDDetails, std::string, json_name, "test_icd");
322 BUILDER_VALUE(TestICDDetails, ManifestDiscoveryType, discovery_type, ManifestDiscoveryType::generic);
323 BUILDER_VALUE(TestICDDetails, bool, is_fake, false);
324 };
325
326 struct TestLayerDetails {
TestLayerDetailsTestLayerDetails327 TestLayerDetails(ManifestLayer layer_manifest, const std::string& json_name) noexcept
328 : layer_manifest(layer_manifest), json_name(json_name) {}
329 BUILDER_VALUE(TestLayerDetails, ManifestLayer, layer_manifest, {});
330 BUILDER_VALUE(TestLayerDetails, std::string, json_name, "test_layer");
331 BUILDER_VALUE(TestLayerDetails, ManifestDiscoveryType, discovery_type, ManifestDiscoveryType::generic);
332 BUILDER_VALUE(TestLayerDetails, bool, is_fake, false);
333 };
334
335 enum class ManifestLocation {
336 null = 0,
337 driver = 1,
338 driver_env_var = 2,
339 explicit_layer = 3,
340 explicit_layer_env_var = 4,
341 explicit_layer_add_env_var = 5,
342 implicit_layer = 6,
343 override_layer = 7,
344 windows_app_package = 8,
345 };
346
347 struct FrameworkEnvironment {
348 FrameworkEnvironment() noexcept; // default is to enable VK_LOADER_DEBUG=all
349 FrameworkEnvironment(bool enable_log) noexcept;
350
351 void add_icd(TestICDDetails icd_details) noexcept;
352 void add_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
353 void add_implicit_layer(TestLayerDetails layer_details) noexcept;
354 void add_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
355 void add_explicit_layer(TestLayerDetails layer_details) noexcept;
356 void add_fake_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
357 void add_fake_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
358
359 TestICD& get_test_icd(size_t index = 0) noexcept;
360 TestICD& reset_icd(size_t index = 0) noexcept;
361 fs::path get_test_icd_path(size_t index = 0) noexcept;
362 fs::path get_icd_manifest_path(size_t index = 0) noexcept;
363
364 TestLayer& get_test_layer(size_t index = 0) noexcept;
365 TestLayer& reset_layer(size_t index = 0) noexcept;
366 fs::path get_test_layer_path(size_t index = 0) noexcept;
367 fs::path get_layer_manifest_path(size_t index = 0) noexcept;
368
369 fs::FolderManager& get_folder(ManifestLocation location) noexcept;
370
371 PlatformShimWrapper platform_shim;
372 std::vector<fs::FolderManager> folders;
373
374 DebugUtilsLogger debug_log;
375 VulkanFunctions vulkan_functions;
376
377 std::vector<TestICDHandle> icds;
378 std::vector<TestLayerHandle> layers;
379
380 std::string env_var_vk_icd_filenames;
381 std::string add_env_var_vk_icd_filenames;
382
383 std::string env_var_vk_layer_paths;
384 std::string add_env_var_vk_layer_paths;
385
386 private:
387 void add_layer_impl(TestLayerDetails layer_details, ManifestCategory category);
388 };
389
390 // The following helpers setup an icd with the required extensions and setting to use with WSI
391 // By default they use whatever the set VK_USE_PLATFORM_XXX macros define
392 void setup_WSI_in_ICD(TestICD& icd);
393 void setup_WSI_in_create_instance(InstWrapper& inst);
394 // api_selection: optionally provide a VK_USE_PLATFORM_XXX string to select which API to create a surface with
395 // Note: MUST provide api_selection on platforms with multiple viable API's, such as linux and MacOS
396 VkSurfaceKHR create_surface(InstWrapper& inst, const char* api_selection = nullptr);
397