Re-implement most of the vulkan init code

This commit is contained in:
Bailey Harrison 2022-10-17 17:36:25 +01:00
parent 729fa09302
commit fecf1cccc5
2 changed files with 554 additions and 10 deletions

View File

@ -49,7 +49,7 @@ namespace engine {
private: private:
class Impl; class Impl;
std::unique_ptr<Impl> m_pimpl; std::unique_ptr<Impl> pimpl;
}; };

View File

@ -1,4 +1,5 @@
// The implementation of the graphics layer using Vulkan 1.3 // The implementation of the graphics layer using Vulkan 1.3.
// This uses SDL specific code
#ifdef ENGINE_BUILD_VULKAN #ifdef ENGINE_BUILD_VULKAN
@ -21,9 +22,14 @@
#include <SDL_vulkan.h> #include <SDL_vulkan.h>
#include <assert.h> #include <assert.h>
#include <unordered_set>
#include <array>
namespace engine { namespace engine {
// singleton variable
static GFXDevice* s_gfx_device_instance = nullptr;
// structures // structures
struct LayerInfo { struct LayerInfo {
@ -31,6 +37,40 @@ namespace engine {
std::optional<std::vector<VkLayerProperties>::iterator> validationLayer; std::optional<std::vector<VkLayerProperties>::iterator> validationLayer;
}; };
struct SwapchainSupportDetails {
VkSurfaceCapabilitiesKHR caps{};
std::vector<VkSurfaceFormatKHR> formats{};
std::vector<VkPresentModeKHR> presentModes{};
};
struct Queue {
uint32_t familyIndex;
uint32_t queueIndex;
bool supportsGraphics = false;
bool supportsTransfer = false;
bool supportsCompute = false;
VkQueue handle;
};
struct Swapchain {
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
VkExtent2D extent;
VkFormat format;
std::vector<VkImage> images{};
std::vector<VkImageView> imageViews{};
};
// enums
enum class QueueFlags : uint32_t {
GRAPHICS = (1 << 0),
TRANSFER = (1 << 1),
COMPUTE = (1 << 2),
};
// functions // functions
static std::vector<const char*> getRequiredVulkanExtensions(SDL_Window* window) static std::vector<const char*> getRequiredVulkanExtensions(SDL_Window* window)
@ -154,16 +194,196 @@ namespace engine {
return debugMessengerInfo; return debugMessengerInfo;
} }
static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance)
{
VkSurfaceKHR surface;
if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) {
throw std::runtime_error("Unable to create window surface");
}
return surface;
}
// returns the index of the queue supporting the requested flags
static Queue getQueueSupporting(const std::vector<Queue> queues, QueueFlags flags)
{
uint32_t bitmask = static_cast<uint32_t>(flags);
for (int i = 0; i < queues.size(); i++) {
if (bitmask & static_cast<uint32_t>(QueueFlags::GRAPHICS)) {
if (queues[i].supportsGraphics == false) continue;
}
if (bitmask & static_cast<uint32_t>(QueueFlags::TRANSFER)) {
if (queues[i].supportsTransfer == false) continue;
}
if (bitmask & static_cast<uint32_t>(QueueFlags::COMPUTE)) {
if (queues[i].supportsCompute == false) continue;
}
return queues[i];
}
throw std::runtime_error("Unable to find the requested queue");
}
static void createSwapchain(VkDevice device, const std::vector<Queue> queues, SDL_Window* window, VkSurfaceKHR surface, const SwapchainSupportDetails& supportDetails, Swapchain* swapchain)
{
VkResult res;
VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0];
for (const auto& format : supportDetails.formats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
chosenSurfaceFormat = format; // prefer using srgb non linear colors
}
}
VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;
for (const auto& presMode : supportDetails.presentModes) {
if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) {
chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing
}
}
VkExtent2D chosenSwapExtent{};
if (supportDetails.caps.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
chosenSwapExtent = supportDetails.caps.currentExtent;
}
else {
// if fb size isn't already found, get it from SDL
int width, height;
SDL_Vulkan_GetDrawableSize(window, &width, &height);
chosenSwapExtent.width = static_cast<uint32_t>(width);
chosenSwapExtent.height = static_cast<uint32_t>(height);
chosenSwapExtent.width = std::clamp(
chosenSwapExtent.width,
supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width);
chosenSwapExtent.height = std::clamp(
chosenSwapExtent.height,
supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height);
}
uint32_t imageCount = supportDetails.caps.minImageCount + 1;
if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) {
imageCount = supportDetails.caps.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0,
.surface = surface,
.minImageCount = imageCount,
.imageFormat = chosenSurfaceFormat.format,
.imageColorSpace = chosenSurfaceFormat.colorSpace,
.imageExtent = chosenSwapExtent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.preTransform = supportDetails.caps.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = chosenPresentMode,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
};
std::array<uint32_t, 2> queueFamilyIndices{
getQueueSupporting(queues, QueueFlags::GRAPHICS).familyIndex,
getQueueSupporting(queues, QueueFlags::TRANSFER).familyIndex
};
// if graphics and transfer queues aren't in the same family
if (queueFamilyIndices[0] != queueFamilyIndices[1]) {
throw std::runtime_error("Graphics and transfer queues must be in the same family");
}
res = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain->swapchain);
assert(res == VK_SUCCESS);
// get all the image handles
uint32_t swapchainImageCount = 0;
res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, nullptr);
assert(res == VK_SUCCESS);
swapchain->images.resize(swapchainImageCount);
res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, swapchain->images.data());
assert(res == VK_SUCCESS);
swapchain->format = chosenSurfaceFormat.format;
swapchain->extent = chosenSwapExtent;
// create image views
swapchain->imageViews.clear();
for (VkImage image : swapchain->images) {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.image = image;
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapchain->format;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
VkImageView imageView;
res = vkCreateImageView(device, &createInfo, nullptr, &imageView);
assert(res == VK_SUCCESS);
swapchain->imageViews.push_back(imageView);
}
}
struct GFXDevice::Impl { struct GFXDevice::Impl {
VkInstance m_instance = VK_NULL_HANDLE; VkInstance instance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
SwapchainSupportDetails swapchainSupportDetails{}; // available capabilities, formats, and present modes
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
std::vector<Queue> queues{};
VkCommandPool commandPool = VK_NULL_HANDLE;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VmaAllocator allocator = nullptr;
Swapchain swapchain{};
}; };
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window)
{ {
m_pimpl = std::make_unique<Impl>(); // ensure there is only one instance of this class
if (s_gfx_device_instance != nullptr) {
throw std::runtime_error("Multiple gfxdevice classes cannot be created");
}
s_gfx_device_instance = this;
pimpl = std::make_unique<Impl>();
VkResult res; VkResult res;
@ -257,7 +477,7 @@ namespace engine {
} }
#endif #endif
res = vkCreateInstance(&instanceInfo, nullptr, &m_pimpl->m_instance); res = vkCreateInstance(&instanceInfo, nullptr, &pimpl->instance);
if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
throw std::runtime_error("The graphics driver is incompatible with vulkan"); throw std::runtime_error("The graphics driver is incompatible with vulkan");
} }
@ -266,7 +486,7 @@ namespace engine {
// load the instance functions // load the instance functions
volkLoadInstanceOnly(m_pimpl->m_instance); volkLoadInstanceOnly(pimpl->instance);
@ -274,18 +494,342 @@ namespace engine {
{ {
VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo(); VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo();
VkResult res = vkCreateDebugUtilsMessengerEXT(m_pimpl->m_instance, &createInfo, nullptr, &m_pimpl->m_debugMessenger); VkResult res = vkCreateDebugUtilsMessengerEXT(pimpl->instance, &createInfo, nullptr, &pimpl->debugMessenger);
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
} }
// get the surface
pimpl->surface = createSurface(window, pimpl->instance);
// Select a physical device and get capabilities, features, and display modes.
// Create a logical device and create the queues and their corresponding command buffers.
{
// enumerate physical devices
uint32_t physDeviceCount = 0;
VkResult res;
res = vkEnumeratePhysicalDevices(pimpl->instance, &physDeviceCount, nullptr);
assert(res == VK_SUCCESS);
if (physDeviceCount == 0) {
throw std::runtime_error("No GPU found with vulkan support!");
}
std::vector<VkPhysicalDevice> physicalDevices(physDeviceCount);
res = vkEnumeratePhysicalDevices(pimpl->instance, &physDeviceCount, physicalDevices.data());
assert(res == VK_SUCCESS);
// find suitable device:
const std::vector<const char*> requiredDeviceExtensions{
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
for (const auto& dev : physicalDevices) {
// first, check extension support
uint32_t extensionCount;
res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data());
assert(res == VK_SUCCESS);
for (const auto& extToFind : requiredDeviceExtensions) {
bool extFound = false;
for (const auto& ext : availableExtensions) {
if (strcmp(extToFind, ext.extensionName) == 0) {
extFound = true;
}
}
if (!extFound) {
continue;
}
}
// get swapchain support details:
// get surface capabilities
res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, pimpl->surface, &pimpl->swapchainSupportDetails.caps);
assert(res == VK_SUCCESS);
// check there is at least one supported surface format
uint32_t surfaceFormatCount = 0;
res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, nullptr);
assert(res == VK_SUCCESS);
if (surfaceFormatCount == 0) {
continue;
}
pimpl->swapchainSupportDetails.formats.resize(surfaceFormatCount);
res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, pimpl->swapchainSupportDetails.formats.data());
assert(res == VK_SUCCESS);
// check there is at least one supported present mode
uint32_t surfacePresentModeCount = 0;
res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, nullptr);
assert(res == VK_SUCCESS);
if (surfacePresentModeCount == 0) {
continue;
}
pimpl->swapchainSupportDetails.presentModes.resize(surfacePresentModeCount);
res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, pimpl->swapchainSupportDetails.presentModes.data());
assert(res == VK_SUCCESS);
// check physical device properties
VkPhysicalDeviceProperties devProps;
vkGetPhysicalDeviceProperties(dev, &devProps);
// check that the device supports vulkan 1.3
if (devProps.apiVersion < VK_API_VERSION_1_3) {
continue;
}
pimpl->physicalDevice = dev;
break;
} // end for()
if (pimpl->physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("No suitable Vulkan physical device found");
}
VkPhysicalDeviceProperties devProps;
vkGetPhysicalDeviceProperties(pimpl->physicalDevice, &devProps);
INFO("Selected physical device: {}", devProps.deviceName);
TRACE("Supported present modes:");
for (const auto& presMode : pimpl->swapchainSupportDetails.presentModes) {
switch (presMode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR");
break;
case VK_PRESENT_MODE_MAILBOX_KHR:
TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR");
break;
case VK_PRESENT_MODE_FIFO_KHR:
TRACE("\tVK_PRESENT_MODE_FIFO_KHR");
break;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR");
break;
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR");
break;
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR");
break;
default:
TRACE("\tUNKNOWN DISPLAY MODE");
break;
}
}
// Get the queue families and find ones that support graphics, transfer, and compute
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(pimpl->physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(pimpl->physicalDevice, &queueFamilyCount, queueFamilies.data());
std::optional<uint32_t> graphicsFamilyIndex;
std::optional<uint32_t> transferFamilyIndex;
std::optional<uint32_t> computeFamilyIndex;
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkQueueFamilyProperties family = queueFamilies[i];
if (family.queueCount > 0) {
if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
TRACE("GRAPHICS:");
graphicsFamilyIndex = i;
}
if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) {
TRACE("TRANSFER:");
transferFamilyIndex = i;
}
if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) {
TRACE("COMPUTE:");
computeFamilyIndex = i;
}
TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount);
}
}
if (graphicsFamilyIndex.has_value() == false ||
transferFamilyIndex.has_value() == false) {
throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags");
}
// there is no guaranteed support for compute queues
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
// use a set to filter out duplicate indices
std::unordered_set<uint32_t> uniqueQueueFamilies{};
if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value());
if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value());
if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value());
float queuePriority = 1.0f;
for (uint32_t family : uniqueQueueFamilies) {
// create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute
Queue newQueue{};
newQueue.familyIndex = family;
newQueue.queueIndex = 0;
if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true;
if (transferFamilyIndex == family) newQueue.supportsTransfer = true;
if (computeFamilyIndex == family) newQueue.supportsCompute = true;
TRACE("Creating queue from family {}", family);
VkDeviceQueueCreateInfo queueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = family,
.queueCount = 1,
.pQueuePriorities = &queuePriority,
};
queueCreateInfos.push_back(queueCreateInfo);
pimpl->queues.push_back(newQueue);
}
// check the physical device is compatible with the surface
VkBool32 graphicsQueueCanPresent;
res = vkGetPhysicalDeviceSurfaceSupportKHR(pimpl->physicalDevice, graphicsFamilyIndex.value(), pimpl->surface, &graphicsQueueCanPresent);
assert(res == VK_SUCCESS);
if (graphicsQueueCanPresent != VK_TRUE) {
throw std::runtime_error("The selected queue family does not support this surface");
}
VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(),
.pQueueCreateInfos = queueCreateInfos.data(),
// IGNORED: .enabledLayerCount
// IGNORED: .ppEnabledLayerNames
.enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(),
.ppEnabledExtensionNames = requiredDeviceExtensions.data(),
.pEnabledFeatures = nullptr,
};
res = vkCreateDevice(pimpl->physicalDevice, &deviceCreateInfo, nullptr, &pimpl->device);
if (res != VK_SUCCESS) {
throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res));
}
volkLoadDevice(pimpl->device);
for (auto& q : pimpl->queues) {
vkGetDeviceQueue(pimpl->device, q.familyIndex, q.queueIndex, &q.handle);
}
Queue gfxQueue = getQueueSupporting(pimpl->queues, QueueFlags::GRAPHICS);
VkCommandPoolCreateInfo gfxCmdPoolInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = gfxQueue.familyIndex,
};
res = vkCreateCommandPool(pimpl->device, &gfxCmdPoolInfo, nullptr, &pimpl->commandPool);
assert(res == VK_SUCCESS);
VkCommandBufferAllocateInfo gfxCmdBufInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pimpl->commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1
};
res = vkAllocateCommandBuffers(pimpl->device, &gfxCmdBufInfo, &pimpl->commandBuffer);
assert(res == VK_SUCCESS);
}
// now make the memory allocator using vk_mem_alloc.h
{
VmaVulkanFunctions functions{
.vkGetInstanceProcAddr = nullptr,
.vkGetDeviceProcAddr = nullptr,
.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties,
.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties,
.vkAllocateMemory = vkAllocateMemory,
.vkFreeMemory = vkFreeMemory,
.vkMapMemory = vkMapMemory,
.vkUnmapMemory = vkUnmapMemory,
.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges,
.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges,
.vkBindBufferMemory = vkBindBufferMemory,
.vkBindImageMemory = vkBindImageMemory,
.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements,
.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements,
.vkCreateBuffer = vkCreateBuffer,
.vkDestroyBuffer = vkDestroyBuffer,
.vkCreateImage = vkCreateImage,
.vkDestroyImage = vkDestroyImage,
.vkCmdCopyBuffer = vkCmdCopyBuffer,
.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2,
.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2,
.vkBindBufferMemory2KHR = vkBindBufferMemory2,
.vkBindImageMemory2KHR = vkBindImageMemory2,
.vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2,
.vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements,
.vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements,
};
VmaAllocatorCreateInfo createInfo{
.flags = 0,
.physicalDevice = pimpl->physicalDevice,
.device = pimpl->device,
.preferredLargeHeapBlockSize = 0,
.pAllocationCallbacks = nullptr,
.pDeviceMemoryCallbacks = nullptr,
.pHeapSizeLimit = nullptr,
.pVulkanFunctions = &functions,
.instance = pimpl->instance,
.vulkanApiVersion = VK_API_VERSION_1_3,
.pTypeExternalMemoryHandleTypes = nullptr
};
VkResult res = vmaCreateAllocator(&createInfo, &pimpl->allocator);
assert(res == VK_SUCCESS);
}
// Now make the swapchain
createSwapchain(pimpl->device, pimpl->queues, window, pimpl->surface, pimpl->swapchainSupportDetails, &pimpl->swapchain);
} }
GFXDevice::~GFXDevice() GFXDevice::~GFXDevice()
{ {
TRACE("Destroying GFXDevice..."); TRACE("Destroying GFXDevice...");
vkDestroyDebugUtilsMessengerEXT(m_pimpl->m_instance, m_pimpl->m_debugMessenger, nullptr); for (VkImageView view : pimpl->swapchain.imageViews) {
vkDestroyInstance(m_pimpl->m_instance, nullptr); vkDestroyImageView(pimpl->device, view, nullptr);
}
vkDestroySwapchainKHR(pimpl->device, pimpl->swapchain.swapchain, nullptr);
vmaDestroyAllocator(pimpl->allocator);
vkDestroyCommandPool(pimpl->device, pimpl->commandPool, nullptr);
vkDestroyDevice(pimpl->device, nullptr);
vkDestroySurfaceKHR(pimpl->instance, pimpl->surface, nullptr);
vkDestroyDebugUtilsMessengerEXT(pimpl->instance, pimpl->debugMessenger, nullptr);
vkDestroyInstance(pimpl->instance, nullptr);
s_gfx_device_instance = nullptr;
} }
void GFXDevice::draw() void GFXDevice::draw()