mirror of
https://github.com/bailwillharr/engine.git
synced 2024-09-21 04:51:18 +00:00
Re-implement most of the vulkan init code
This commit is contained in:
parent
729fa09302
commit
fecf1cccc5
@ -49,7 +49,7 @@ namespace engine {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_pimpl;
|
std::unique_ptr<Impl> pimpl;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user