Files
mxl-multiviewer/VulkanContext.cpp
2026-05-18 00:03:06 +03:00

414 lines
9.2 KiB
C++

#include "VulkanContext.hpp"
#include <SDL3/SDL.h>
#include <SDL3/SDL_vulkan.h>
#include <stdexcept>
QueueFamilyIndices findQueueFamilies(
VkPhysicalDevice device,
VkSurfaceKHR surface)
{
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(
device,
&queueFamilyCount,
nullptr
);
std::vector<VkQueueFamilyProperties> queueFamilies(
queueFamilyCount
);
vkGetPhysicalDeviceQueueFamilyProperties(
device,
&queueFamilyCount,
queueFamilies.data()
);
for (uint32_t i = 0; i < queueFamilyCount; ++i)
{
if (queueFamilies[i].queueFlags &
VK_QUEUE_GRAPHICS_BIT)
{
indices.graphicsFamily = i;
}
VkBool32 presentSupport = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(
device,
i,
surface,
&presentSupport
);
if (presentSupport)
{
indices.presentFamily = i;
}
if (indices.isComplete())
{
break;
}
}
return indices;
}
SwapchainSupportDetails querySwapchainSupport(
VkPhysicalDevice device,
VkSurfaceKHR surface)
{
SwapchainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
device,
surface,
&details.capabilities
);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(
device,
surface,
&formatCount,
nullptr
);
if (formatCount != 0)
{
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(
device,
surface,
&formatCount,
details.formats.data()
);
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(
device,
surface,
&presentModeCount,
nullptr
);
if (presentModeCount != 0)
{
details.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(
device,
surface,
&presentModeCount,
details.presentModes.data()
);
}
return details;
}
VulkanContext::VulkanContext(
SDL_Window* window,
const char* appName,
uint32_t versionMajor,
uint32_t versionMinor,
uint32_t versionPatch)
{
uint32_t extensionCount = 0;
const char* const* extensions =
SDL_Vulkan_GetInstanceExtensions(
&extensionCount
);
VkApplicationInfo appInfo{};
appInfo.sType =
VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = appName;
appInfo.applicationVersion =
VK_MAKE_VERSION(
versionMajor,
versionMinor,
versionPatch
);
appInfo.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo createInfo{};
createInfo.sType =
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.enabledExtensionCount =
extensionCount;
createInfo.ppEnabledExtensionNames =
extensions;
if (vkCreateInstance(
&createInfo,
nullptr,
&m_instance) != VK_SUCCESS)
{
throw std::runtime_error(
"Failed to create Vulkan instance"
);
}
if (!SDL_Vulkan_CreateSurface(
window,
m_instance,
nullptr,
&m_surface))
{
vkDestroyInstance(m_instance, nullptr);
throw std::runtime_error(
"Failed to create Vulkan surface"
);
}
uint32_t gpuCount = 0;
vkEnumeratePhysicalDevices(
m_instance,
&gpuCount,
nullptr
);
if (gpuCount == 0)
{
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
vkDestroyInstance(m_instance, nullptr);
throw std::runtime_error(
"No Vulkan GPUs found"
);
}
std::vector<VkPhysicalDevice> gpus(gpuCount);
vkEnumeratePhysicalDevices(
m_instance,
&gpuCount,
gpus.data()
);
for (const auto& gpu : gpus)
{
QueueFamilyIndices testIndices =
findQueueFamilies(gpu, m_surface);
if (testIndices.isComplete())
{
m_physicalDevice = gpu;
m_queueIndices = testIndices;
break;
}
}
if (m_physicalDevice == VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
vkDestroyInstance(m_instance, nullptr);
throw std::runtime_error(
"No suitable GPU found"
);
}
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(
m_physicalDevice,
&props
);
std::cout << "Selected GPU: "
<< props.deviceName
<< std::endl;
float queuePriority = 1.0f;
std::vector<VkDeviceQueueCreateInfo>
queueCreateInfos;
std::vector<uint32_t> uniqueQueues;
uniqueQueues.push_back(
*m_queueIndices.graphicsFamily
);
if (*m_queueIndices.presentFamily !=
*m_queueIndices.graphicsFamily)
{
uniqueQueues.push_back(
*m_queueIndices.presentFamily
);
}
for (uint32_t queueFamily : uniqueQueues)
{
VkDeviceQueueCreateInfo queueInfo{};
queueInfo.sType =
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueInfo.queueFamilyIndex = queueFamily;
queueInfo.queueCount = 1;
queueInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueInfo);
}
const std::vector<const char*>
deviceExtensions =
{
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
VkPhysicalDeviceFeatures deviceFeatures{};
VkDeviceCreateInfo deviceInfo{};
deviceInfo.sType =
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceInfo.queueCreateInfoCount =
static_cast<uint32_t>(
queueCreateInfos.size()
);
deviceInfo.pQueueCreateInfos =
queueCreateInfos.data();
deviceInfo.enabledExtensionCount =
static_cast<uint32_t>(
deviceExtensions.size()
);
deviceInfo.ppEnabledExtensionNames =
deviceExtensions.data();
deviceInfo.pEnabledFeatures = &deviceFeatures;
if (vkCreateDevice(
m_physicalDevice,
&deviceInfo,
nullptr,
&m_device) != VK_SUCCESS)
{
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
vkDestroyInstance(m_instance, nullptr);
throw std::runtime_error(
"Failed to create logical device"
);
}
std::cout << "Graphics queue: "
<< *m_queueIndices.graphicsFamily
<< std::endl;
std::cout << "Present queue: "
<< *m_queueIndices.presentFamily
<< std::endl;
vkGetDeviceQueue(
m_device,
*m_queueIndices.graphicsFamily,
0,
&m_graphicsQueue
);
vkGetDeviceQueue(
m_device,
*m_queueIndices.presentFamily,
0,
&m_presentQueue
);
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType =
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags =
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex =
*m_queueIndices.graphicsFamily;
if (vkCreateCommandPool(
m_device,
&poolInfo,
nullptr,
&m_commandPool) != VK_SUCCESS)
{
vkDestroyDevice(m_device, nullptr);
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
vkDestroyInstance(m_instance, nullptr);
throw std::runtime_error(
"Failed to create command pool"
);
}
std::cout << "Vulkan context created"
<< std::endl;
}
VulkanContext::~VulkanContext()
{
if (m_device != VK_NULL_HANDLE)
{
vkDestroyCommandPool(
m_device,
m_commandPool,
nullptr
);
vkDestroyDevice(m_device, nullptr);
m_device = VK_NULL_HANDLE;
}
if (m_surface != VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
m_surface = VK_NULL_HANDLE;
}
if (m_instance != VK_NULL_HANDLE)
{
vkDestroyInstance(m_instance, nullptr);
m_instance = VK_NULL_HANDLE;
}
}
void VulkanContext::release()
{
if (m_device != VK_NULL_HANDLE)
{
vkDeviceWaitIdle(m_device);
vkDestroyCommandPool(
m_device,
m_commandPool,
nullptr
);
vkDestroyDevice(m_device, nullptr);
m_device = VK_NULL_HANDLE;
}
if (m_surface != VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(
m_instance,
m_surface,
nullptr
);
m_surface = VK_NULL_HANDLE;
}
if (m_instance != VK_NULL_HANDLE)
{
vkDestroyInstance(m_instance, nullptr);
m_instance = VK_NULL_HANDLE;
}
}