vkCreateDevice Exception thrown at 0x0000000000000000

Hello,

In my setup I have 2 ‘video cards’, when enumerating the devices they show up in this order

  • GeForce GTX 1080
  • Intel® UHD Graphics 630

I have a piece of code that selects the physical device to run on and (for testing purposes) I sometimes run on the 630 (as a low end card). However it seems that when I try to create a device for the Intel® UHD Graphics 630, it crashes in VkCreateDevice. Now this only happens if the GeForce GTX 1080 is enabled, since then the vulkan instance enumerates 2 devices. if I disable the 1080 through device manager, the same code works. Vulkan then only enumerates 1 device and it works as you think it would.

I expected the validation layer to give some more information, but unfortunately it does not. I can see the callstack in the crash:

0000000000000000()	Unknown
igvk64.dll!00007fff0876aec6()	Unknown
igvk64.dll!00007fff0874e11e()	Unknown
igvk64.dll!00007fff0873af5a()	Unknown
vulkan-1.dll!00007fff23249dbb()	Unknown
VkLayer_unique_objects.dll!unique_objects::CreateDevice(VkPhysicalDevice_T * gpu, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice_T * * pDevice) Line 207	C++
VkLayer_threading.dll!threading::CreateDevice(VkPhysicalDevice_T * gpu, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice_T * * pDevice) Line 163	C++
VkLayer_parameter_validation.dll!parameter_validation::vkCreateDevice(VkPhysicalDevice_T * physicalDevice, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice_T * * pDevice) Line 552	C++
VkLayer_object_tracker.dll!object_tracker::CreateDevice(VkPhysicalDevice_T * physicalDevice, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice_T * * pDevice) Line 834	C++
VkLayer_core_validation.dll!core_validation::CreateDevice(VkPhysicalDevice_T * gpu, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice_T * * pDevice) Line 2242	C++

Which seems to indicate that the problem is somewhere in the Intel driver (igvk64.dll)

To make it easy to reproduce I have put together a small piece of code that tries to create a device for each enumerated physical device (attachments seem to not work, so code is copy/pasted below)

I am running Vulkan runtime 1.1.73.0
I am running NVIDIA driver 397.93
I am running Intel UHD Graphics Driver 22.20.16.4758 (supports vulkan version 1.0.50)

Another interesting thing, which does not cause crashes but triggers the validation layer, seems to be that when using the NVIDIA driver you are required to call vkGetPhysicalDeviceQueueFamilyProperties, as just setting the queueFamilyIndex to 0 will trigger validation errors.


#define WIN32_LEAN_AND_MEAN			/// vulkan.h includes windows.h, so the Windows defines need to happen before including vulkan.h
#define NOMINMAX
#define VK_USE_PLATFORM_WIN32_KHR
#include <vulkan/vulkan.h>
#include <assert.h>

#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>

#define VK_CHECK_RESULT(x)										do { VkResult result = (x); assert(result == VK_SUCCESS); } while(0)
#define VK_CHECK_RESULT_RETURN(x)								do { VkResult result = (x); assert(result == VK_SUCCESS); if(result != VK_SUCCESS) return; } while(0)
#define sizeof_array(x)											(sizeof(x) / sizeof(x[0]))

void Print(const char* text)
{
	printf("%s
", text);

	OutputDebugStringA(text);
	OutputDebugStringA("
");
}

class TestCreateDevices
{
public:

	TestCreateDevices()
	{
		CreateInstance("");
		CreateDevices();
		DestroyInstance();
	}

private:

	void CreateInstance(const char* applicationName)
	{
		// Enabled Extensions
		const char* enabledExtensions[] =
		{
			"VK_KHR_surface",
			"VK_KHR_win32_surface",
		};

		// Enabled Layers
		const char* enabledLayers[] =
		{
			"VK_LAYER_LUNARG_assistant_layer",
			"VK_LAYER_LUNARG_core_validation",
			"VK_LAYER_LUNARG_object_tracker",
			"VK_LAYER_LUNARG_parameter_validation",
			"VK_LAYER_LUNARG_standard_validation",
		};

		VkApplicationInfo applicationInfo = {};
		applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
		applicationInfo.pApplicationName = applicationName;
		applicationInfo.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
		applicationInfo.pEngineName = applicationName;
		applicationInfo.engineVersion = VK_MAKE_VERSION(0, 1, 0);
		applicationInfo.apiVersion = VK_API_VERSION_1_1;

		VkInstanceCreateInfo instanceCI = {};
		instanceCI.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
		instanceCI.pApplicationInfo = &applicationInfo;
		instanceCI.ppEnabledExtensionNames = enabledExtensions;
		instanceCI.enabledExtensionCount = static_cast<uint32_t>(sizeof_array(enabledExtensions));
		instanceCI.ppEnabledLayerNames = enabledLayers;
		instanceCI.enabledLayerCount = static_cast<uint32_t>(sizeof_array(enabledLayers));

		VK_CHECK_RESULT(vkCreateInstance(&instanceCI, mAllocationCallbacks, &mInstance));
	}

	void DestroyInstance()
	{
		vkDestroyInstance(mInstance, mAllocationCallbacks);
		mInstance = VK_NULL_HANDLE;
	}

	void CreateDevices()
	{
		uint32_t physicalDeviceCount = 0;
		VK_CHECK_RESULT_RETURN(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));

		VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca(sizeof(VkPhysicalDevice) * physicalDeviceCount);
		VK_CHECK_RESULT_RETURN(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, physicalDevices));

		Print("Devices:"); 
		for (uint32_t i = 0; i < physicalDeviceCount; ++i)
		{
			VkPhysicalDeviceProperties physicalDeviceProperties;
			vkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties);
			Print(physicalDeviceProperties.deviceName);

			VkPhysicalDeviceFeatures physicalDeviceFeatures;
			vkGetPhysicalDeviceFeatures(physicalDevices[i], &physicalDeviceFeatures);

			// queueFamilyIndex
			uint32_t queueFamilyIndex = 0;
			uint32_t queueFamilyPropertyCount;
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &queueFamilyPropertyCount, nullptr);
			assert(queueFamilyPropertyCount > 0);

			VkQueueFamilyProperties* queueFamilyProperties = (VkQueueFamilyProperties*)alloca(sizeof(VkQueueFamilyProperties) * queueFamilyPropertyCount);
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &queueFamilyPropertyCount, queueFamilyProperties);

			// queue
			float queuePriority = 1.0f;
			VkDeviceQueueCreateInfo queueCI = {};
			queueCI.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
			queueCI.pNext = nullptr;
			queueCI.flags = 0;
			queueCI.queueFamilyIndex = queueFamilyIndex;
			queueCI.queueCount = 1;
			queueCI.pQueuePriorities = &queuePriority;

			const char* enabledExtensions[] =
			{
				"VK_KHR_swapchain",
			};

			VkDeviceCreateInfo deviceCI = {};
			deviceCI.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
			deviceCI.pNext = nullptr;
			deviceCI.flags = 0;
			deviceCI.queueCreateInfoCount = 1;
			deviceCI.pQueueCreateInfos = &queueCI;
			deviceCI.enabledLayerCount = 0;
			deviceCI.ppEnabledLayerNames = nullptr;
			deviceCI.ppEnabledExtensionNames = enabledExtensions;
			deviceCI.enabledExtensionCount = static_cast<uint32_t>(sizeof_array(enabledExtensions));
			deviceCI.pEnabledFeatures = &physicalDeviceFeatures;

			VkDevice device;
			VK_CHECK_RESULT(vkCreateDevice(physicalDevices[i], &deviceCI, mAllocationCallbacks, &device));
			vkDestroyDevice(device, mAllocationCallbacks);
		}
	}
	
	VkAllocationCallbacks*				mAllocationCallbacks = nullptr;
	VkInstance							mInstance = VK_NULL_HANDLE;
};


int main(int argc, char* argv[])
{
	TestCreateDevices();
	return 0;
}

You are enabling layers twice, and in weird order. Remove the VK_LAYER_LUNARG_core_validation, VK_LAYER_LUNARG_object_tracker and VK_LAYER_LUNARG_parameter_validation.
VK_LAYER_LUNARG_assistant_layer should probably be last, as to first catch errors before potentially crashing.
Should also enable identical set in vkCreateDevice for compatibility.

Thank you for the suggestions.

I am not 100% clear what you mean by “you are enabling layers twice”, to my understanding there are instance layers and device layers and the set is not necessarily the same.

That said… I tried your approach of reorder/remove and I tried to disable all layers/extensions both on the vkInstance and the vkDevice and the crash remains the same.

[QUOTE=efolkertsma;43521]
I am not 100% clear what you mean by “you are enabling layers twice”, to my understanding there are instance layers and device layers and the set is not necessarily the same.[/QUOTE]

Your enable set is:


		const char* enabledLayers[] =
		{
			"VK_LAYER_LUNARG_assistant_layer",
			"VK_LAYER_LUNARG_core_validation",
			"VK_LAYER_LUNARG_object_tracker",
			"VK_LAYER_LUNARG_parameter_validation",
			"VK_LAYER_LUNARG_standard_validation",
		};

The VK_LAYER_LUNARG_standard_validation is a meta-layer which expands to:


VK_LAYER_GOOGLE_threading
VK_LAYER_LUNARG_parameter_validation
VK_LAYER_LUNARG_object_tracker
VK_LAYER_LUNARG_core_validation
VK_LAYER_GOOGLE_unique_objects

(in this order; BTW the order matters too).
Therefore, as you can see, you are practically enabling the VK_LAYER_LUNARG_core_validation, VK_LAYER_LUNARG_object_tracker and VK_LAYER_LUNARG_parameter_validation twice.

Also as you can see, even if you removed VK_LAYER_LUNARG_standard_validation from the list, the remaining layers are in different order than how canonical VK_LAYER_LUNARG_standard_validation meta-layer would enable them.

Errors that would be otherwisely caught by the Validation Layers, can crash other types of layers (without error message). Therefore it usually (not always) makes sense to have the Validation Layers first in the list, and others last (such as the VK_LAYER_LUNARG_assistant_layer), so it first prints an error message then crashes, not other way around.

As for the device layers, they were deprecated. But for backwards-compatibility you are supposed to pretend they are not. My preferred way to do so is just to pass to the vkCreateDevice the exact same enabledLayers you gave to vkCreateInstance

As for your crash, I would suggest updating the drivers. There seems to be v24 (with 1.1.73).
I previously had problems with this kind of “multi-monitor” setup – in Linux distro though. I kinda hope it works though. Everybody gets shoved the iGPUs their throats, so it is quite common setup (though it has to be enabled in BIOS).

PS:
Check if your Queue Family actually has >0 queues.

For NVIDIA there seems to be v398 driver available.

Updated both Intel + NVIDIA drivers now it works… thank you for the suggestions!

I am running NVIDIA driver 398.11
I am running Intel UHD Graphics Driver 24.20.100.6136

PS1: When I originally tried to update the Intel Driver I got errors from the installer that the driver was not validated for the OS. Turns out that some vendors have their own driver vetting and block driver versions they didn’t vet yet. Fortunately this is something that can be overridden by manually selecting the driver through the .inf file. Not very user friendly, but does the trick.
PS2: When I first installed just the Intel driver then it failed to create device for the GTX 1080, then I updated that driver… success.
PS3: After the updated drivers I did get warnings on a layer not being supported, fortunately the real code (not the sample) checks the layers I’m trying to enable against the layers supported (as queried by the device)