Create Vulkan swapchain

This commit is contained in:
Grayson Riffe 2025-02-11 03:24:53 -06:00
parent b0005c250f
commit 1824b73d66
9 changed files with 90 additions and 11 deletions

View File

@ -25,7 +25,7 @@ namespace nf {
// Start client
{
cli::Client client(config);
client::Client client(config);
client.run();
}

View File

@ -4,7 +4,7 @@
#include "Client.h"
#include "util.h"
namespace nf::cli {
namespace nf::client {
Client::Client(ClientConfig config)
: m_running(true)
, m_config(config)

View File

@ -5,7 +5,7 @@
#include "Window.h"
#include "render/RenderEngine.h"
namespace nf::cli {
namespace nf::client {
class Client {
public:
Client(ClientConfig config);

View File

@ -4,7 +4,7 @@
#include "Window.h"
#include "util.h"
namespace nf::cli {
namespace nf::client {
Window::Window(const char* windowTitle)
: m_handle(nullptr)
, m_wndClassName("NothinFancyWindow")

View File

@ -5,7 +5,7 @@
#include <Windows.h>
namespace nf::cli {
namespace nf::client {
class Window {
public:
Window(const char* windowTitle);

View File

@ -4,7 +4,7 @@
#include "RenderEngine.h"
#include "util.h"
namespace nf::cli::render {
namespace nf::client::render {
RenderEngine::RenderEngine(std::shared_ptr<Window> window, DisplayConfig display)
: m_window(window)
, m_display(display)
@ -17,16 +17,21 @@ namespace nf::cli::render {
, m_queueGraphics()
, m_queuePresent()
, m_renderPassOutput()
, m_swapchain()
, m_swapchainImageFormat()
, m_swapchainImages()
{
NFLog("Initializing render engine");
m_window->setDisplay(m_display);
m_window->show();
createInstance();
createSurface(m_window->getHandle());
pickPhysicalDevice();
createDevice();
createSwapchain();
createRenderPass();
m_window->show();
}
void RenderEngine::createInstance() {
@ -43,6 +48,7 @@ namespace nf::cli::render {
std::vector<const char*> instanceExtNames = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME };
instanceCI.ppEnabledExtensionNames = instanceExtNames.data();
instanceCI.enabledExtensionCount = instanceExtNames.size();
if (vkCreateInstance(&instanceCI, nullptr, &m_instance) != VK_SUCCESS)
NFError("Could not create Vulkan instance");
}
@ -50,6 +56,7 @@ namespace nf::cli::render {
void RenderEngine::createSurface(HWND window) {
VkWin32SurfaceCreateInfoKHR surfaceCI = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
surfaceCI.hwnd = window;
if (vkCreateWin32SurfaceKHR(m_instance, &surfaceCI, nullptr, &m_surface) != VK_SUCCESS)
NFError("Could not create Vulkan surface");
}
@ -154,6 +161,7 @@ namespace nf::cli::render {
const char* swapChainExtName = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
deviceCI.enabledExtensionCount = 1;
deviceCI.ppEnabledExtensionNames = &swapChainExtName;
if (vkCreateDevice(m_physicalDevice, &deviceCI, nullptr, &m_device) != VK_SUCCESS)
NFError("Could not create Vulkan device");
@ -161,9 +169,75 @@ namespace nf::cli::render {
vkGetDeviceQueue(m_device, m_queueFIPresent, 0, &m_queuePresent);
}
void RenderEngine::createSwapchain() {
VkSurfaceCapabilitiesKHR surfaceCapabilities = {};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfaceCapabilities);
uint32_t numSurfaceFormats = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &numSurfaceFormats, nullptr);
if (numSurfaceFormats == 0)
NFError("No Vulkan surface formats found");
std::vector<VkSurfaceFormatKHR> surfaceFormats(numSurfaceFormats);
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.data());
VkSurfaceFormatKHR chosenSurfaceFormat = surfaceFormats[0];
for (int i = 0; i < surfaceFormats.size(); i++)
if (surfaceFormats[i].format == VK_FORMAT_B8G8R8A8_SRGB && surfaceFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
chosenSurfaceFormat = surfaceFormats[i];
uint32_t numPresentModes = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &numPresentModes, nullptr);
if (numPresentModes == 0)
NFError("No Vulkan surface present modes found");
std::vector<VkPresentModeKHR> presentModes(numPresentModes);
vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &numPresentModes, presentModes.data());
VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;
// TODO: IMMEDIATE isn't always available, so do something about that
for (int i = 0; i < presentModes.size(); i++)
if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
chosenPresentMode = presentModes[i];
VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent;
if (swapchainExtent.width == std::numeric_limits<uint32_t>::max())
swapchainExtent.width = m_display.width, swapchainExtent.height = m_display.height;
uint32_t numRequestedImages = surfaceCapabilities.minImageCount + (surfaceCapabilities.minImageCount != surfaceCapabilities.maxImageCount ? 1 : 0);
VkSwapchainCreateInfoKHR swapchainCI = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
swapchainCI.surface = m_surface;
swapchainCI.minImageCount = numRequestedImages;
swapchainCI.imageFormat = m_swapchainImageFormat = chosenSurfaceFormat.format;
swapchainCI.imageColorSpace = chosenSurfaceFormat.colorSpace;
swapchainCI.imageExtent = swapchainExtent;
swapchainCI.imageArrayLayers = 1;
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
std::vector<uint32_t> queueFamilyIndices = { m_queueFIGraphics, m_queueFIPresent };
if (m_queueFIGraphics != m_queueFIPresent) {
swapchainCI.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCI.queueFamilyIndexCount = 2;
swapchainCI.pQueueFamilyIndices = queueFamilyIndices.data();
}
swapchainCI.preTransform = surfaceCapabilities.currentTransform;
swapchainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchainCI.presentMode = chosenPresentMode;
swapchainCI.clipped = VK_TRUE;
swapchainCI.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(m_device, &swapchainCI, nullptr, &m_swapchain) != VK_SUCCESS)
NFError("Could not create Vulkan swapchain");
uint32_t numSwapchainImages = 0;
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numSwapchainImages, nullptr);
m_swapchainImages.resize(numSwapchainImages);
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numSwapchainImages, m_swapchainImages.data());
}
void RenderEngine::createRenderPass() {
VkAttachmentDescription outputColorAttachment = {};
outputColorAttachment.format = VK_FORMAT_R8G8B8A8_SRGB;
outputColorAttachment.format = m_swapchainImageFormat;
outputColorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
outputColorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
outputColorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@ -186,12 +260,14 @@ namespace nf::cli::render {
renderPassCI.pAttachments = &outputColorAttachment;
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpassDescription;
if (vkCreateRenderPass(m_device, &renderPassCI, nullptr, &m_renderPassOutput) != VK_SUCCESS)
NFError("Could not create Vulkan render pass");
}
RenderEngine::~RenderEngine() {
vkDestroyRenderPass(m_device, m_renderPassOutput, nullptr);
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
vkDestroyDevice(m_device, nullptr);
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
vkDestroyInstance(m_instance, nullptr);

View File

@ -4,7 +4,7 @@
#include "client/Window.h"
#include "nf/config.h"
namespace nf::cli::render {
namespace nf::client::render {
class RenderEngine {
public:
RenderEngine(std::shared_ptr<Window> window, DisplayConfig display);
@ -15,6 +15,7 @@ namespace nf::cli::render {
void createSurface(HWND window);
void pickPhysicalDevice();
void createDevice();
void createSwapchain();
void createRenderPass();
std::shared_ptr<Window> m_window;
@ -27,5 +28,8 @@ namespace nf::cli::render {
VkDevice m_device;
VkQueue m_queueGraphics, m_queuePresent;
VkRenderPass m_renderPassOutput;
VkSwapchainKHR m_swapchain;
VkFormat m_swapchainImageFormat;
std::vector<VkImage> m_swapchainImages;
};
}

View File

@ -37,6 +37,7 @@
// Windows
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#undef max
// Vulkan
#define VK_USE_PLATFORM_WIN32_KHR

View File

@ -3,8 +3,6 @@
#include "util.h"
#undef max
namespace nf::util {
double getRand() {
static std::random_device dev;