swaping the chain

This commit is contained in:
Ben Kyd
2023-11-18 16:29:29 +00:00
parent 58ce34125f
commit 640fc4db5a
7 changed files with 275 additions and 72 deletions

View File

@@ -6,27 +6,10 @@
#include "yolo/yolo.hpp"
#include <map>
#include <optional>
#include <set>
namespace inferno::graphics {
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete()
{
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
@@ -112,6 +95,11 @@ std::vector<const char*> getRequiredExtensions()
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
yolo::info("Requested instance extensions:");
for (const auto& extension : extensions) {
yolo::info("\t{}", extension);
}
return extensions;
}
@@ -149,39 +137,6 @@ QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice
return indices;
}
bool device_evaluate(GraphicsDevice* g, VkPhysicalDevice device)
{
int score = 0;
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
yolo::info("Found device {}", deviceProperties.deviceName);
// Discrete GPUs have a significant performance advantage
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
yolo::info("Device {} is a discrete GPU", deviceProperties.deviceName);
score += 1000;
}
// We really want to favour ones with RayTracing support
// Maximum possible size of textures affects graphics quality
score += deviceProperties.limits.maxImageDimension2D;
// Application won't function without geometry shaders
if (!deviceFeatures.geometryShader)
return 0;
// Ensure that the device can process the graphics commands that we need
QueueFamilyIndices indices = device_get_queue_families(g, device);
if (!indices.isComplete())
return 0;
return score;
}
bool device_evaluate_extensions(VkPhysicalDevice device, std::vector<const char*> extensions)
{
uint32_t extensionCount;
@@ -199,39 +154,54 @@ bool device_evaluate_extensions(VkPhysicalDevice device, std::vector<const char*
return requiredExtensions.empty();
}
SwapChainSupportDetails device_get_swap_chain_support(GraphicsDevice* g, VkPhysicalDevice device)
bool device_evaluate(GraphicsDevice* g, VkPhysicalDevice device)
{
SwapChainSupportDetails details;
int score = 0;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, g->VulkanSurface, &details.capabilities);
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr);
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
if (formatCount != 0) {
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, details.formats.data());
yolo::info("Found device {}", deviceProperties.deviceName);
// Discrete GPUs have a significant performance advantage
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
yolo::info("Device {} is a discrete GPU", deviceProperties.deviceName);
score += 1000;
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, nullptr);
if (presentModeCount != 0) {
details.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, details.presentModes.data());
// does the device support the extensions we need?
if (!device_evaluate_extensions(device, DEVICE_EXTENSIONS)) {
return 0;
}
return details;
// Maximum possible size of textures affects graphics quality
score += deviceProperties.limits.maxImageDimension2D;
// Application won't function without geometry shaders
if (!deviceFeatures.geometryShader)
return 0;
// Ensure that the device can process the graphics commands that we need
QueueFamilyIndices indices = device_get_queue_families(g, device);
if (!indices.isComplete())
return 0;
return score;
}
GraphicsDevice* device_create()
{
GraphicsDevice* device = new GraphicsDevice();
device_create_vulkan_instance(device);
device_vulkan_debugger(device);
window_set_surface(device);
device->SurfaceSize = window_get_size();
device_create_vulkan_physical_device(device);
device_create_vulkan_logical_device(device);
device_create_command_pool(device);
return device;
}
@@ -371,7 +341,12 @@ void device_create_vulkan_logical_device(GraphicsDevice* device)
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = 0;
createInfo.enabledExtensionCount = static_cast<uint32_t>(DEVICE_EXTENSIONS.size());
createInfo.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data();
yolo::info("Requested device extensions:");
for (const auto& extension : DEVICE_EXTENSIONS) {
yolo::info("\t{}", extension);
}
#ifdef VALIDATION_LAYERS_ENABLED
createInfo.enabledLayerCount = static_cast<uint32_t>(VALIDATION_LAYERS.size());
@@ -389,4 +364,19 @@ void device_create_vulkan_logical_device(GraphicsDevice* device)
vkGetDeviceQueue(device->VulkanDevice, indices.presentFamily.value(), 0, &device->VulkanPresentQueue);
}
void device_create_command_pool(GraphicsDevice* device)
{
QueueFamilyIndices queueFamilyIndices = device_get_queue_families(device, device->VulkanPhysicalDevice);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
if (vkCreateCommandPool(device->VulkanDevice, &poolInfo, nullptr, &device->VulkanCommandPool) != VK_SUCCESS) {
yolo::error("failed to create command pool!");
}
yolo::info("Vulkan command pool created");
}
}

View File

@@ -2,6 +2,8 @@
#include "graphics.hpp"
#include <optional>
namespace inferno::graphics {
#define VALIDATION_LAYERS_ENABLED true
@@ -11,6 +13,10 @@ const std::vector<const char*> VALIDATION_LAYERS = {
};
#endif
const std::vector<const char*> DEVICE_EXTENSIONS = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
typedef struct GraphicsDevice {
VkInstance VulkanInstance;
VkDebugUtilsMessengerEXT VulkanDebugMessenger;
@@ -19,9 +25,21 @@ typedef struct GraphicsDevice {
VkSurfaceKHR VulkanSurface;
VkQueue VulkanGraphicsQueue;
VkQueue VulkanPresentQueue;
VkSwapchainKHR VulkanSwapChain;
VkCommandPool VulkanCommandPool;
glm::ivec2 SurfaceSize;
} GraphicsDevice;
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete()
{
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
// MUST ONLY BE CALLED AFTER GLFW INIT
GraphicsDevice* device_create();
void device_cleanup(GraphicsDevice* device);
@@ -30,4 +48,8 @@ void device_create_vulkan_instance(GraphicsDevice* device);
void device_vulkan_debugger(GraphicsDevice* device);
void device_create_vulkan_physical_device(GraphicsDevice* device);
void device_create_vulkan_logical_device(GraphicsDevice* device);
void device_create_command_pool(GraphicsDevice* device);
QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice device);
}

View File

@@ -1,6 +1,23 @@
#include "pipeline.hpp"
#include "device.hpp"
#include "swapchain.hpp"
namespace inferno::graphics {
Pipeline* pipeline_create(GraphicsDevice* device)
{
Pipeline* pipeline = new Pipeline();
pipeline->Device = device;
pipeline->Swap = swapchain_create(device, device->SurfaceSize);
return pipeline;
}
void pipeline_cleanup(Pipeline* pipeline)
{
delete pipeline;
}
} // namespace inferno::graphics

View File

@@ -5,12 +5,14 @@
namespace inferno::graphics {
struct GraphicsDevice;
struct SwapChain;
typedef struct Pipeline {
VkPipelineLayout Layout;
VkPipeline Pipeline;
GraphicsDevice* Device;
SwapChain* Swap;
} Pipeline;
Pipeline* pipeline_create(GraphicsDevice* device);
void pipeline_cleanup(Pipeline* pipeline);
}

View File

@@ -0,0 +1,150 @@
#include "swapchain.hpp"
#include "device.hpp"
#include "yolo/yolo.hpp"
namespace inferno::graphics {
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR Capabilities;
std::vector<VkSurfaceFormatKHR> Formats;
std::vector<VkPresentModeKHR> PresentModes;
};
SwapChainSupportDetails device_get_swapchain_support(GraphicsDevice* g, VkPhysicalDevice device)
{
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, g->VulkanSurface, &details.Capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr);
if (formatCount != 0) {
details.Formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, details.Formats.data());
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, nullptr);
if (presentModeCount != 0) {
details.PresentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, details.PresentModes.data());
}
return details;
}
VkSurfaceFormatKHR device_choose_swap_surface_format(const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
for (const auto& availableFormat : availableFormats) {
if (
availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return availableFormat;
}
}
return availableFormats[0];
}
VkPresentModeKHR device_choose_swap_present_mode(const std::vector<VkPresentModeKHR>& availablePresentModes)
{
for (const auto& availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D device_choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities, int width, int height)
{
if (capabilities.currentExtent.width != UINT32_MAX) {
return capabilities.currentExtent;
} else {
VkExtent2D actualExtent = { (uint32_t)width, (uint32_t)height };
actualExtent.width = std::max(
capabilities.minImageExtent.width,
std::min(capabilities.maxImageExtent.width, actualExtent.width));
actualExtent.height = std::max(
capabilities.minImageExtent.height,
std::min(capabilities.maxImageExtent.height, actualExtent.height));
return actualExtent;
}
}
SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size)
{
SwapChain* swapchain = new SwapChain();
SwapChainSupportDetails swapChainSupport = device_get_swapchain_support(device, device->VulkanPhysicalDevice);
VkSurfaceFormatKHR surfaceFormat = device_choose_swap_surface_format(swapChainSupport.Formats);
VkPresentModeKHR presentMode = device_choose_swap_present_mode(swapChainSupport.PresentModes);
VkExtent2D extent = device_choose_swap_extent(swapChainSupport.Capabilities, surface_size.x, surface_size.y);
uint32_t imageCount = swapChainSupport.Capabilities.minImageCount + 1;
if (swapChainSupport.Capabilities.maxImageCount > 0 && imageCount > swapChainSupport.Capabilities.maxImageCount) {
imageCount = swapChainSupport.Capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = device->VulkanSurface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
QueueFamilyIndices indices = device_get_queue_families(device, device->VulkanPhysicalDevice);
uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
if (indices.graphicsFamily != indices.presentFamily) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
} else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0; // Optional
createInfo.pQueueFamilyIndices = nullptr; // Optional
}
createInfo.preTransform = swapChainSupport.Capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(device->VulkanDevice, &createInfo, nullptr, &swapchain->Handle) != VK_SUCCESS) {
yolo::error("failed to create swap chain!");
exit(1);
}
yolo::info("Swap chain created");
vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, nullptr);
swapchain->Images.resize(imageCount);
vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, swapchain->Images.data());
swapchain->ImageFormat = surfaceFormat.format;
swapchain->Extent = extent;
swapchain->Device = device;
return swapchain;
}
void swapchain_cleanup(SwapChain* swapchain)
{
vkDestroySwapchainKHR(swapchain->Device->VulkanDevice, swapchain->Handle, nullptr);
delete swapchain;
}
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "graphics.hpp"
namespace inferno::graphics {
struct GraphicsDevice;
typedef struct SwapChain {
VkSwapchainKHR Handle;
VkFormat ImageFormat;
VkExtent2D Extent;
std::vector<VkImage> Images;
GraphicsDevice* Device;
} SwapChain;
SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size);
void swapchain_cleanup(SwapChain* swapchain);
}

View File

@@ -93,8 +93,8 @@ InfernoApp* inferno_create()
// Create window
graphics::window_create("Inferno v" INFERNO_VERSION, 1920, 1080);
graphics::GraphicsDevice* d = graphics::device_create();
graphics::GraphicsDevice* device = graphics::device_create();
graphics::Pipeline* p = graphics::pipeline_create(device);
// // setup the scene
// scene::Material* basicMaterial = new scene::Material("basic");