| /* Copyright (c) 2020-2025 The Khronos Group Inc. |
| * Copyright (c) 2020-2025 Valve Corporation |
| * Copyright (c) 2020-2025 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Instanceless tests |
| // Tests of validation of vkCreateInstance and vkDestroyInstance via the pNext debug callback. |
| // |
| // This set of test should ideally be as complete as possible. Most of the VUs are Implicit (i.e. automatically generated), but any |
| // of the parameters could expose a bug or inadequacy in the Loader or the debug extension. |
| // |
| // Note: testing pCreateInfo pointer, the sType of a debug struct, the debug callback pointer, the ppEnabledLayerNames pointer, and |
| // the ppEnabledExtensionNames would require extenally enabled debug layers, so this is currently not performed. |
| // |
| // TODO: VkDebugReportCallbackCreateInfoEXT::flags and VkDebugUtilsMessengerCreateInfoEXT various Flags could theoretically be |
| // tested if the debug extensions are made robust enough |
| |
| #include <memory> |
| #include <thread> |
| #include <vector> |
| |
| #include "../framework/layer_validation_tests.h" |
| |
| static VkInstance dummy_instance; |
| |
| class NegativeInstanceless : public VkLayerTest {}; |
| |
| TEST_F(NegativeInstanceless, InstanceExtensionDependencies) { |
| TEST_DESCRIPTION("Test enabling instance extension without dependencies met."); |
| |
| if (!InstanceExtensionSupported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find required instance extension"; |
| } |
| ASSERT_TRUE(InstanceExtensionSupported(VK_KHR_SURFACE_EXTENSION_NAME)); // Driver should always provide dependencies |
| |
| Monitor().SetDesiredError("VUID-vkCreateInstance-ppEnabledExtensionNames-01388"); |
| m_instance_extension_names.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); |
| const auto ici = GetInstanceCreateInfo(); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(NegativeInstanceless, ExtensionNestedDependency) { |
| TEST_DESCRIPTION("Make sure nested dependency extension logic is being checked"); |
| // VkSwapchainPresentModesCreateInfoEXT is part of VK_EXT_swapchain_maintenance1 |
| // VK_EXT_swapchain_maintenance1 requires VK_EXT_surface_maintenance1 |
| // VK_EXT_surface_maintenance1 requires VK_KHR_get_surface_capabilities2 |
| // |
| // Don't enable VK_KHR_get_surface_capabilities2 |
| SetTargetApiVersion(VK_API_VERSION_1_0); |
| if (!InstanceExtensionSupported(VK_KHR_SURFACE_EXTENSION_NAME) || |
| !InstanceExtensionSupported(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) || |
| !InstanceExtensionSupported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME) || |
| !InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find the required instance extensions"; |
| } |
| m_instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME); |
| m_instance_extension_names.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME); |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| |
| Monitor().SetDesiredError("VUID-vkCreateInstance-ppEnabledExtensionNames-01388"); |
| const auto ici = GetInstanceCreateInfo(); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(NegativeInstanceless, InstanceBadStype) { |
| TEST_DESCRIPTION("Test creating instance with bad sType."); |
| |
| auto ici = GetInstanceCreateInfo(); |
| |
| Monitor().SetDesiredError("VUID-VkInstanceCreateInfo-sType-sType"); |
| ici.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(NegativeInstanceless, InstanceDuplicatePnextStype) { |
| TEST_DESCRIPTION("Test creating instance with duplicate sType in the pNext chain."); |
| |
| if (!InstanceExtensionSupported(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find required instance extension"; |
| } |
| m_instance_extension_names.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME); |
| |
| auto ici = GetInstanceCreateInfo(); |
| |
| Monitor().SetDesiredError("VUID-VkInstanceCreateInfo-sType-unique"); |
| const VkValidationFeaturesEXT duplicate_pnext = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, ici.pNext}; |
| const VkValidationFeaturesEXT first_pnext = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, &duplicate_pnext}; |
| ici.pNext = &first_pnext; |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(NegativeInstanceless, InstanceAppInfoBadStype) { |
| TEST_DESCRIPTION("Test creating instance with invalid sType in VkApplicationInfo."); |
| |
| auto ici = GetInstanceCreateInfo(); |
| |
| VkApplicationInfo bad_app_info = {}; |
| if (ici.pApplicationInfo) bad_app_info = *ici.pApplicationInfo; |
| bad_app_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
| ici.pApplicationInfo = &bad_app_info; |
| |
| Monitor().SetDesiredError("VUID-VkApplicationInfo-sType-sType"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| TEST_F(NegativeInstanceless, InstanceValidationFeaturesBadFlags) { |
| TEST_DESCRIPTION("Test creating instance with invalid flags in VkValidationFeaturesEXT."); |
| |
| if (!InstanceExtensionSupported(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find required instance extension"; |
| } |
| m_instance_extension_names.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME); |
| |
| auto ici = GetInstanceCreateInfo(); |
| |
| // the test framework should not be using VkValidationFeatureEnableEXT itself |
| for (auto traversable_pnext = reinterpret_cast<const VkBaseInStructure*>(ici.pNext); traversable_pnext; |
| traversable_pnext = traversable_pnext->pNext) { |
| ASSERT_NE(traversable_pnext->sType, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT); |
| } |
| |
| VkValidationFeaturesEXT validation_features_template = {}; |
| validation_features_template.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; |
| validation_features_template.pNext = ici.pNext; |
| |
| { |
| const VkValidationFeatureEnableEXT bad_enable = (VkValidationFeatureEnableEXT)0x42; |
| VkValidationFeaturesEXT validation_features = validation_features_template; |
| validation_features.enabledValidationFeatureCount = 1; |
| validation_features.pEnabledValidationFeatures = &bad_enable; |
| ici.pNext = &validation_features; |
| |
| Monitor().SetDesiredError("VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| { |
| const VkValidationFeatureDisableEXT bad_disable = (VkValidationFeatureDisableEXT)0x42; |
| VkValidationFeaturesEXT validation_features = validation_features_template; |
| validation_features.disabledValidationFeatureCount = 1; |
| validation_features.pDisabledValidationFeatures = &bad_disable; |
| ici.pNext = &validation_features; |
| |
| Monitor().SetDesiredError("VUID-VkValidationFeaturesEXT-pDisabledValidationFeatures-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| } |
| |
| TEST_F(NegativeInstanceless, InstanceValidationFlags) { |
| TEST_DESCRIPTION("Test creating instance with invalid VkValidationFlagsEXT."); |
| |
| if (!InstanceExtensionSupported(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Did not find required instance extension"; |
| } |
| m_instance_extension_names.push_back(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME); |
| |
| auto ici = GetInstanceCreateInfo(); |
| |
| // the test framework should not be using VkValidationFlagsEXT itself |
| for (auto traversable_pnext = reinterpret_cast<const VkBaseInStructure*>(ici.pNext); traversable_pnext; |
| traversable_pnext = traversable_pnext->pNext) { |
| ASSERT_NE(traversable_pnext->sType, VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT); |
| } |
| |
| VkValidationFlagsEXT validation_flags_template = {}; |
| validation_flags_template.sType = VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT; |
| validation_flags_template.pNext = ici.pNext; |
| |
| { |
| const VkValidationCheckEXT bad_disable = (VkValidationCheckEXT)0x42; |
| VkValidationFlagsEXT validation_flags = validation_flags_template; |
| validation_flags.disabledValidationCheckCount = 1; |
| validation_flags.pDisabledValidationChecks = &bad_disable; |
| ici.pNext = &validation_flags; |
| |
| Monitor().SetDesiredError("VUID-VkValidationFlagsEXT-pDisabledValidationChecks-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| { |
| VkValidationFlagsEXT validation_flags = validation_flags_template; |
| validation_flags.disabledValidationCheckCount = 0; |
| ici.pNext = &validation_flags; |
| |
| Monitor().SetDesiredError("VUID-VkValidationFlagsEXT-disabledValidationCheckCount-arraylength"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| } |
| |
| void* VKAPI_PTR DummyAlloc(void*, size_t size, size_t alignment, VkSystemAllocationScope) { |
| size_t space = size + alignment - 1; |
| void* mem_ptr = std::malloc(space); |
| if (!mem_ptr) { |
| printf("Dummy Alloc failed\n"); |
| return nullptr; |
| } |
| return std::align(alignment, size, mem_ptr, space); |
| } |
| void VKAPI_PTR DummyFree(void*, void* pMemory) { |
| (void)pMemory; |
| // The test which uses DummyFree plays foul of the nature of memory allocation on Windows. Because it doesn't use |
| // a VkAllocationCallback during vkCreateInstance then passes one in during vkDestroyInstance, the memory that is |
| // allocated during vkCreateInstance lives in a different 'heap'. Trying to free it from the application with the |
| // DummyFree callbacks will crash the application. The goal of this change is to enable Leak Sanitizer to run on |
| // CI runs in linux, so leaking memory in the windows tests is okay. |
| #if !defined(WIN32) |
| std::free(pMemory); |
| #endif |
| } |
| void* VKAPI_PTR DummyRealloc(void* pUserData, void* pOriginal, size_t size, size_t alignment, |
| VkSystemAllocationScope allocationScope) { |
| DummyFree(pUserData, pOriginal); |
| return DummyAlloc(pUserData, size, alignment, allocationScope); |
| } |
| void VKAPI_PTR DummyInfoAlloc(void*, size_t, VkInternalAllocationType, VkSystemAllocationScope) {} |
| void VKAPI_PTR DummyInfoFree(void*, size_t, VkInternalAllocationType, VkSystemAllocationScope) {} |
| |
| TEST_F(NegativeInstanceless, DestroyInstanceAllocationCallbacksCompatibility) { |
| TEST_DESCRIPTION("Test vkDestroyInstance with incompatible allocation callbacks."); |
| |
| const auto ici = GetInstanceCreateInfo(); |
| const VkAllocationCallbacks alloc_callbacks = {nullptr, &DummyAlloc, &DummyRealloc, |
| &DummyFree, &DummyInfoAlloc, &DummyInfoFree}; |
| |
| { |
| VkInstance instance; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateInstance(&ici, nullptr, &instance)); |
| |
| Monitor().SetDesiredError("VUID-vkDestroyInstance-instance-00631"); |
| vk::DestroyInstance(instance, &alloc_callbacks); |
| Monitor().VerifyFound(); |
| } |
| } |
| |
| // When address/tread sanitizer is on, this tends to fail tests after it |
| TEST_F(NegativeInstanceless, DISABLED_DestroyInstanceHandleLeak) { |
| TEST_DESCRIPTION("Test vkDestroyInstance while leaking a VkDevice object."); |
| RETURN_IF_SKIP(InitFramework()); |
| if (!IsPlatformMockICD()) { |
| // This test leaks a device (on purpose) and should not be run on a real driver |
| GTEST_SKIP() << "This test only runs on the mock ICD"; |
| } |
| const auto ici = GetInstanceCreateInfo(); |
| |
| VkInstance instance; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateInstance(&ici, nullptr, &instance)); |
| uint32_t physical_device_count = 1; |
| VkPhysicalDevice physical_device; |
| const VkResult err = vk::EnumeratePhysicalDevices(instance, &physical_device_count, &physical_device); |
| ASSERT_TRUE(err == VK_SUCCESS || err == VK_INCOMPLETE) << string_VkResult(err); |
| ASSERT_EQ(physical_device_count, 1); |
| |
| float dqci_priorities[] = {1.0}; |
| VkDeviceQueueCreateInfo dqci = vku::InitStructHelper(); |
| dqci.queueFamilyIndex = 0; |
| dqci.queueCount = 1; |
| dqci.pQueuePriorities = dqci_priorities; |
| |
| VkDeviceCreateInfo dci = vku::InitStructHelper(); |
| dci.queueCreateInfoCount = 1; |
| dci.pQueueCreateInfos = &dqci; |
| |
| VkDevice leaked_device; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateDevice(physical_device, &dci, nullptr, &leaked_device)); |
| |
| Monitor().SetDesiredError("VUID-vkDestroyInstance-instance-00629"); |
| vk::DestroyInstance(instance, nullptr); |
| Monitor().VerifyFound(); |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL callback(VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, |
| const char*, void*) { |
| return VK_FALSE; |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_PTR utils_callback(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, |
| const VkDebugUtilsMessengerCallbackDataEXT*, void*) { |
| return VK_FALSE; |
| } |
| |
| #ifndef VK_USE_PLATFORM_ANDROID_KHR |
| TEST_F(NegativeInstanceless, ExtensionStructsWithoutExtensions) { |
| TEST_DESCRIPTION("Create instance with structures in pNext while not including required extensions"); |
| |
| #ifdef VK_USE_PLATFORM_METAL_EXT |
| AddRequiredExtensions(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); |
| #endif |
| |
| VkInstanceCreateInfo ici = vku::InitStructHelper(); |
| #if defined(VK_USE_PLATFORM_METAL_EXT) |
| ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; |
| #endif |
| ici.pApplicationInfo = &app_info_; |
| ici.enabledLayerCount = size32(instance_layers_); |
| ici.ppEnabledLayerNames = instance_layers_.data(); |
| ici.enabledExtensionCount = size32(m_instance_extension_names); |
| ici.ppEnabledExtensionNames = m_instance_extension_names.data(); |
| |
| VkInstance instance; |
| VkDebugReportCallbackCreateInfoEXT debug_report_callback = vku::InitStructHelper(); |
| debug_report_callback.pfnCallback = callback; |
| debug_report_callback.pNext = m_errorMonitor->GetDebugCreateInfo(); |
| ici.pNext = &debug_report_callback; |
| m_errorMonitor->SetDesiredError("VUID-VkInstanceCreateInfo-pNext-04925"); |
| vk::CreateInstance(&ici, nullptr, &instance); |
| m_errorMonitor->VerifyFound(); |
| |
| VkDirectDriverLoadingInfoLUNARG driver = vku::InitStructHelper(); |
| |
| VkDirectDriverLoadingListLUNARG direct_driver_loading_list = vku::InitStructHelper(); |
| direct_driver_loading_list.pNext = const_cast<VkDebugUtilsMessengerCreateInfoEXT*>(m_errorMonitor->GetDebugCreateInfo()); |
| direct_driver_loading_list.driverCount = 1u; |
| direct_driver_loading_list.pDrivers = &driver; |
| ici.pNext = &direct_driver_loading_list; |
| m_errorMonitor->SetDesiredError("VUID-VkInstanceCreateInfo-pNext-09400"); |
| vk::CreateInstance(&ici, nullptr, &instance); |
| m_errorMonitor->VerifyFound(); |
| |
| // This must be last because it messes up the extension list |
| VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger = vku::InitStructHelper(); |
| debug_utils_messenger.pNext = m_errorMonitor->GetDebugCreateInfo(); |
| debug_utils_messenger.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; |
| debug_utils_messenger.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; |
| debug_utils_messenger.pfnUserCallback = utils_callback; |
| ici.pNext = &debug_utils_messenger; |
| // Ignore the first extension which is VK_EXT_debug_utils |
| ici.enabledExtensionCount = size32(m_instance_extension_names) - 1; |
| if (ici.enabledExtensionCount > 0) { |
| ici.ppEnabledExtensionNames = &m_instance_extension_names[1]; |
| } |
| m_errorMonitor->SetDesiredError("VUID-VkInstanceCreateInfo-pNext-04926"); |
| vk::CreateInstance(&ici, nullptr, &instance); |
| m_errorMonitor->VerifyFound(); |
| } |
| #endif |
| |
| // The test works, you will see the errors, but the test framework is not setup to hook into the debug callback before the create |
| // instance so no way to detect it |
| TEST_F(NegativeInstanceless, DISABLED_VkLayerSettingEXT) { |
| const char* ids[] = {"something"}; |
| VkLayerSettingEXT setting = {nullptr, "message_id_filter", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, ids}; |
| VkLayerSettingsCreateInfoEXT layer_settings_create_info = vku::InitStructHelper(); |
| layer_settings_create_info.settingCount = 1; |
| layer_settings_create_info.pSettings = &setting; |
| |
| auto ici = GetInstanceCreateInfo(); |
| ici.pNext = &layer_settings_create_info; |
| |
| { |
| Monitor().SetDesiredError("VUID-VkLayerSettingEXT-pLayerName-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| { |
| setting = {OBJECT_LAYER_NAME, nullptr, VK_LAYER_SETTING_TYPE_STRING_EXT, 1, ids}; |
| Monitor().SetDesiredError("VUID-VkLayerSettingEXT-pSettingName-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| { |
| setting = {OBJECT_LAYER_NAME, "message_id_filter", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, nullptr}; |
| Monitor().SetDesiredError("VUID-VkLayerSettingEXT-valueCount-10070"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| |
| { |
| layer_settings_create_info.pSettings = nullptr; |
| Monitor().SetDesiredError("VUID-VkLayerSettingsCreateInfoEXT-pSettings-parameter"); |
| vk::CreateInstance(&ici, nullptr, &dummy_instance); |
| Monitor().VerifyFound(); |
| } |
| } |
| |
| // Test for https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9181 |
| TEST_F(NegativeInstanceless, DestroyDeviceRace) { |
| TEST_DESCRIPTION("Test vkDestroyDevice from multiple threads to make sure handle lookup isn't accessing stale data"); |
| RETURN_IF_SKIP(InitFramework()); |
| const auto ici = GetInstanceCreateInfo(); |
| |
| VkInstance instance; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateInstance(&ici, nullptr, &instance)); |
| uint32_t physical_device_count = 1; |
| VkPhysicalDevice physical_device; |
| const VkResult err = vk::EnumeratePhysicalDevices(instance, &physical_device_count, &physical_device); |
| ASSERT_TRUE(err == VK_SUCCESS || err == VK_INCOMPLETE) << string_VkResult(err); |
| ASSERT_EQ(physical_device_count, 1); |
| |
| float dqci_priorities[] = {1.0}; |
| VkDeviceQueueCreateInfo dqci = vku::InitStructHelper(); |
| dqci.queueFamilyIndex = 0; |
| dqci.queueCount = 1; |
| dqci.pQueuePriorities = dqci_priorities; |
| |
| VkDeviceCreateInfo dci = vku::InitStructHelper(); |
| dci.queueCreateInfoCount = 1; |
| dci.pQueueCreateInfos = &dqci; |
| |
| VkDevice device; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateDevice(physical_device, &dci, nullptr, &device)); |
| |
| const auto& destroy_func = [device]() { vk::DestroyDevice(device, nullptr); }; |
| std::thread destroy_thread(destroy_func); |
| destroy_thread.join(); |
| |
| VkDevice device2; |
| ASSERT_EQ(VK_SUCCESS, vk::CreateDevice(physical_device, &dci, nullptr, &device2)); |
| |
| vk::DestroyDevice(device2, nullptr); |
| vk::DestroyInstance(instance, nullptr); |
| } |