diff --git a/src/DeviceLibrary.cpp b/src/DeviceLibrary.cpp new file mode 100644 index 0000000..13c72f5 --- /dev/null +++ b/src/DeviceLibrary.cpp @@ -0,0 +1,87 @@ +#include "DeviceLibrary.h" +#include +#include +#include +#include +#include +#include +#include +using namespace AgnosiaEngine; + +VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; +VkPhysicalDeviceProperties deviceProperties; +VkPhysicalDeviceFeatures deviceFeatures; + +struct QueueFamilyIndices { + std::optional graphicsFamily; + + bool isComplete() { + return graphicsFamily.has_value(); + } +}; + +QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { + // First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. + // These store the flags, the amount of queued items in the family, and timestamp data. Queue families are simply group collections of tasks we want to get done. + // Next, we check the flags of the queueFamily item, use a bitwise and to see if they match, i.e. support graphical operations, then return that to notify that we have at least one family that supports VK_QUEUE_GRAPHICS_BIT. + // Which means this device supports graphical operations! + QueueFamilyIndices indices; + + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for(const auto& queueFamily : queueFamilies) { + if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + if(indices.isComplete()) { + break; + } + i++; + } + return indices; +} + +bool isDeviceSuitable(VkPhysicalDevice device) { + // These two are simple, create a structure to hold the apiVersion, driverVersion, vendorID, deviceID and type, name, and a few other settings. + // Then populate it by passing in the device and the structure reference. + vkGetPhysicalDeviceProperties(device, &deviceProperties); + // Similarly, we can pass in the device and a deviceFeatures struct, this is quite special, it holds a struct of optional features the GPU can perform. + // Some, like a geometry shader, and stereoscopic rendering (multiViewport) we want, so we dont return true without them. + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + // We need to find a device that supports graphical operations, or else we cant do much with it! This function just runs over all the queueFamilies and sees if there + // is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped! + QueueFamilyIndices indices = findQueueFamilies(device); + + return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.multiViewport && deviceFeatures.geometryShader && indices.isComplete(); +} + +void DeviceLibrary::pickPhysicalDevice(VkInstance& instance) { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if(deviceCount == 0) { + throw std::runtime_error("Failed to find GPU's with Vulkan Support!!"); + } + std::vector devices(deviceCount); // Direct Initialization is weird af, yo + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + for(const auto& device : devices) { + if(isDeviceSuitable(device)) { + std::cout << "Using device: " << deviceProperties.deviceName << std::endl; + //Once we have buttons or such, maybe ask the user or write a config file for which GPU to use? + physicalDevice = device; + break; + } + } + + if(physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("Failed to find a suitable GPU!"); + } +} + + diff --git a/src/DeviceLibrary.h b/src/DeviceLibrary.h new file mode 100644 index 0000000..6364294 --- /dev/null +++ b/src/DeviceLibrary.h @@ -0,0 +1,7 @@ +#include +namespace AgnosiaEngine { + class DeviceLibrary { + public: + void pickPhysicalDevice(VkInstance& instance); + }; +} diff --git a/src/main.cpp b/src/main.cpp index 53d3118..16745b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include #include "debug/VulkanDebugLibs.h" +#include "DeviceLibrary.h" using namespace AgnosiaEngine; #include @@ -36,6 +37,7 @@ private: GLFWwindow* window; VkInstance instance; VulkanDebugLibs debug; + DeviceLibrary device; // Initialize GLFW Window. First, Initialize GLFW lib, disable resizing for // now, and create window. void initWindow() { @@ -50,6 +52,7 @@ private: void initVulkan() { createInstance(); debug.setupDebugMessenger(instance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DNDEBUG flag (as per the makefile) + device.pickPhysicalDevice(instance); } void createInstance() {