blob: 2a6e4d1afdf7d3e63c5458adee7bd10754bc748a [file] [log] [blame]
/* 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);
}