From 4f79db3afe55fff14ecf2b8b26f1a2bdc5b2dcf5 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Fri, 22 Jul 2022 10:04:04 -0600 Subject: [PATCH] Refactor unknown function tests Allow multiple layers to intercept an unknown function and then create a single test which tests many common combinations. --- tests/framework/icd/physical_device.h | 7 +- tests/framework/icd/test_icd.cpp | 8 +- tests/framework/icd/test_icd.h | 3 +- tests/framework/layer/test_layer.cpp | 69 +- tests/framework/layer/test_layer.h | 29 +- tests/framework/test_environment.h | 13 +- tests/framework/test_util.h | 11 +- tests/loader_threading_tests.cpp | 10 +- tests/loader_unknown_ext_tests.cpp | 1338 +++++++++++++++++++++++++-------- tests/loader_version_tests.cpp | 73 +- 10 files changed, 1165 insertions(+), 396 deletions(-) diff --git a/tests/framework/icd/physical_device.h b/tests/framework/icd/physical_device.h index cd0c97f..fbe5cb1 100644 --- a/tests/framework/icd/physical_device.h +++ b/tests/framework/icd/physical_device.h @@ -69,6 +69,11 @@ struct PhysicalDevice { std::vector> queue_handles; + // Unknown physical device functions. Add a `VulkanFunction` to this list which will be searched in + // vkGetInstanceProcAddr for custom_instance_functions and vk_icdGetPhysicalDeviceProcAddr for custom_physical_device_functions. + // To add unknown device functions, add it to the PhysicalDevice directly (in the known_device_functions member) + BUILDER_VECTOR(PhysicalDevice, VulkanFunction, custom_physical_device_functions, custom_physical_device_function) + // List of function names which are 'known' to the physical device but have test defined implementations // The purpose of this list is so that vkGetDeviceProcAddr returns 'a real function pointer' in tests // without actually implementing any of the logic inside of it. @@ -88,4 +93,4 @@ struct PhysicalDeviceGroup { std::vector physical_device_handles; VkBool32 subset_allocation = false; -}; \ No newline at end of file +}; diff --git a/tests/framework/icd/test_icd.cpp b/tests/framework/icd/test_icd.cpp index c618d68..41790db 100644 --- a/tests/framework/icd/test_icd.cpp +++ b/tests/framework/icd/test_icd.cpp @@ -1241,9 +1241,11 @@ PFN_vkVoidFunction get_physical_device_func(VkInstance instance, const char* pNa return to_vkVoidFunction(test_vkGetPhysicalDeviceToolPropertiesEXT); } - for (auto& func : icd.custom_physical_device_functions) { - if (func.name == pName) { - return to_vkVoidFunction(func.function); + for (auto& phys_dev : icd.physical_devices) { + for (auto& func : phys_dev.custom_physical_device_functions) { + if (func.name == pName) { + return to_vkVoidFunction(func.function); + } } } return nullptr; diff --git a/tests/framework/icd/test_icd.h b/tests/framework/icd/test_icd.h index 14bf455..6acdab1 100644 --- a/tests/framework/icd/test_icd.h +++ b/tests/framework/icd/test_icd.h @@ -80,11 +80,10 @@ struct TestICD { std::vector messenger_handles; std::vector swapchain_handles; - // Unknown instance and physical device functions. Add a `VulkanFunction` to this list which will be searched in + // Unknown instance functions Add a `VulkanFunction` to this list which will be searched in // vkGetInstanceProcAddr for custom_instance_functions and vk_icdGetPhysicalDeviceProcAddr for custom_physical_device_functions. // To add unknown device functions, add it to the PhysicalDevice directly (in the known_device_functions member) BUILDER_VECTOR(TestICD, VulkanFunction, custom_instance_functions, custom_instance_function) - BUILDER_VECTOR(TestICD, VulkanFunction, custom_physical_device_functions, custom_physical_device_function) // Must explicitely state support for the tooling info extension, that way we can control if vkGetInstanceProcAddr returns a // function pointer for vkGetPhysicalDeviceToolPropertiesEXT or vkGetPhysicalDeviceToolProperties (core version) diff --git a/tests/framework/layer/test_layer.cpp b/tests/framework/layer/test_layer.cpp index f4a43bd..0ccb1f6 100644 --- a/tests/framework/layer/test_layer.cpp +++ b/tests/framework/layer/test_layer.cpp @@ -146,6 +146,7 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* VkLayerInstanceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; + PFN_vk_icdGetPhysicalDeviceProcAddr fpGetPhysicalDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetPhysicalDeviceProcAddr; PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); if (fpCreateInstance == NULL) { return VK_ERROR_INITIALIZATION_FAILED; @@ -161,15 +162,28 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* return result; } layer.instance_handle = *pInstance; - layer.next_GetPhysicalDeviceProcAddr = - reinterpret_cast(fpGetInstanceProcAddr(*pInstance, "vk_layerGetPhysicalDeviceProcAddr")); - + if (layer.use_gipa_GetPhysicalDeviceProcAddr) { + layer.next_GetPhysicalDeviceProcAddr = + reinterpret_cast(fpGetInstanceProcAddr(*pInstance, "vk_layerGetPhysicalDeviceProcAddr")); + } else { + layer.next_GetPhysicalDeviceProcAddr = fpGetPhysicalDeviceProcAddr; + } // Init layer's dispatch table using GetInstanceProcAddr of // next layer in the chain. layer_init_instance_dispatch_table(layer.instance_handle, &layer.instance_dispatch_table, fpGetInstanceProcAddr); if (layer.create_instance_callback) result = layer.create_instance_callback(layer); + for (auto& func : layer.custom_physical_device_interception_functions) { + auto next_func = layer.next_GetPhysicalDeviceProcAddr(*pInstance, func.name.c_str()); + layer.custom_dispatch_functions.at(func.name.c_str()) = next_func; + } + + for (auto& func : layer.custom_device_interception_functions) { + auto next_func = layer.next_vkGetInstanceProcAddr(*pInstance, func.name.c_str()); + layer.custom_dispatch_functions.at(func.name.c_str()) = next_func; + } + if (layer.do_spurious_allocations_in_create_instance && pAllocator && pAllocator->pfnAllocation) { layer.spurious_instance_memory_allocation = pAllocator->pfnAllocation(pAllocator->pUserData, 100, 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); @@ -221,6 +235,11 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDevice(VkPhysicalDevice physicalDevi // initialize layer's dispatch table layer_init_device_dispatch_table(device.device_handle, &device.dispatch_table, fpGetDeviceProcAddr); + for (auto& func : layer.custom_device_interception_functions) { + auto next_func = layer.next_vkGetDeviceProcAddr(*pDevice, func.name.c_str()); + layer.custom_dispatch_functions.at(func.name.c_str()) = next_func; + } + if (layer.create_device_callback) { result = layer.create_device_callback(layer); } @@ -453,9 +472,29 @@ FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_layerGetPhysicalDev #endif // trampolines +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_device_func(VkDevice device, const char* pName); +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_device_func_impl(VkDevice device, const char* pName) { + if (string_eq(pName, "vkGetDeviceProcAddr")) return to_vkVoidFunction(get_device_func); + if (string_eq(pName, "vkDestroyDevice")) return to_vkVoidFunction(test_vkDestroyDevice); + + for (auto& func : layer.custom_device_interception_functions) { + if (func.name == pName) { + return to_vkVoidFunction(func.function); + } + } + + for (auto& func : layer.custom_device_implementation_functions) { + if (func.name == pName) { + return to_vkVoidFunction(func.function); + } + } + + return nullptr; +} VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_device_func(VkDevice device, const char* pName) { - if (string_eq(pName, "vkDestroyDevice")) return to_vkVoidFunction(test_vkDestroyDevice); + PFN_vkVoidFunction ret_dev = get_device_func_impl(device, pName); + if (ret_dev != nullptr) return ret_dev; return layer.next_vkGetDeviceProcAddr(device, pName); } @@ -468,7 +507,13 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_physical_device_func(VkInstance ins if (string_eq(pName, "vkEnumeratePhysicalDeviceGroups")) return (PFN_vkVoidFunction)test_vkEnumeratePhysicalDeviceGroups; if (string_eq(pName, "vkGetPhysicalDeviceProperties")) return (PFN_vkVoidFunction)test_vkGetPhysicalDeviceProperties; - for (auto& func : layer.custom_physical_device_functions) { + for (auto& func : layer.custom_physical_device_interception_functions) { + if (func.name == pName) { + return to_vkVoidFunction(func.function); + } + } + + for (auto& func : layer.custom_physical_device_implementation_functions) { if (func.name == pName) { return to_vkVoidFunction(func.function); } @@ -479,8 +524,8 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_physical_device_func(VkInstance ins #endif return nullptr; } - -VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_instance_func(VkInstance instance, const char* pName) { +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_instance_func(VkInstance instance, const char* pName); +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_instance_func_impl(VkInstance instance, const char* pName) { if (pName == nullptr) return nullptr; if (string_eq(pName, "vkGetInstanceProcAddr")) return to_vkVoidFunction(get_instance_func); if (string_eq(pName, "vkEnumerateInstanceLayerProperties")) return to_vkVoidFunction(test_vkEnumerateInstanceLayerProperties); @@ -495,6 +540,16 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_instance_func(VkInstance instance, PFN_vkVoidFunction ret_phys_dev = get_physical_device_func(instance, pName); if (ret_phys_dev != nullptr) return ret_phys_dev; + PFN_vkVoidFunction ret_dev = get_device_func_impl(nullptr, pName); + if (ret_dev != nullptr) return ret_dev; + + return nullptr; +} + +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_instance_func(VkInstance instance, const char* pName) { + PFN_vkVoidFunction ret_dev = get_instance_func_impl(instance, pName); + if (ret_dev != nullptr) return ret_dev; + return layer.next_vkGetInstanceProcAddr(instance, pName); } diff --git a/tests/framework/layer/test_layer.h b/tests/framework/layer/test_layer.h index a99a3bb..2d3571b 100644 --- a/tests/framework/layer/test_layer.h +++ b/tests/framework/layer/test_layer.h @@ -131,7 +131,31 @@ struct TestLayer { BUILDER_VECTOR(TestLayer, VkPhysicalDeviceGroupProperties, removed_physical_device_groups, removed_physical_device_group) BUILDER_VECTOR(TestLayer, VkPhysicalDeviceGroupProperties, added_physical_device_groups, added_physical_device_group) - BUILDER_VECTOR(TestLayer, VulkanFunction, custom_physical_device_functions, custom_physical_device_function) + BUILDER_VECTOR(TestLayer, VulkanFunction, custom_physical_device_implementation_functions, + custom_physical_device_implementation_function) + BUILDER_VECTOR(TestLayer, VulkanFunction, custom_device_implementation_functions, custom_device_implementation_function) + + // Only need a single map for all 'custom' function - assumes that all function names are distinct, IE there cannot be a + // physical device and device level function with the same name + std::unordered_map custom_dispatch_functions; + std::vector custom_physical_device_interception_functions; + TestLayer& add_custom_physical_device_intercept_function(std::string func_name, PFN_vkVoidFunction function) { + custom_physical_device_interception_functions.push_back({func_name, function}); + custom_dispatch_functions[func_name] = nullptr; + return *this; + } + std::vector custom_device_interception_functions; + TestLayer& add_custom_device_interception_function(std::string func_name, PFN_vkVoidFunction function) { + custom_device_interception_functions.push_back({func_name, function}); + custom_dispatch_functions[func_name] = nullptr; + return *this; + } + PFN_vkVoidFunction get_custom_intercept_function(const char* name) { + if (custom_dispatch_functions.count(name) > 0) { + return custom_dispatch_functions.at(name); + } + return nullptr; + } BUILDER_VALUE(TestLayer, bool, do_spurious_allocations_in_create_instance, false) void* spurious_instance_memory_allocation = nullptr; @@ -142,6 +166,9 @@ struct TestLayer { }; std::vector spurious_device_memory_allocations; + // By default query GPDPA from GIPA, don't use value given from pNext + BUILDER_VALUE(TestLayer, bool, use_gipa_GetPhysicalDeviceProcAddr, true) + PFN_vkGetInstanceProcAddr next_vkGetInstanceProcAddr = VK_NULL_HANDLE; PFN_GetPhysicalDeviceProcAddr next_GetPhysicalDeviceProcAddr = VK_NULL_HANDLE; PFN_vkGetDeviceProcAddr next_vkGetDeviceProcAddr = VK_NULL_HANDLE; diff --git a/tests/framework/test_environment.h b/tests/framework/test_environment.h index a925913..dbd9d71 100644 --- a/tests/framework/test_environment.h +++ b/tests/framework/test_environment.h @@ -169,6 +169,7 @@ struct DeviceWrapper { // Convenience operator VkDevice() { return dev; } + operator VkDevice() const { return dev; } VulkanFunctions* operator->() { return functions; } FromVoidStarFunc load(const char* func_name) { return FromVoidStarFunc(functions->vkGetDeviceProcAddr(dev, func_name)); } @@ -303,12 +304,12 @@ struct TestLayerHandle { }; enum class ManifestDiscoveryType { - generic, // put the manifest in the regular locations - none, // don't add to regular locations - eg D3DKMT - env_var, // use the corresponding env-var for it - add_env_var, // use the corresponding add-env-var for it - override_folder, // add to a special folder for the override layer to use - windows_app_package, // let the app package search find it + generic, // put the manifest in the regular locations + none, // don't add to regular locations - eg D3DKMT + env_var, // use the corresponding env-var for it + add_env_var, // use the corresponding add-env-var for it + override_folder, // add to a special folder for the override layer to use + windows_app_package, // let the app package search find it }; struct TestICDDetails { diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h index 70f8cfa..37791c2 100644 --- a/tests/framework/test_util.h +++ b/tests/framework/test_util.h @@ -247,9 +247,9 @@ inline void copy_string_to_char_array(std::string const& src, char* dst, size_t #if defined(WIN32) // Convert an UTF-16 wstring to an UTF-8 string -std::string narrow(const std::wstring &utf16); +std::string narrow(const std::wstring& utf16); // Convert an UTF-8 string to an UTF-16 wstring -std::wstring widen(const std::string &utf8); +std::wstring widen(const std::string& utf8); #endif #if defined(WIN32) @@ -752,11 +752,11 @@ struct VulkanFunctions { VulkanFunctions(); - FromVoidStarFunc load(VkInstance inst, const char* func_name) { + FromVoidStarFunc load(VkInstance inst, const char* func_name) const { return FromVoidStarFunc(vkGetInstanceProcAddr(inst, func_name)); } - FromVoidStarFunc load(VkDevice device, const char* func_name) { + FromVoidStarFunc load(VkDevice device, const char* func_name) const { return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name)); } }; @@ -844,8 +844,9 @@ inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperti struct VulkanFunction { std::string name; - void* function; + PFN_vkVoidFunction function; }; + template bool check_permutation(std::initializer_list expected, std::array const& returned) { if (expected.size() != returned.size()) return false; diff --git a/tests/loader_threading_tests.cpp b/tests/loader_threading_tests.cpp index 946944c..78999a0 100644 --- a/tests/loader_threading_tests.cpp +++ b/tests/loader_threading_tests.cpp @@ -77,14 +77,14 @@ TEST(ThreadingTests, ConcurentGetDeviceProcAddr) { driver.physical_devices.emplace_back("physical_device_0"); driver.physical_devices.back().known_device_functions.push_back( - {"vkCmdBindPipeline", reinterpret_cast(test_vkCmdBindPipeline)}); + {"vkCmdBindPipeline", to_vkVoidFunction(test_vkCmdBindPipeline)}); driver.physical_devices.back().known_device_functions.push_back( - {"vkCmdBindDescriptorSets", reinterpret_cast(test_vkCmdBindDescriptorSets)}); + {"vkCmdBindDescriptorSets", to_vkVoidFunction(test_vkCmdBindDescriptorSets)}); driver.physical_devices.back().known_device_functions.push_back( - {"vkCmdBindVertexBuffers", reinterpret_cast(test_vkCmdBindVertexBuffers)}); + {"vkCmdBindVertexBuffers", to_vkVoidFunction(test_vkCmdBindVertexBuffers)}); driver.physical_devices.back().known_device_functions.push_back( - {"vkCmdBindIndexBuffer", reinterpret_cast(test_vkCmdBindIndexBuffer)}); - driver.physical_devices.back().known_device_functions.push_back({"vkCmdDraw", reinterpret_cast(test_vkCmdDraw)}); + {"vkCmdBindIndexBuffer", to_vkVoidFunction(test_vkCmdBindIndexBuffer)}); + driver.physical_devices.back().known_device_functions.push_back({"vkCmdDraw", to_vkVoidFunction(test_vkCmdDraw)}); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); diff --git a/tests/loader_unknown_ext_tests.cpp b/tests/loader_unknown_ext_tests.cpp index 492eec3..294251a 100644 --- a/tests/loader_unknown_ext_tests.cpp +++ b/tests/loader_unknown_ext_tests.cpp @@ -27,11 +27,24 @@ #include "test_environment.h" #include +#include + +enum class TestConfig { + add_layer_implementation, + add_layer_interception, +}; + +bool has_flag(std::vector const& flags, TestConfig config) { + for (auto const& flag : flags) + if (flag == config) return true; + return false; +} /* - Creates a TestICD with a function unknown to the loader called vkNotRealFuncTEST. The TestICD, when vk_icdGetPhysicalDeviceProcAddr - is called, will return the custom_physical_device_function if the function name matches vkNotRealFuncTEST. The test then calls the - function to verify that the unknown physical device function dispatching is working correctly. + Creates a TestICD with a function unknown to the loader called vkNotRealFuncTEST. The TestICD, when + vk_icdGetPhysicalDeviceProcAddr is called, will return the custom_physical_device_function if the function name matches + vkNotRealFuncTEST. The test then calls the function to verify that the unknown physical device function dispatching is + working correctly. */ template struct custom_functions { @@ -47,56 +60,282 @@ struct custom_functions { }; }; -template -void fill_custom_functions(std::vector& driver_function_list, std::vector& fake_function_names, - FunctionStruct const& funcs, uint32_t function_count, uint32_t function_start = 0) { - for (uint32_t i = function_start; i < function_start + function_count;) { - fake_function_names.push_back(std::string("vkNotIntRealFuncTEST_") + std::to_string(i++)); - driver_function_list.push_back(VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_zero)}); +/* +Functions for testing of layer interception of unknown functions. Note the need to pass a pointer to the layer and the name +of the called function as a parameter, this is necessary to allow a generic layer implementation, as the layer must look up +the function pointer to use. A real layer would store the function pointer in a dedicated structure per-instance/device, but +since the TestLayer is a generic layer, there isn't a fixed list of functions that should be supported. +*/ + +PFN_vkVoidFunction find_custom_func(TestLayer* layer, const char* name) { + if (layer->custom_dispatch_functions.count(name) > 0) { + return layer->custom_dispatch_functions.at(name); + } + return nullptr; +} + +template +struct layer_intercept_functions { + static VKAPI_ATTR uint32_t VKAPI_CALL func_zero(DispatchableHandleType handle, TestLayer* layer, const char* name, uint32_t i) { + auto func = reinterpret_cast(find_custom_func(layer, name)); + if (func == nullptr) return 1337; + return func(handle, layer, name, i + 3); + } + static VKAPI_ATTR uint32_t VKAPI_CALL func_one(DispatchableHandleType handle, TestLayer* layer, const char* name, uint32_t i, + float f) { + auto func = reinterpret_cast(find_custom_func(layer, name)); + if (func == nullptr) return 1337; + return func(handle, layer, name, i + 2, f + 1.f); + } + static VKAPI_ATTR float VKAPI_CALL func_two(DispatchableHandleType handle, TestLayer* layer, const char* name, uint32_t foo, + uint32_t bar, float baz) { + auto func = reinterpret_cast(find_custom_func(layer, name)); + if (func == nullptr) return -1337; + return func(handle, layer, name, foo + 1, bar + 2, baz * 2); + }; + static VKAPI_ATTR int VKAPI_CALL func_three(DispatchableHandleType handle, TestLayer* layer, const char* name, int* ptr_a, + int* ptr_b) { + auto func = reinterpret_cast(find_custom_func(layer, name)); + if (func == nullptr) return -1337; + *ptr_a += 1; + *ptr_b -= 2; + return func(handle, layer, name, ptr_a, ptr_b); + }; + static VKAPI_ATTR float VKAPI_CALL func_four(DispatchableHandleType handle, TestLayer* layer, const char* name, int* ptr_a, + int* ptr_b, int foo, int bar, float k, float l, char a, char b, char c) { + auto func = reinterpret_cast(find_custom_func(layer, name)); + if (func == nullptr) return -1337.f; + return func(handle, layer, name, ptr_a, ptr_b, foo + 4, bar + 5, k + 1, l + 2, 'd', 'e', 'f'); + }; +}; - fake_function_names.push_back(std::string("vkNotIntRealIntFuncTEST_") + std::to_string(i++)); - driver_function_list.push_back(VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_one)}); +template +struct layer_implementation_functions { + static VKAPI_ATTR uint32_t VKAPI_CALL func_zero(DispatchableHandleType device, TestLayer* layer, const char* name, uint32_t i) { + return i * 3; + } + static VKAPI_ATTR uint32_t VKAPI_CALL func_one(DispatchableHandleType device, TestLayer* layer, const char* name, uint32_t i, + float f) { + return static_cast(i * 3 + f * 10.f); + } + static VKAPI_ATTR float VKAPI_CALL func_two(DispatchableHandleType handle, TestLayer* layer, const char* name, uint32_t foo, + uint32_t bar, float baz) { + return baz + foo + bar; + }; + static VKAPI_ATTR int VKAPI_CALL func_three(DispatchableHandleType handle, TestLayer* layer, const char* name, int* ptr_a, + int* ptr_b) { + return *ptr_a + *ptr_b; + }; + static VKAPI_ATTR float VKAPI_CALL func_four(DispatchableHandleType handle, TestLayer* layer, const char* name, int* ptr_a, + int* ptr_b, int foo, int bar, float k, float l, char a, char b, char c) { + return *ptr_a + *ptr_b + foo + bar + k + l + static_cast(a) + static_cast(b) + static_cast(c); + }; +}; - fake_function_names.push_back(std::string("vkIntNotIntRealFloatFuncTEST_") + std::to_string(i++)); - driver_function_list.push_back(VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_two)}); +// Add function_count strings to the func_names vector, starting at function_start place. Essentially a utility for filling +// up a list of names to use later +void add_function_names(std::vector& func_names, uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + func_names.push_back(std::string("vkNotIntRealFuncTEST_") + std::to_string(i++)); + func_names.push_back(std::string("vkNotIntRealIntFuncTEST_") + std::to_string(i++)); + func_names.push_back(std::string("vkIntNotIntRealFloatFuncTEST_") + std::to_string(i++)); + func_names.push_back(std::string("vkNotRealFuncPointerPointerTEST_") + std::to_string(i++)); + func_names.push_back(std::string("vkNotRealFuncTEST_pointer_pointer_int_int_float_float_char_char_char_") + + std::to_string(i++)); + } +} - fake_function_names.push_back(std::string("vkNotRealFuncPointerPointerTEST_") + std::to_string(i++)); - driver_function_list.push_back(VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_three)}); +// Add data to the function_list, which could be a driver or a layer list of implementation functions. +template +void fill_implementation_functions(std::vector& function_list, std::vector& func_names, + FunctionStruct const& funcs, uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + function_list.push_back(VulkanFunction{func_names.at(i++), to_vkVoidFunction(funcs.func_zero)}); + function_list.push_back(VulkanFunction{func_names.at(i++), to_vkVoidFunction(funcs.func_one)}); + function_list.push_back(VulkanFunction{func_names.at(i++), to_vkVoidFunction(funcs.func_two)}); + function_list.push_back(VulkanFunction{func_names.at(i++), to_vkVoidFunction(funcs.func_three)}); + function_list.push_back(VulkanFunction{func_names.at(i++), to_vkVoidFunction(funcs.func_four)}); + } +} - fake_function_names.push_back(std::string("vkNotRealFuncTEST_pointer_pointer_int_int_float_float_char_char_char_") + - std::to_string(i++)); - driver_function_list.push_back(VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_four)}); +// Add device interception functions to a layer. Need to call `add_custom_device_interception_function` since the layer has +// to setup a unordered_map for storing the next function in the chain, and key it based on the name +template +void fill_device_intercept_functions(TestLayer& layer, std::vector& func_names, FunctionStruct const& funcs, + uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + layer.add_custom_device_interception_function(func_names.at(i++), to_vkVoidFunction(funcs.func_zero)); + layer.add_custom_device_interception_function(func_names.at(i++), to_vkVoidFunction(funcs.func_one)); + layer.add_custom_device_interception_function(func_names.at(i++), to_vkVoidFunction(funcs.func_two)); + layer.add_custom_device_interception_function(func_names.at(i++), to_vkVoidFunction(funcs.func_three)); + layer.add_custom_device_interception_function(func_names.at(i++), to_vkVoidFunction(funcs.func_four)); } } +// Add physical device interception functions to a layer. Need to call `add_custom_device_interception_function` since the +// layer has to setup a unordered_map for storing the next function in the chain, and key it based on the name +template +void fill_phys_dev_intercept_functions(TestLayer& layer, std::vector& func_names, FunctionStruct const& funcs, + uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + layer.add_custom_physical_device_intercept_function(func_names.at(i++), to_vkVoidFunction(funcs.func_zero)); + layer.add_custom_physical_device_intercept_function(func_names.at(i++), to_vkVoidFunction(funcs.func_one)); + layer.add_custom_physical_device_intercept_function(func_names.at(i++), to_vkVoidFunction(funcs.func_two)); + layer.add_custom_physical_device_intercept_function(func_names.at(i++), to_vkVoidFunction(funcs.func_three)); + layer.add_custom_physical_device_intercept_function(func_names.at(i++), to_vkVoidFunction(funcs.func_four)); + } +} + template void check_custom_functions(FunctionLoader& loader, ParentType parent, DispatchableHandleType handle, FunctionStruct const& s, - std::vector& fake_function_names, uint32_t function_count, uint32_t function_start = 0) { + std::vector& func_names, uint32_t function_count, uint32_t function_start = 0) { for (uint32_t i = function_start; i < function_start + function_count;) { - decltype(FunctionStruct::func_zero)* returned_func_i = loader.load(parent, fake_function_names.at(i++).c_str()); + decltype(FunctionStruct::func_zero)* returned_func_i = loader.load(parent, func_names.at(i++).c_str()); ASSERT_NE(returned_func_i, nullptr); EXPECT_EQ(returned_func_i(handle, i * 10), i * 10); - decltype(FunctionStruct::func_one)* returned_func_ii = loader.load(parent, fake_function_names.at(i++).c_str()); + decltype(FunctionStruct::func_one)* returned_func_ii = loader.load(parent, func_names.at(i++).c_str()); ASSERT_NE(returned_func_ii, nullptr); EXPECT_EQ(returned_func_ii(handle, i * 10, i * 5), i * 10 + i * 5); - decltype(FunctionStruct::func_two)* returned_func_iif = loader.load(parent, fake_function_names.at(i++).c_str()); + decltype(FunctionStruct::func_two)* returned_func_iif = loader.load(parent, func_names.at(i++).c_str()); ASSERT_NE(returned_func_iif, nullptr); EXPECT_NEAR(returned_func_iif(handle, i * 10, i * 5, 0.1234f), i * 10 + i * 5 + 0.1234f, 0.001); int x = 5; int y = -505; - decltype(FunctionStruct::func_three)* returned_func_pp = loader.load(parent, fake_function_names.at(i++).c_str()); + decltype(FunctionStruct::func_three)* returned_func_pp = loader.load(parent, func_names.at(i++).c_str()); ASSERT_NE(returned_func_pp, nullptr); EXPECT_EQ(returned_func_pp(handle, &x, &y), -500); - decltype(FunctionStruct::func_four)* returned_func_ppiiffccc = loader.load(parent, fake_function_names.at(i++).c_str()); + x = 5; + y = -505; + decltype(FunctionStruct::func_four)* returned_func_ppiiffccc = loader.load(parent, func_names.at(i++).c_str()); ASSERT_NE(returned_func_ppiiffccc, nullptr); EXPECT_NEAR(returned_func_ppiiffccc(handle, &x, &y, 200, 300, 0.123f, 1001.89f, 'a', 'b', 'c'), -500 + 200 + 300 + 0.123 + 1001.89 + 97 + 98 + 99, 0.001f); } } + +template +void check_layer_custom_functions(FunctionLoader& loader, ParentType parent, DispatchableHandleType handle, TestLayer& layer, + FunctionStruct const& s, std::vector& func_names, uint32_t function_count, + uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + decltype(FunctionStruct::func_zero)* returned_func_i = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_i, nullptr); + EXPECT_EQ(returned_func_i(handle, &layer, func_names.at(i).c_str(), i), (i + 3) * 3); + i++; + decltype(FunctionStruct::func_one)* returned_func_if = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_if, nullptr); + EXPECT_EQ(returned_func_if(handle, &layer, func_names.at(i).c_str(), i, i + 1.f), (i + 2) * 3 + (i + 2) * 10); + i++; + + decltype(FunctionStruct::func_two)* returned_func_iif = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_iif, nullptr); + EXPECT_NEAR(returned_func_iif(handle, &layer, func_names.at(i).c_str(), i * 10, i * 5, 0.1234f), + (i * 10 + 1) + (i * 5 + 2) + (0.1234f * 2.f), 0.001); + i++; + + int x = 5 + i; + int y = -505 - i; + decltype(FunctionStruct::func_three)* returned_func_pp = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_pp, nullptr); + EXPECT_EQ(returned_func_pp(handle, &layer, func_names.at(i).c_str(), &x, &y), + (5 + static_cast(i) + 1) + (-505 - static_cast(i) - 2)); + i++; + + x = 5; + y = -505; + decltype(FunctionStruct::func_four)* returned_func_ppiiffccc = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_ppiiffccc, nullptr); + EXPECT_NEAR( + returned_func_ppiiffccc(handle, &layer, func_names.at(i).c_str(), &x, &y, 200, 300, 0.123f, 1001.89f, 'a', 'b', 'c'), + -500 + (200 + 4) + (300 + 5) + (0.123 + 1) + (1001.89 + 2) + 100 + 101 + 102, + 0.001f); // layer changes abc to def + i++; + } +} + +template +void check_layer_custom_functions_no_implementation(FunctionLoader& loader, ParentType parent, DispatchableHandleType handle, + TestLayer& layer, FunctionStruct const& s, std::vector& func_names, + uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + decltype(FunctionStruct::func_zero)* returned_func_i = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_i, nullptr); + EXPECT_EQ(1337U, returned_func_i(handle, &layer, func_names.at(i).c_str(), i)); + i++; + decltype(FunctionStruct::func_one)* returned_func_if = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_if, nullptr); + EXPECT_EQ(1337U, returned_func_if(handle, &layer, func_names.at(i).c_str(), i, i + 1.f)); + i++; + + decltype(FunctionStruct::func_two)* returned_func_iif = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_iif, nullptr); + EXPECT_NEAR(-1337.0, returned_func_iif(handle, &layer, func_names.at(i).c_str(), i * 10, i * 5, 0.1234f), 0.001); + i++; + + int x = 5 + i; + int y = -505 - i; + decltype(FunctionStruct::func_three)* returned_func_pp = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_pp, nullptr); + EXPECT_EQ(-1337, returned_func_pp(handle, &layer, func_names.at(i).c_str(), &x, &y)); + i++; + + x = 5; + y = -505; + decltype(FunctionStruct::func_four)* returned_func_ppiiffccc = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_ppiiffccc, nullptr); + EXPECT_NEAR( + -1337.0, + returned_func_ppiiffccc(handle, &layer, func_names.at(i).c_str(), &x, &y, 200, 300, 0.123f, 1001.89f, 'a', 'b', 'c'), + 0.001); + i++; + } +} + +template +void check_layer_custom_functions_no_interception(FunctionLoader& loader, ParentType parent, DispatchableHandleType handle, + TestLayer& layer, FunctionStruct const& s, std::vector& func_names, + uint32_t function_count, uint32_t function_start = 0) { + for (uint32_t i = function_start; i < function_start + function_count;) { + decltype(FunctionStruct::func_zero)* returned_func_i = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_i, nullptr); + EXPECT_EQ(returned_func_i(handle, &layer, func_names.at(i).c_str(), i), (i)*3); + i++; + decltype(FunctionStruct::func_one)* returned_func_if = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_if, nullptr); + EXPECT_EQ(returned_func_if(handle, &layer, func_names.at(i).c_str(), i, i + 1.f), (i)*3 + (i + 1) * 10); + i++; + + decltype(FunctionStruct::func_two)* returned_func_iif = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_iif, nullptr); + EXPECT_NEAR(returned_func_iif(handle, &layer, func_names.at(i).c_str(), i * 10, i * 5, 0.1234f), + (i * 10) + (i * 5) + (0.1234f), 0.001); + i++; + + int x = 5 + i; + int y = -505 - i; + decltype(FunctionStruct::func_three)* returned_func_pp = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_pp, nullptr); + EXPECT_EQ(returned_func_pp(handle, &layer, func_names.at(i).c_str(), &x, &y), + (5 + static_cast(i)) + (-505 - static_cast(i))); + i++; + + x = 5; + y = -505; + decltype(FunctionStruct::func_four)* returned_func_ppiiffccc = loader.load(parent, func_names.at(i).c_str()); + ASSERT_NE(returned_func_ppiiffccc, nullptr); + EXPECT_NEAR( + returned_func_ppiiffccc(handle, &layer, func_names.at(i).c_str(), &x, &y, 200, 300, 0.123f, 1001.89f, 'a', 'b', 'c'), + -500 + (200) + (300) + (0.123) + (1001.89) + 97 + 98 + 99, 0.001f); + i++; + } +} + using custom_physical_device_functions = custom_functions; +using layer_intercept_physical_device_functions = layer_intercept_functions; +using layer_implementation_physical_device_functions = layer_implementation_functions; TEST(UnknownFunction, PhysicalDeviceFunction) { #if defined(__APPLE__) @@ -104,18 +343,19 @@ TEST(UnknownFunction, PhysicalDeviceFunction) { #endif FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; auto& driver = env.get_test_icd(); - std::vector fake_function_names; + uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; + std::vector function_names; + add_function_names(function_names, function_count); driver.physical_devices.emplace_back("physical_device_0"); - fill_custom_functions(driver.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, - function_count); + fill_implementation_functions(driver.physical_devices.at(0).custom_physical_device_functions, function_names, + custom_physical_device_functions{}, function_count); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); VkPhysicalDevice phys_dev = inst.GetPhysDev(); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev, custom_physical_device_functions{}, fake_function_names, + check_custom_functions(env.vulkan_functions, inst.inst, phys_dev, custom_physical_device_functions{}, function_names, function_count); } @@ -128,7 +368,9 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupport) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); auto& driver_0 = env.get_test_icd(0); auto& driver_1 = env.get_test_icd(1); - std::vector fake_function_names; + uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; + std::vector function_names; + add_function_names(function_names, function_count); // used to identify the GPUs VkPhysicalDeviceProperties props{}; @@ -139,11 +381,11 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupport) { props.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; driver_1.physical_devices.back().set_properties(props); - for (uint32_t i = 0; i < 25; i++) { - fill_custom_functions(driver_0.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10); - fill_custom_functions(driver_1.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10 + 5); + for (uint32_t i = 0; i < function_count / 10; i++) { + fill_implementation_functions(driver_0.physical_devices.at(0).custom_physical_device_functions, function_names, + custom_physical_device_functions{}, 5, i * 10); + fill_implementation_functions(driver_1.physical_devices.at(0).custom_physical_device_functions, function_names, + custom_physical_device_functions{}, 5, i * 10 + 5); } InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); @@ -156,11 +398,11 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupport) { phys_dev_0 = phys_devs[1]; phys_dev_1 = phys_devs[0]; } - for (uint32_t i = 0; i < 25; i++) { - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_0, custom_physical_device_functions{}, fake_function_names, - 5, i * 10); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_1, custom_physical_device_functions{}, fake_function_names, - 5, i * 10 + 5); + for (uint32_t i = 0; i < function_count / 10; i++) { + check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_0, custom_physical_device_functions{}, function_names, 5, + i * 10); + check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_1, custom_physical_device_functions{}, function_names, 5, + i * 10 + 5); } } @@ -174,7 +416,8 @@ TEST(UnknownFunctionDeathTests, PhysicalDeviceFunctionErrorPath) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); auto& driver_0 = env.get_test_icd(0); auto& driver_1 = env.get_test_icd(1); - std::vector fake_function_names; + std::vector function_names; + add_function_names(function_names, 1); // used to identify the GPUs VkPhysicalDeviceProperties props{}; @@ -184,11 +427,11 @@ TEST(UnknownFunctionDeathTests, PhysicalDeviceFunctionErrorPath) { driver_1.physical_devices.emplace_back("physical_device_1"); props.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; driver_1.physical_devices.back().set_properties(props); - fake_function_names.push_back(std::string("vkNotIntRealFuncTEST_0")); + function_names.push_back(std::string("vkNotIntRealFuncTEST_0")); custom_physical_device_functions funcs{}; - driver_0.custom_physical_device_functions.push_back( - VulkanFunction{fake_function_names.back(), reinterpret_cast(funcs.func_zero)}); + driver_0.physical_devices.at(0).custom_physical_device_functions.push_back( + VulkanFunction{function_names.back(), to_vkVoidFunction(funcs.func_zero)}); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); @@ -200,12 +443,12 @@ TEST(UnknownFunctionDeathTests, PhysicalDeviceFunctionErrorPath) { // use the wrong GPU to query the functions, should get 5 errors decltype(custom_physical_device_functions::func_zero)* returned_func_i = - env.vulkan_functions.load(inst.inst, fake_function_names.at(0).c_str()); + env.vulkan_functions.load(inst.inst, function_names.at(0).c_str()); ASSERT_NE(returned_func_i, nullptr); ASSERT_DEATH(returned_func_i(phys_dev_to_use, 0), "Extension vkNotIntRealFuncTEST_0 not supported for this physical device"); } -TEST(UnknownFunction, PhysicalDeviceFunctionWithImplicitLayer) { +TEST(UnknownFunction, PhysicalDeviceFunctionWithImplicitLayerImplementation) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif @@ -213,27 +456,29 @@ TEST(UnknownFunction, PhysicalDeviceFunctionWithImplicitLayer) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; auto& driver = env.get_test_icd(); - std::vector fake_function_names; + std::vector function_names; + add_function_names(function_names, function_count); driver.physical_devices.emplace_back("physical_device_0"); - fill_custom_functions(driver.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, - function_count); env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("DISABLE_ME")), "implicit_layer_unknown_function_intercept.json"); + auto& layer = env.get_test_layer(); + fill_implementation_functions(layer.custom_physical_device_implementation_functions, function_names, + layer_implementation_physical_device_functions{}, function_count); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); VkPhysicalDevice phys_dev = inst.GetPhysDev(); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev, custom_physical_device_functions{}, fake_function_names, - function_count); + check_layer_custom_functions_no_interception(env.vulkan_functions, inst.inst, phys_dev, layer, + layer_implementation_physical_device_functions{}, function_names, function_count); } -TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupportWithImplicitLayer) { +TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupportWithImplicitLayerImplementation) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif @@ -242,7 +487,9 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupportWithImplicitLay env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); auto& driver_0 = env.get_test_icd(0); auto& driver_1 = env.get_test_icd(1); - std::vector fake_function_names; + uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; + std::vector function_names; + add_function_names(function_names, function_count); // used to identify the GPUs VkPhysicalDeviceProperties props{}; @@ -252,11 +499,11 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupportWithImplicitLay driver_1.physical_devices.emplace_back("physical_device_1"); props.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; driver_1.physical_devices.back().set_properties(props); - for (uint32_t i = 0; i < 25; i++) { - fill_custom_functions(driver_0.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10); - fill_custom_functions(driver_1.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10 + 5); + for (uint32_t i = 0; i < function_count / 10; i++) { + fill_implementation_functions(driver_0.physical_devices.at(0).custom_physical_device_functions, function_names, + custom_physical_device_functions{}, 5, i * 10); + fill_implementation_functions(driver_1.physical_devices.at(0).custom_physical_device_functions, function_names, + custom_physical_device_functions{}, 5, i * 10 + 5); } env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -276,11 +523,11 @@ TEST(UnknownFunction, PhysicalDeviceFunctionMultipleDriverSupportWithImplicitLay phys_dev_0 = phys_devs[1]; phys_dev_1 = phys_devs[0]; } - for (uint32_t i = 0; i < 25; i++) { - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_0, custom_physical_device_functions{}, fake_function_names, - 5, i * 10); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_1, custom_physical_device_functions{}, fake_function_names, - 5, i * 10 + 5); + for (uint32_t i = 0; i < function_count / 10; i++) { + check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_0, custom_physical_device_functions{}, function_names, 5, + i * 10); + check_custom_functions(env.vulkan_functions, inst.inst, phys_dev_1, custom_physical_device_functions{}, function_names, 5, + i * 10 + 5); } } @@ -292,410 +539,843 @@ TEST(UnknownFunction, PhysicalDeviceFunctionWithImplicitLayerInterception) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; auto& driver = env.get_test_icd(); - std::vector fake_function_names; - driver.physical_devices.emplace_back("physical_device_0"); + std::vector function_names; + add_function_names(function_names, function_count); + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("DISABLE_ME")), "implicit_layer_unknown_function_intercept.json"); auto& layer = env.get_test_layer(); - fill_custom_functions(layer.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, - function_count); + fill_phys_dev_intercept_functions(layer, function_names, layer_intercept_physical_device_functions{}, function_count); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); VkPhysicalDevice phys_dev = inst.GetPhysDev(); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev, custom_physical_device_functions{}, fake_function_names, - function_count); + check_layer_custom_functions_no_implementation(env.vulkan_functions, inst.inst, phys_dev, layer, + layer_intercept_physical_device_functions{}, function_names, function_count); } -TEST(UnknownFunction, PhysicalDeviceFunctionWithMultipleImplicitLayersInterception) { +TEST(UnknownFunction, PhysicalDeviceFunctionDriverSupportWithImplicitLayerInterception) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); auto& driver = env.get_test_icd(); - std::vector fake_function_names; - + uint32_t function_count = 100; + std::vector function_names; + add_function_names(function_names, function_count); driver.physical_devices.emplace_back("physical_device_0"); - - env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept_0") - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) - .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept_0.json"); - auto& layer_0 = env.get_test_layer(); + fill_implementation_functions(driver.physical_devices.at(0).custom_physical_device_functions, function_names, + layer_implementation_physical_device_functions{}, function_count); env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept_1") + .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept_1.json"); - auto& layer_1 = env.get_test_layer(); - for (uint32_t i = 0; i < 25; i++) { - fill_custom_functions(layer_0.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10); - fill_custom_functions(layer_1.custom_physical_device_functions, fake_function_names, custom_physical_device_functions{}, 5, - i * 10 + 5); - } + "implicit_layer_unknown_function_intercept.json"); + auto& layer = env.get_test_layer(); + fill_phys_dev_intercept_functions(layer, function_names, layer_intercept_physical_device_functions{}, function_count); + InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); VkPhysicalDevice phys_dev = inst.GetPhysDev(); - check_custom_functions(env.vulkan_functions, inst.inst, phys_dev, custom_physical_device_functions{}, fake_function_names, 250); + check_layer_custom_functions(env.vulkan_functions, inst.inst, phys_dev, layer, layer_intercept_physical_device_functions{}, + function_names, function_count); } -using custom_device_functions = custom_functions; - -TEST(UnknownFunction, DeviceFunctionFromGetInstanceProcAddr) { +TEST(UnknownFunction, PhysicalDeviceFunctionWithMultipleImplicitLayersInterception) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - std::vector fake_function_names; - - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_device_functions{}, - function_count); - - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); - - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); - check_custom_functions(env.vulkan_functions, inst.inst, dev.dev, custom_device_functions{}, fake_function_names, - function_count); -} - -TEST(UnknownFunction, DeviceFunctionFromGetInstanceProcAddrWithImplicitLayer) { -#if defined(__APPLE__) - GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; -#endif - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); + std::vector function_names; uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; - auto& driver = env.get_test_icd(); + add_function_names(function_names, function_count); driver.physical_devices.emplace_back("physical_device_0"); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_device_functions{}, - function_count); env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") + .set_name("VK_LAYER_implicit_layer_unknown_function_intercept_0") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept.json"); - - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); - - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); - check_custom_functions(env.vulkan_functions, inst.inst, dev.dev, custom_device_functions{}, fake_function_names, - function_count); -} - -TEST(UnknownFunction, DeviceFunctionFromGetDeviceProcAddr) { - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_device_functions{}, - function_count); - - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); - - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); - check_custom_functions(env.vulkan_functions, dev.dev, dev.dev, custom_device_functions{}, fake_function_names, function_count); -} - -TEST(UnknownFunction, DeviceFunctionFromGetDeviceProcAddrWithImplicitLayer) { - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_device_functions{}, - function_count); + "implicit_layer_unknown_function_intercept_0.json"); + auto& layer_0 = env.get_test_layer(0); + layer_0.set_use_gipa_GetPhysicalDeviceProcAddr(true); env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") + .set_name("VK_LAYER_implicit_layer_unknown_function_intercept_1") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept.json"); - + "implicit_layer_unknown_function_intercept_1.json"); + auto& layer_1 = env.get_test_layer(1); + layer_1.set_use_gipa_GetPhysicalDeviceProcAddr(false); + for (uint32_t i = 0; i < function_count / 10; i++) { + fill_implementation_functions(driver.physical_devices.at(0).custom_physical_device_functions, function_names, + layer_implementation_physical_device_functions{}, 5, i * 10); + fill_phys_dev_intercept_functions(layer_0, function_names, layer_intercept_physical_device_functions{}, 5, i * 10); + fill_phys_dev_intercept_functions(layer_1, function_names, layer_intercept_physical_device_functions{}, 5, i * 10 + 5); + } InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); - check_custom_functions(env.vulkan_functions, dev.dev, dev.dev, custom_device_functions{}, fake_function_names, function_count); + VkPhysicalDevice phys_dev = inst.GetPhysDev(); + for (uint32_t i = 0; i < function_count / 10; i++) { + check_layer_custom_functions(env.vulkan_functions, inst.inst, phys_dev, layer_0, + layer_intercept_physical_device_functions{}, function_names, 5, i * 10); + check_layer_custom_functions_no_implementation(env.vulkan_functions, inst.inst, phys_dev, layer_1, + layer_intercept_physical_device_functions{}, function_names, 5, i * 10 + 5); + } } -using custom_command_buffer_functions = custom_functions; - -TEST(UnknownFunction, CommandBufferFunctionFromGetDeviceProcAddr) { - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; +template +ParentType get_parent_type(InstWrapper const& inst, DeviceWrapper const& dev); - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, - custom_command_buffer_functions{}, function_count); +template <> +VkInstance get_parent_type(InstWrapper const& inst, DeviceWrapper const& dev) { + return inst.inst; +} +template <> +VkDevice get_parent_type(InstWrapper const& inst, DeviceWrapper const& dev) { + return dev.dev; +} - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); +template +DispatchableHandleType get_dispatch_handle(FrameworkEnvironment& env, DeviceWrapper const& dev, + std::vector const& flags); - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); +template <> +VkDevice get_dispatch_handle(FrameworkEnvironment& env, DeviceWrapper const& dev, std::vector const& flags) { + return dev.dev; +} - DeviceFunctions funcs{env.vulkan_functions, dev}; +template <> +VkCommandBuffer get_dispatch_handle(FrameworkEnvironment& env, DeviceWrapper const& dev, + std::vector const& flags) { VkCommandPool command_pool; VkCommandPoolCreateInfo pool_create_info{}; + DeviceFunctions funcs{env.vulkan_functions, dev}; funcs.vkCreateCommandPool(dev, &pool_create_info, nullptr, &command_pool); VkCommandBuffer command_buffer; VkCommandBufferAllocateInfo alloc_info{}; alloc_info.commandBufferCount = 1; alloc_info.commandPool = command_pool; funcs.vkAllocateCommandBuffers(dev, &alloc_info, &command_buffer); + return command_buffer; +} - check_custom_functions(env.vulkan_functions, dev.dev, command_buffer, custom_command_buffer_functions{}, fake_function_names, - function_count); +template <> +VkQueue get_dispatch_handle(FrameworkEnvironment& env, DeviceWrapper const& dev, std::vector const& flags) { + DeviceFunctions funcs{env.vulkan_functions, dev.dev}; + VkQueue queue; + funcs.vkGetDeviceQueue(dev, 0, 0, &queue); + return queue; } -TEST(UnknownFunction, CommandBufferFunctionFromGetDeviceProcAddrWithImplicitLayer) { +template +void unknown_function_test_impl(std::vector const& flags) { + using custom_functions_type = custom_functions; + using layer_implementation_functions_type = layer_implementation_functions; + using layer_intercept_functions_type = layer_intercept_functions; + FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; + uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; + auto& driver = env.get_test_icd(); driver.physical_devices.emplace_back("physical_device_0"); driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, - custom_command_buffer_functions{}, function_count); - env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) - .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept.json"); + std::vector function_names; + add_function_names(function_names, function_count); + + if (has_flag(flags, TestConfig::add_layer_interception)) { + fill_implementation_functions(driver.physical_devices.back().known_device_functions, function_names, + layer_implementation_functions_type{}, function_count); + } else { + fill_implementation_functions(driver.physical_devices.back().known_device_functions, function_names, + custom_functions_type{}, function_count); + } + TestLayer* layer_ptr = nullptr; + if (has_flag(flags, TestConfig::add_layer_implementation) || has_flag(flags, TestConfig::add_layer_interception)) { + env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ME")), + "implicit_layer_unknown_function_intercept.json"); + layer_ptr = &env.get_test_layer(); + } + if (has_flag(flags, TestConfig::add_layer_implementation) && has_flag(flags, TestConfig::add_layer_interception)) { + for (uint32_t i = 0; i < function_count / 10; i++) { + fill_implementation_functions(layer_ptr->custom_device_implementation_functions, function_names, + layer_implementation_functions_type{}, 5, i * 10); + fill_device_intercept_functions(*layer_ptr, function_names, layer_intercept_functions_type{}, 5, i * 10 + 5); + } + } else if (has_flag(flags, TestConfig::add_layer_implementation)) { + fill_implementation_functions(layer_ptr->custom_device_implementation_functions, function_names, custom_functions_type{}, + function_count); + } else if (has_flag(flags, TestConfig::add_layer_interception)) { + fill_device_intercept_functions(*layer_ptr, function_names, layer_intercept_functions_type{}, function_count); + } InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); DeviceWrapper dev{inst}; + dev.create_info.add_device_queue({}); dev.CheckCreate(inst.GetPhysDev()); + auto dispatch_type = get_dispatch_handle(env, dev, flags); + auto parent_type = get_parent_type(inst, dev); + + if (has_flag(flags, TestConfig::add_layer_implementation) && has_flag(flags, TestConfig::add_layer_interception)) { + for (uint32_t i = 0; i < function_count / 10; i++) { + check_layer_custom_functions_no_interception(env.vulkan_functions, parent_type, dispatch_type, *layer_ptr, + layer_implementation_functions_type{}, function_names, 5, i * 10); + } + } else if (has_flag(flags, TestConfig::add_layer_interception)) { + check_layer_custom_functions(env.vulkan_functions, parent_type, dispatch_type, *layer_ptr, layer_intercept_functions_type{}, + function_names, function_count); + + } else { + check_custom_functions(env.vulkan_functions, parent_type, dispatch_type, custom_functions_type{}, function_names, + function_count); + } +} - DeviceFunctions funcs{env.vulkan_functions, dev}; - VkCommandPool command_pool; - VkCommandPoolCreateInfo pool_create_info{}; - funcs.vkCreateCommandPool(dev, &pool_create_info, nullptr, &command_pool); - VkCommandBuffer command_buffer; - VkCommandBufferAllocateInfo alloc_info{}; - alloc_info.commandBufferCount = 1; - alloc_info.commandPool = command_pool; - funcs.vkAllocateCommandBuffers(dev, &alloc_info, &command_buffer); +// Device - check_custom_functions(env.vulkan_functions, dev.dev, command_buffer, custom_command_buffer_functions{}, fake_function_names, - function_count); +TEST(UnknownFunction, DeviceFromGDPA) { unknown_function_test_impl({}); } + +TEST(UnknownFunction, DeviceFromGDPAWithLayerImplementation) { + unknown_function_test_impl({TestConfig::add_layer_implementation}); } -TEST(UnknownFunction, CommandBufferFunctionFromGetInstanceProcAddr) { +TEST(UnknownFunction, DeviceFromGDPAWithLayerInterception) { + unknown_function_test_impl({TestConfig::add_layer_interception}); +} + +TEST(UnknownFunction, DeviceFromGDPAWithLayerInterceptionAndLayerImplementation) { + unknown_function_test_impl({TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} + +TEST(UnknownFunction, DeviceFromGIPA) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, - custom_command_buffer_functions{}, function_count); + unknown_function_test_impl({}); +} - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); +TEST(UnknownFunction, DeviceFromGIPAWithLayerImplementation) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); +TEST(UnknownFunction, DeviceFromGIPAWithLayerInterception) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} - DeviceFunctions funcs{env.vulkan_functions, dev}; - VkCommandPool command_pool; - VkCommandPoolCreateInfo pool_create_info{}; - funcs.vkCreateCommandPool(dev, &pool_create_info, nullptr, &command_pool); - VkCommandBuffer command_buffer; - VkCommandBufferAllocateInfo alloc_info{}; - alloc_info.commandBufferCount = 1; - alloc_info.commandPool = command_pool; - funcs.vkAllocateCommandBuffers(dev, &alloc_info, &command_buffer); +TEST(UnknownFunction, DeviceFromGIPAWithLayerInterceptionAndLayerImplementation) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} - check_custom_functions(env.vulkan_functions, inst.inst, command_buffer, custom_command_buffer_functions{}, fake_function_names, - function_count); +// Command buffers + +TEST(UnknownFunction, CommandBufferFromGDPA) { unknown_function_test_impl({}); } + +TEST(UnknownFunction, CommandBufferFromGDPAWithLayerImplementation) { + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} + +TEST(UnknownFunction, CommandBufferFromGDPAWithLayerInterception) { + unknown_function_test_impl({TestConfig::add_layer_interception}); } -TEST(UnknownFunction, CommandBufferFunctionFromGetInstanceProcAddrWithImplicitLayer) { +TEST(UnknownFunction, CommandBufferFromGDPAWithLayerInterceptionAndLayerImplementation) { + unknown_function_test_impl( + {TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} + +TEST(UnknownFunction, CommandBufferFromGIPA) { #if defined(__APPLE__) GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; #endif - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = MAX_NUM_UNKNOWN_EXTS; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, - custom_command_buffer_functions{}, function_count); + unknown_function_test_impl({}); +} - env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) - .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept.json"); +TEST(UnknownFunction, CommandBufferFromGIPAWithLayerImplementation) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); +TEST(UnknownFunction, CommandBufferFromGIPAWithLayerInterception) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} - DeviceWrapper dev{inst}; - dev.CheckCreate(inst.GetPhysDev()); +TEST(UnknownFunction, CommandBufferFromGIPAWithLayerInterceptionAndLayerImplementation) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl( + {TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} - DeviceFunctions funcs{env.vulkan_functions, dev}; - VkCommandPool command_pool; - VkCommandPoolCreateInfo pool_create_info{}; - funcs.vkCreateCommandPool(dev, &pool_create_info, nullptr, &command_pool); - VkCommandBuffer command_buffer; - VkCommandBufferAllocateInfo alloc_info{}; - alloc_info.commandBufferCount = 1; - alloc_info.commandPool = command_pool; - funcs.vkAllocateCommandBuffers(dev, &alloc_info, &command_buffer); +// Queues - check_custom_functions(env.vulkan_functions, inst.inst, command_buffer, custom_command_buffer_functions{}, fake_function_names, - function_count); +TEST(UnknownFunction, QueueFromGDPA) { unknown_function_test_impl({}); } + +TEST(UnknownFunction, QueueFromGDPAWithLayerImplementation) { + unknown_function_test_impl({TestConfig::add_layer_implementation}); } -using custom_queue_functions = custom_functions; +TEST(UnknownFunction, QueueFromGDPAWithLayerInterception) { + unknown_function_test_impl({TestConfig::add_layer_interception}); +} -TEST(UnknownFunction, QueueFunctionFromGetDeviceProcAddr) { - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_queue_functions{}, - function_count); +TEST(UnknownFunction, QueueFromGDPAWithLayerInterceptionAndLayerImplementation) { + unknown_function_test_impl({TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); +TEST(UnknownFunction, QueueFromGIPA) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({}); +} - DeviceWrapper dev{inst}; - dev.create_info.add_device_queue({}); - dev.CheckCreate(inst.GetPhysDev()); - VkQueue queue{}; - env.vulkan_functions.vkGetDeviceQueue(dev, 0, 0, &queue); +TEST(UnknownFunction, QueueFromGIPAWithLayer) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); +} - check_custom_functions(env.vulkan_functions, dev.dev, queue, custom_queue_functions{}, fake_function_names, function_count); +TEST(UnknownFunction, QueueFromGIPAWithLayerInterception) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_implementation}); } -TEST(UnknownFunction, QueueFunctionFromGetDeviceProcAddrWithImplicitLayer) { - FrameworkEnvironment env{}; - env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)); - uint32_t function_count = 1000; - auto& driver = env.get_test_icd(); - driver.physical_devices.emplace_back("physical_device_0"); - driver.physical_devices.back().add_queue_family_properties({}); - std::vector fake_function_names; - fill_custom_functions(driver.physical_devices.back().known_device_functions, fake_function_names, custom_queue_functions{}, - function_count); +TEST(UnknownFunction, QueueFromGIPAWithLayerInterceptionAndLayerImplementation) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skip this test as currently macOS doesn't fully support unknown functions."; +#endif + unknown_function_test_impl({TestConfig::add_layer_interception, TestConfig::add_layer_implementation}); +} - env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} - .set_name("VK_LAYER_implicit_layer_unknown_function_intercept") - .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) - .set_disable_environment("DISABLE_ME")), - "implicit_layer_unknown_function_intercept.json"); +/* + The purpose of LayerInterceptData is to provide a place to store data that is accessible inside the interception function. + It works by being a templated type with static variables. Every unique type used creates a new template instantiation, with its own + static variable storage. Thus interception functions that are templated correctly have per-template static data storage at their + disposal, which is used to query the next function in the chain and call down. +*/ - InstWrapper inst{env.vulkan_functions}; - inst.CheckCreate(); +template +struct LayerInterceptData { + static TestLayer* layer; + static const char* name; +}; +template +TestLayer* LayerInterceptData::layer = nullptr; +template +const char* LayerInterceptData::name = nullptr; + +template +struct FunctionZero { + static VKAPI_ATTR uint32_t VKAPI_CALL implementation(DispatchableHandle handle, uint32_t a, uint32_t b) { return a + b; } + + template + static VKAPI_ATTR uint32_t VKAPI_CALL intercept(DispatchableHandle handle, uint32_t a, uint32_t b) { + decltype(implementation)* func = + reinterpret_cast(LayerType::layer->get_custom_intercept_function(LayerType::name)); + if (func == nullptr) return 1337; + return func(handle, a + 3, b + 7); + } - DeviceWrapper dev{inst}; - dev.create_info.add_device_queue({}); - dev.CheckCreate(inst.GetPhysDev()); - VkQueue queue{}; - env.vulkan_functions.vkGetDeviceQueue(dev, 0, 0, &queue); + template + static void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, const char* name, + uint32_t interception_count = 1) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + EXPECT_EQ(returned_func(dispatch_type, 4, 9), (4 + 3 * interception_count) + (9 + 7 * interception_count)); + } + template + static void check_no_implementation(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, + const char* name) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + EXPECT_EQ(returned_func(dispatch_type, 5, 2), 1337U); + } +}; - check_custom_functions(env.vulkan_functions, dev.dev, queue, custom_queue_functions{}, fake_function_names, function_count); -} +template +struct FunctionOne { + static VKAPI_ATTR uint32_t VKAPI_CALL implementation(DispatchableHandle handle, uint32_t a, uint32_t b, char c) { + return a + b + c; + } + + template + static VKAPI_ATTR uint32_t VKAPI_CALL intercept(DispatchableHandle handle, uint32_t a, uint32_t b, char c) { + decltype(implementation)* func = + reinterpret_cast(LayerType::layer->get_custom_intercept_function(LayerType::name)); + if (func == nullptr) return 1337; + return func(handle, a + 2, b + 9, c + 1); + } + + template + static void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, const char* name, + uint32_t interception_count = 1) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + EXPECT_EQ(returned_func(dispatch_type, 12, 17, 'a'), + (12 + 2 * interception_count) + (17 + 9 * interception_count) + ('a' + 1 * interception_count)); + } + template + static void check_no_implementation(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, + const char* name) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + EXPECT_EQ(returned_func(dispatch_type, 1, 516, 'c'), 1337U); + } +}; + +template +struct FunctionTwo { + static VKAPI_ATTR float VKAPI_CALL implementation(DispatchableHandle handle, int* ptr_a, int* ptr_b) { + return 0.123f + *ptr_a + *ptr_b; + } + + template + static VKAPI_ATTR float VKAPI_CALL intercept(DispatchableHandle handle, int* ptr_a, int* ptr_b) { + decltype(implementation)* func = + reinterpret_cast(LayerType::layer->get_custom_intercept_function(LayerType::name)); + if (func == nullptr) return -1337.f; + *ptr_a += 2; + *ptr_b += 5; + return func(handle, ptr_a, ptr_b); + } + + template + static void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, const char* name, + uint32_t interception_count = 1) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + int x = 10, y = 3; + EXPECT_NEAR(returned_func(dispatch_type, &x, &y), 0.123f + (10 + 2 * interception_count) + (3 + 5 * interception_count), + 0.001); + } + template + static void check_no_implementation(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, + const char* name) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + int x = 10, y = 0; + EXPECT_NEAR(returned_func(dispatch_type, &x, &y), -1337.f, 0.001); + } +}; + +template +struct FunctionThree { + static VKAPI_ATTR float VKAPI_CALL implementation(DispatchableHandle handle, int* ptr_a, float* ptr_b, uint32_t c) { + return 0.456f + *ptr_a + *ptr_b + c; + } + + template + static VKAPI_ATTR float VKAPI_CALL intercept(DispatchableHandle handle, int* ptr_a, float* ptr_b, uint32_t c) { + decltype(implementation)* func = + reinterpret_cast(LayerType::layer->get_custom_intercept_function(LayerType::name)); + if (func == nullptr) return -1837.f; + *ptr_a += 55; + *ptr_b += 5.98f; + return func(handle, ptr_a, ptr_b, c + 100); + } + + template + static void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, const char* name, + uint32_t interception_count = 1) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + int x = 96; + float y = 7; + EXPECT_NEAR(returned_func(dispatch_type, &x, &y, 30), + 0.456f + (96 + 55 * interception_count) + (7 + 5.98f * interception_count) + (30 + 100 * interception_count), + 0.001); + } + template + static void check_no_implementation(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, + const char* name) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + int x = 10; + float y = 0; + EXPECT_NEAR(returned_func(dispatch_type, &x, &y, 40), -1837.f, 0.001); + } +}; + +template +struct FunctionFour { + static VKAPI_ATTR VkResult VKAPI_CALL implementation(DispatchableHandle handle, VkPhysicalDeviceLimits* limits, uint32_t* count, + VkExtensionProperties* props) { + limits->nonCoherentAtomSize = 0x0000ABCD0000FEDCU; + if (props == nullptr) { + *count = 5; + return VK_INCOMPLETE; + } else { + for (uint32_t i = 0; i < *count; i++) { + props[i].specVersion = i; + } + return VK_SUCCESS; + } + } + + template + static VKAPI_ATTR VkResult VKAPI_CALL intercept(DispatchableHandle handle, VkPhysicalDeviceLimits* limits, uint32_t* count, + VkExtensionProperties* props) { + decltype(implementation)* func = + reinterpret_cast(LayerType::layer->get_custom_intercept_function(LayerType::name)); + if (func == nullptr) return VK_ERROR_DEVICE_LOST; + VkResult res = func(handle, limits, count, props); + if (props) { + for (uint32_t i = 5; i < *count; i++) { + props[i].specVersion = 1234 + i * 2; + } + } else if (count) { + *count += 1; + } + return res; + } -TEST(UnknownFunction, QueueFunctionFromGetInstanceProcAddr) { + template + static void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, const char* name, + uint32_t interception_count = 1) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + VkPhysicalDeviceLimits limits{}; + uint32_t count = 0; + EXPECT_EQ(returned_func(dispatch_type, &limits, &count, nullptr), VK_INCOMPLETE); + EXPECT_EQ(count, 5 + interception_count); + std::vector props(count, VkExtensionProperties{}); + EXPECT_EQ(returned_func(dispatch_type, &limits, &count, props.data()), VK_SUCCESS); + for (uint32_t i = 0; i < 5; i++) { + EXPECT_EQ(props.at(i).specVersion, i); + } + for (uint32_t i = 5; i < interception_count; i++) { + EXPECT_EQ(props.at(i).specVersion, 1234 + i * 2); // interception should do this + } + } + template + static void check_no_implementation(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type, + const char* name) { + decltype(implementation)* returned_func = loader.load(parent, name); + ASSERT_NE(returned_func, nullptr); + VkPhysicalDeviceLimits limits{}; + EXPECT_EQ(returned_func(dispatch_type, &limits, nullptr, nullptr), VK_ERROR_DEVICE_LOST); + } +}; + +struct UnknownFunction { + std::string name; + + bool has_implementation = false; + std::vector interception_stack; + + UnknownFunction() = default; + UnknownFunction(std::string name) : name(name) {} + + template + void check(VulkanFunctions const& loader, ParentType parent, DispatchableHandle dispatch_type) { + if (has_implementation) { + // Find how many layers intercept this function, stop if any layer 'implements' the function, thus doesn't return + uint32_t intercept_count = 0; + for (auto const& elem : interception_stack) { + if (elem == 1) break; + intercept_count++; + } + FunctionType::Function::check(loader, parent, dispatch_type, name.c_str(), intercept_count); + } else { + FunctionType::Function::check_no_implementation(loader, parent, dispatch_type, name.c_str()); + } + } + + void push_layer_implementation() { interception_stack.push_back(1); } + void push_layer_interception() { interception_stack.push_back(0); } +}; + +// For VkDevice, VkCommandBuffer, & VkQueue +template +struct UnknownFunctionInfo { + using Function = FunctionType; + + static void add_to_driver(UnknownFunction& func, PhysicalDevice& physical_device) { + physical_device.add_device_function({func.name.c_str(), to_vkVoidFunction(Function::implementation)}); + func.has_implementation = true; + } + + template + static void add_to_layer(UnknownFunction& func, TestLayer& layer, LayerStruct intercept_struct) { + LayerInterceptData::layer = &layer; + LayerInterceptData::name = func.name.c_str(); + layer.add_custom_device_interception_function( + func.name, to_vkVoidFunction(&Function::template intercept>)); + func.push_layer_interception(); + } + + static void add_implementation_to_layer(UnknownFunction& func, TestLayer& layer) { + layer.add_custom_device_implementation_function({func.name.c_str(), to_vkVoidFunction(Function::implementation)}); + func.has_implementation = true; + func.push_layer_implementation(); + } +}; + +// Specialization for VkPhysicalDevice + +template +struct UnknownFunctionInfo { + using Function = FunctionType; + + static void add_to_driver(UnknownFunction& func, PhysicalDevice& physical_device) { + physical_device.add_custom_physical_device_function({func.name.c_str(), to_vkVoidFunction(Function::implementation)}); + func.has_implementation = true; + } + + template + static void add_to_layer(UnknownFunction& func, TestLayer& layer, LayerStruct intercept_struct) { + LayerInterceptData::layer = &layer; + LayerInterceptData::name = func.name.c_str(); + layer.add_custom_physical_device_intercept_function( + func.name, to_vkVoidFunction(&Function::template intercept>)); + func.push_layer_interception(); + } + + static void add_implementation_to_layer(UnknownFunction& func, TestLayer& layer) { + layer.add_custom_physical_device_implementation_function({func.name.c_str(), to_vkVoidFunction(Function::implementation)}); + func.has_implementation = true; + func.push_layer_implementation(); + } +}; + +struct Functions { + template