yave/src/yave_vulkan_instance.cpp

202 lines
6.2 KiB
C++

#include "yave_vulkan_instance.hpp"
#include <cstdint>
#include <vulkan/vulkan_core.h>
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<VkLayerProperties> 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<uint32_t>(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<VkPhysicalDevice> 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<VkQueueFamilyProperties> 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<uint32_t>(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