#include "yave_vulkan_instance.hpp" #include #include namespace yave { YaveVulkanInstance::YaveVulkanInstance() { createInstance(); pickPhysicalDevice(); createLogicalDevice(); } YaveVulkanInstance::~YaveVulkanInstance() { destroyLogicalDevice(); destroyInstance(); } void YaveVulkanInstance::createInstance() { VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Hello Triangle"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "Yave"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_0; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; uint32_t glfwExtensionCount = 0; const char **glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); createInfo.enabledExtensionCount = glfwExtensionCount; createInfo.ppEnabledExtensionNames = glfwExtensions; createInfo.enabledLayerCount = 0; checkLayersValidSuppport(createInfo); if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create vulkan instance!"); } } void YaveVulkanInstance::destroyInstance() { vkDestroyInstance(this->instance, nullptr); } bool YaveVulkanInstance::checkLayersValidSuppport( VkInstanceCreateInfo &createInfo) { // Finish if validation is disabled (Release mode) if (!this->enableValidationLayers) { createInfo.enabledLayerCount = 0; return true; } // Get layers uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); // Validate layers for (const char *layerName : validationLayers) { bool layerFound = false; for (const auto &layerProperties : availableLayers) { if (strcmp(layerName, layerProperties.layerName) == 0) { layerFound = true; break; } } if (!layerFound) { return false; } } return true; createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); return true; } // TODO: Create way to pick the most powerful and feature rich device //(maybe use a suitability score rather than bool) // Right now we're settling for the first device with queue family support bool YaveVulkanInstance::isDeviceSuitable(VkPhysicalDevice device) { QueueFamilyIndices indices = findQueueFamilies(device); return indices.isComplete(); } void YaveVulkanInstance::pickPhysicalDevice() { physicalDevice = VK_NULL_HANDLE; // Get number of vulken enabled devices on system uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); // No point in running vulkan if it's not supported on any devices if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support!"); } // Now we have the number of devices we can create a vector to store them for // evaluation std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); // Use our suitability function to see if a device is suitable for (const auto &device : devices) { if (isDeviceSuitable(device)) { physicalDevice = device; break; } } // If we couldn't select a device throw an error if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("failed to find a suitable GPU!"); } } QueueFamilyIndices YaveVulkanInstance::findQueueFamilies(VkPhysicalDevice device) { // Get number of queues QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); // Put queues into vector std::vector queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); // iterate over the devices ques and see if it has a graphics bit int i = 0; for (const auto &queueFamily : queueFamilies) { if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } // If we know we have support we don't need to look for anything more if (indices.isComplete()) { break; } i++; } return indices; } void YaveVulkanInstance::createLogicalDevice() { QueueFamilyIndices indices = findQueueFamilies(physicalDevice); // Only need a single queue as we can feed it with multiple threads and // trigger it from the main thread to start off with. VkDeviceQueueCreateInfo queueCreateInfo{}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); queueCreateInfo.queueCount = 1; // Since we only have one queue it has all the priority. float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; // TODO: set up device with interesting features as we use them VkPhysicalDeviceFeatures deviceFeatures{}; // Create logical device. VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = &queueCreateInfo; createInfo.queueCreateInfoCount = 1; createInfo.pEnabledFeatures = &deviceFeatures; // TODO: Require specific features when we need some // Device specific create information createInfo.enabledExtensionCount = 0; if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } // Create logical device if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } // Whilst we're here we might as well set up our queue vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); } void YaveVulkanInstance::destroyLogicalDevice() { vkDestroyDevice(device, nullptr); } } // namespace yave