I'm having some difficulty with VkGetDeviceQueue

I’ve been having some trouble with the VkGetDeviceQueue function.

I have a class for handling my vulkan instance, and variables.


#pragma once
#include <iostream>
#include <vulkan/vulkan.h>
#include <string>
#include "_Error.h"
#include "GLFW/glfw3.h"
using namespace std;

class vulkanHandler
{
private:
		//Structure holding logic device information, queues, and the logical device
	struct logicDevice
	{
		string type;
		float Priority = 1.0f;
		VkQueue graphicsQueue;
		VkDevice device;
		
		VkDeviceCreateInfo createInfo;
		VkDeviceQueueCreateInfo Q_createInfo;
	};
	
		//Create a structure of queue counts since you can't have an vector with an array type.
	struct queueCounts
	{
		int Graphics;
		int Compute;
		int Transfer;
		int Sparce;
	};

		//define a struct to hold all of the device information for each GPU
	struct GPUwrapper
	{
		VkPhysicalDevice Device;						//Physical device
		VkPhysicalDeviceProperties DeviceProperties;	//It's properties
		vector<VkQueueFamilyProperties> QueueProperties;//It's Queue properties
		vector<queueCounts> queueCounts;				//Queue counts per family. indicies will match with queueproperties
	};


		//Holds all the information relating to the instance;
	struct instanceProperties
	{
		VkApplicationInfo appInfo;
		VkInstanceCreateInfo createInfo;
		vector<VkExtensionProperties> vulkanExtensions;
	};

		//Declarations
	VkInstance instance;								//The vulkan instance 
	instanceProperties instanceProperties;				//It's corrosponding properties
	vector<GPUwrapper> GPU;								//A vector array of the available GPU's on the system
	vector<logicDevice> logic;							//A vector array of logic devices.

	Console dev;										//Creates a debug console(externally defined)

	uint32_t glfwExtensionCount;						//The required amount of extensions for glfw
	uint32_t extensionCount;							//Vulkan extensions



public:
	vulkanHandler(GLFWwindow* Window)
	{
			//Upon declaring the vulkanHandler object this code will run. Initializing the instance
			//We populate our ApplicationInfo variable with the proper information
		instanceProperties.appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;				//Structure Type
		instanceProperties.appInfo.pApplicationName = "Game";								//Application Name
		instanceProperties.appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);			//Current version of the app
		instanceProperties.appInfo.pEngineName = "beta";									//Engine name
		instanceProperties.appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);				//Build version
		instanceProperties.appInfo.apiVersion = VK_API_VERSION_1_0;						    //Vulkan API version

			//We attach the application info variable to the instance creation information variable
		instanceProperties.createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;		//Structure type
		instanceProperties.createInfo.pApplicationInfo = &instanceProperties.appInfo;		//Pointer to our appinfo var

		const char** glfwExtensions;									//the count of glfwExtension
		glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
																		//Amount of GLFW req extensions

		instanceProperties.createInfo.enabledExtensionCount = glfwExtensionCount;			//Tell the instance create info
		instanceProperties.createInfo.ppEnabledExtensionNames = glfwExtensions;				//About glfw extensions
		instanceProperties.createInfo.enabledLayerCount = 0;								//Total number of global layers
			//Create an instance with the information we just populated.
		if (!vkCreateInstance(&instanceProperties.createInfo, nullptr, &instance) == VK_SUCCESS) {
				//If failed, log the failure
			dev.Log("Failed to create Vulkan instance", { 3,VK_ERROR_INSTANCE });
			//TODO !!! ADD ERROR CORRECTION ATTEMPTS, AND A COMPLETE FAIL CONDITION
		}

			//Get the required vulkan extensions and store them in a vector array for later.
		vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
		instanceProperties.vulkanExtensions.resize(extensionCount);
		vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, instanceProperties.vulkanExtensions.data());

			//After initializing the instance, we move to physical devices.
		physicalDevice();
	}



private:


	void physicalDevice()
	{
			//Define temporary varibles that way we can push them to our arrays
		VkPhysicalDeviceProperties buffer;
		GPUwrapper gpuBuffer;
		uint32_t intbuffer;

		/*This will define the required structures that make up a physical device.*/
		vkEnumeratePhysicalDevices(instance, &intbuffer, nullptr);			//Querery cDevice
		vector<VkPhysicalDevice> devicePool(intbuffer);	//Vector array to hold the physical devices
		vkEnumeratePhysicalDevices(instance, &intbuffer, devicePool.data());	//Populate

																			//Acquire the physical device properties for each device, and create a GPU for it.
		for each (VkPhysicalDevice device in devicePool)
		{
			//Using the current device in the for look, we get it's corrosponding properties
			vkGetPhysicalDeviceProperties(device, &buffer);
			//Store the data to a buffer GPU object
			gpuBuffer.Device = device;			gpuBuffer.DeviceProperties = buffer;

			//Get the properties for the available queue families.
			vkGetPhysicalDeviceQueueFamilyProperties(device, &intbuffer, nullptr);
			gpuBuffer.QueueProperties.resize(intbuffer);
			vkGetPhysicalDeviceQueueFamilyProperties(device, &intbuffer, gpuBuffer.QueueProperties.data());

			//Push the buffer to the stack, this will keep things organized.
			GPU.push_back(gpuBuffer);
			//Do I need to define new here? gpuBuffer = *new GPUwrapper;
			//I only have 1 GPU, so I wouldn't see an error here if I needed another.
		}

		//After getting the physical devices, we're going to move on to the logical devices
		primaryLogicDevice();
	}

	void primaryLogicDevice()
	{
		//buffer.device = * new VkDevice;
		queueCounts buf;
		logicDevice buffer;

			/* procress The queue families information we acquired in physicalDevice() */
		uint32_t G = 0, C = 0, T = 0, S = 0;			//Store queue index with the greatest count of queues (Naive logic?)
			//Buffer value
		int i = 0;
		for each (GPUwrapper gpu in GPU)
		{
			cout << gpu.DeviceProperties.deviceName _;
			cout << gpu.DeviceProperties.apiVersion _;
				//Reset the value of i since each GPU is it's own object and the parent of these children
			i = 0;
			for each (VkQueueFamilyProperties queueFamily in gpu.QueueProperties)
			{
				buf = *new queueCounts;
					//For each device, and each QueueFamilyProperty, check it's functionality.
				if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
				{
					//.Indicies.graphicIndicies.push_back(i);		//And store away it's index
					//gpu.Counts[0] += queueFamily.queueCount;
					buf.Graphics = queueFamily.queueCount;
					if (queueFamily.queueCount > G)
					{
						G = i;
					}
				}
				if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT)
				{
					//gpu.Indicies.computeIndicies.push_back(i);
					//gpu.Counts[1] += queueFamily.queueCount;
					buf.Compute = queueFamily.queueCount;
					if (queueFamily.queueCount > C)
					{
						C = i;
					}
				}
				if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT)
				{
					//gpu.Indicies.transferIndicies.push_back(i);
					//gpu.Counts[2] += queueFamily.queueCount;
					buf.Transfer = queueFamily.queueCount;
					if (queueFamily.queueCount > T)
					{
						T = i;
					}
				}
				if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)
				{
					//gpu.Indicies.sparceIndicies.push_back(i);
					//gpu.Counts[3] += queueFamily.queueCount;
					buf.Sparce = queueFamily.queueCount;
					if (queueFamily.queueCount > S)
					{
						S = i;
					}
				}
					//Since each device might have multiple queue gamilies, we'll incremement.
				gpu.queueCounts.push_back(buf);
				cout << "	Queue Family: " << i _;					//Note '_;' Is a #define for '<< endl'; Just my shorthand
				cout << "		Graphics: " << buf.Graphics _;
				cout << "		Compute: " << buf.Compute _;
				cout << "		Transfer: " << buf.Transfer _;
				cout << "		Sparce: " << buf.Sparce _;
				i++;	
				buf.Compute = 0;
				buf.Graphics = 0;
				buf.Transfer = 0;
				buf.Sparce = 0;
			}

				//Populate the required Structs to create the logic device, and queue
			buffer.Q_createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
			buffer.Q_createInfo.queueFamilyIndex = G;				//Use the queue with the highest score for graphics
			buffer.Q_createInfo.queueCount = 1;						//Supposedly high queueCounts cause driver crashes?
			buffer.Q_createInfo.pQueuePriorities = &buffer.Priority;//Queue priority

			buffer.type = "Graphics";								//Populate the structure I made that holds logic device info
			buffer.createInfo.enabledLayerCount = 0;				//No validation layers yet :C
			buffer.createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
			buffer.createInfo.enabledExtensionCount = 0;			
			buffer.createInfo.pQueueCreateInfos = &buffer.Q_createInfo;
			buffer.createInfo.pEnabledFeatures = NULL;
				//Attempt to create the logic device.
			if (vkCreateDevice(gpu.Device, &buffer.createInfo, NULL, &buffer.device) != VK_SUCCESS)
			{
					//If failed, cry about it and give up
				dev.Log("You done goofed, kid");
			}
			cout << endl << "Logical device created with graphics queue" << endl;
			logic.push_back(buffer);								//Now that it's been created, add it to the pool of logic devices
			for each(logicDevice device in logic)					//For each logic device we have in logic(just one in this case)
			{
				if (device.type == "Graphics")						//Which we explicitly stated was true earlier
				{
						//Try to bind the queue handler to our device.graphicsQueue variable
					vkGetDeviceQueue(device.device, G, 0, &device.graphicsQueue);				//TODO !!!!!! THROWS EXCEPTION
					cout << "Bound queue to device" _;
				}
			}
			
		}
	}


public:
	//TODO: KILLALL(), CLOSE(), CLEANUP() FUNCTIONS
};

Essentially my goal here is when I create a vulkanHandler object in my main function, it will set up the required information and then I can access the instance through helper functions, mitigating cluttered code.
I tend to run into trouble sometimes with pointers, so I believe that may be my issue.

The issue I’m running into, is after I call the VkGetDeviceQueue function, it returns the VkQueue object pointer as 0x00000000. I’ve attached watches on my objects and I see right before the function is called, everything seems to be correctly populated(except for functions, but I have function count at 0, could that be an issue?)
This of course throws an access write violation.

I think my issue may be with memory management. I’m just not sure what exactly.

I really would appreciate any help c:

  1. Use Validation Layers. I kinda keep repeating that one. Any ideas how to make it obvious to incoming developers that this is something that they should do idiomatically?

  2. Your whole ```VkDeviceCreateInfo[\VAR] is corrupted. It has uninitialized members.

  3. WTH is “for each”?! Be nice and use standards.

  4. BTW you seem to have missing include for vector.

[QUOTE=krOoze;42932]1) Use Validation Layers. I kinda keep repeating that one. Any ideas how to make it obvious to incoming developers that this is something that they should do idiomatically?

  1. Your whole ```VkDeviceCreateInfo[\VAR] is corrupted. It has uninitialized members.

  2. WTH is “for each”?! Be nice and use standards.

  3. BTW you seem to have missing include for vector.[/QUOTE]

  4. I know I need to implement validation layers; however, I have been following the spec and I haven’t seen too much information on it yet, I was working my way down. I’ve had it explained that they exist, but I haven’t came across them in the spec yet. My goal is to understand the full spec, not just follow some kids youtube video to learn how to write my code. That’s actually next on my list. I was going to implement it before the swap chain.

and yes, you can help make it easier to them, by explaining that
“All data in a computer is just numbers. If the wrong numbers get put in the wrong spot the computer wont know, but the program interpreting the data as more than just numbers will (Especially when working with pointers). This means in some cases code can compile fine, but it will not run. You can’t get much information from a crashed application. The solution is validation layers. It’s easier to program to have them as part of your program, instead of deciding later you want to implement them. Upon final release you just disable them reducing overhead. There really aren’t any down sides to validation layers beside some new habits, some initilization.”

  1. I have a watch on there though, and it’s showing all the variables as being set to what I wanted them to be set for? Is this my issue? I was afraid I was pointing to a destroyed local variable since I didn’t create ‘new’ variables.
    Unless since uninstanciated pointers are 0xcccccccc instead of nullptr? Looking this is might be it

  2. “Foreach in C++ and Java. Foreach loop is used to access elements of an array quickly without performing initialization, testing and increment/decrement. The working of foreach loops is to do something for every element rather than doing something n times.” This allows me to go through each item in my vector array without just doing a for loop. It’s essentially the same thing as a for(int i = 0; i < sizeofarray; I++), but I don’t have to use i to index which index I’m accessing (Array[i].___). It’s part of STL/CLR. (TBH I didn’t think this wasn’t a standard. I’ve been using it for a while now)

4)thank you, I actually have it included in the _Error.h file though lol. I did plan on reorganizing all of that once I finish so it can be standalone.

[QUOTE=Fireball3179;42934]1) I know I need to implement validation layers; however, I have been following the spec and I haven’t seen too much information on it yet, I was working my way down. I’ve had it explained that they exist, but I haven’t came across them in the spec yet. My goal is to understand the full spec, not just follow some kids youtube video to learn how to write my code. That’s actually next on my list. I was going to implement it before the swap chain.

and yes, you can help make it easier to them, by explaining that
“All data in a computer is just numbers. If the wrong numbers get put in the wrong spot the computer wont know, but the program interpreting the data as more than just numbers will (Especially when working with pointers). This means in some cases code can compile fine, but it will not run. You can’t get much information from a crashed application. The solution is validation layers. It’s easier to program to have them as part of your program, instead of deciding later you want to implement them. Upon final release you just disable them reducing overhead. There really aren’t any down sides to validation layers beside some new habits, some initilization.”
[/QUOTE]

Well, it is statistically impossible for puny humans to follow the standard (i.e. not make a mistake). That’s why Vulkan validation layers exist. Enabling them is as easy as installing the SDK and setting VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_standard_validation environment variable for your program, and you will get feedback on most Vulkan related bugs in your program in your stdout.

That’s almost exactly what the Vulkan specification says. The problem is probably where to say this, so it gets noticed way before people get in trouble.

I am quite sure the layers will show this.
Anyway your buffer.createInfo.flags is uninitialized. And your buffer.createInfo.queueInfoCount is also uninitialized. And maybe more…

ad 3) Your for each(x in y) is some kind of unportable compiler extension, who knows. Anyway this “for each” is written in C++ as for(auto& element : elements), which is known as “range for” – that is unless you are stuck with C++98\03.

ad 4) Nah, just sayin’ I noticed that. It is a non-issue and quite normal to accidentally include through another header. I guess, that is one of motivations for making Modules in C++20…

You’re right, I actually just glanced through the create info and I left out the queueCreateInfoCount.

and yes, I guess I forget how literal vulkan is sometimes. Null means Null, and I have to state that.
I guess it’s time to set up some validation layers, lol :oops:

Thank you for your help. I really appreciate it c:

And ‘for each’ might be a visual studio thing, I have seen it written with the &auto syntax; Though, I thought it was a compiler level abstraction, not application level. I plan on rewriting this code once I figure it out. I’ll likely use that if it’s more cross compatible. I appreciate the advice.