|
|
|
@ -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);
|
|
|
|
|