From 1e00847f90d43420a7d48c004f5e670925101f7d Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Thu, 12 Jun 2025 16:50:37 +0200 Subject: [PATCH] Cleanup. --- Blaze.cpp | 800 +++++++++++++----------------------------- Blaze.sln.DotSettings | 2 + Blaze.vcxproj | 9 + Blaze.vcxproj.filters | 23 ++ Frame.cpp | 66 ++++ Frame.h | 25 ++ MacroUtils.h | 29 ++ MathUtil.h | 9 + MemoryUtils.h | 10 + RenderDevice.cpp | 369 +++++++++++++++++++ RenderDevice.h | 56 +++ vcpkg.json | 1 - 12 files changed, 848 insertions(+), 551 deletions(-) create mode 100644 Blaze.sln.DotSettings create mode 100644 Frame.cpp create mode 100644 Frame.h create mode 100644 MacroUtils.h create mode 100644 MathUtil.h create mode 100644 MemoryUtils.h create mode 100644 RenderDevice.cpp create mode 100644 RenderDevice.h diff --git a/Blaze.cpp b/Blaze.cpp index db44cca..162a982 100644 --- a/Blaze.cpp +++ b/Blaze.cpp @@ -1,335 +1,263 @@ // Blaze.cpp : This file contains the 'main' function. Program execution begins and ends there. // -#include -#include +#include #include #include -#include #include + +#define SDL_MAIN_USE_CALLBACKS 1 +#include #include +#include #include #include -#define ASSERT(COND) assert((COND)) - -#define VK_CHECK(RESULT) ASSERT((RESULT) == VK_SUCCESS) +#include "Frame.h" +#include "MacroUtils.h" +#include "MathUtil.h" +#include "RenderDevice.h" constexpr uint32_t WIDTH = 1280; constexpr uint32_t HEIGHT = 720; constexpr uint32_t NUM_FRAMES = 3; -uint32_t Clamp(uint32_t const val, uint32_t const minVal, uint32_t const maxVal) +struct MiscData { - return std::min(maxVal, std::max(val, minVal)); -} - -int main() -{ - volkInitialize(); - - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); - - SDL_Window* window = SDL_CreateWindow("Blaze Test", WIDTH, HEIGHT, SDL_WINDOW_VULKAN); - - VkApplicationInfo constexpr applicationInfo = { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = "Test", - .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), - .pEngineName = "Blaze", - .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), - .apiVersion = VK_API_VERSION_1_3, - }; - - uint32_t instanceExtensionCount; - char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount); - - VkInstanceCreateInfo const instanceCreateInfo = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &applicationInfo, - .enabledLayerCount = 0, - .ppEnabledLayerNames = nullptr, - .enabledExtensionCount = instanceExtensionCount, - .ppEnabledExtensionNames = instanceExtensions, - }; - - VkInstance instance; - VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); - volkLoadInstance(instance); - - VkSurfaceKHR surface; - ASSERT(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)); - - VkPhysicalDevice physicalDeviceInUse = nullptr; - VkDevice device = nullptr; - uint32_t directQueueFamilyIndex = 0; - VkQueue directQueue; - { - uint32_t physicalDeviceCount; - VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); - SDL_Log("Found %u GPUs", physicalDeviceCount); - - std::vector physicalDevices(physicalDeviceCount); - VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data())); - - - std::vector queueFamilyProperties; - for (VkPhysicalDevice const physicalDevice : physicalDevices) - { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - - SDL_Log("GPU: %s", properties.deviceName); - - SDL_Log("- API Version %d.%d.%d", - VK_API_VERSION_MAJOR(properties.apiVersion), - VK_API_VERSION_MINOR(properties.apiVersion), - VK_API_VERSION_PATCH(properties.apiVersion)); - - constexpr static uint32_t API_PATCH_BITS = 0xFFF; - if ((properties.apiVersion & (~API_PATCH_BITS)) < VK_API_VERSION_1_3) - { - continue; - } - - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) - { - continue; - } - - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - queueFamilyProperties.resize(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); - - for (uint32_t queueFamilyIndex = 0; VkQueueFamilyProperties const qfp : queueFamilyProperties) - { - bool hasGraphicsSupport = false; - bool hasComputeSupport = false; - bool hasTransferSupport = false; - bool hasPresentSupport = false; - - SDL_Log("- Queue [%d]", queueFamilyIndex); - if (qfp.queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - hasGraphicsSupport = true; - SDL_Log("-- Graphic"); - } - if (qfp.queueFlags & VK_QUEUE_COMPUTE_BIT) - { - hasComputeSupport = true; - SDL_Log("-- Compute"); - } - if (qfp.queueFlags & VK_QUEUE_TRANSFER_BIT) - { - hasTransferSupport = true; - SDL_Log("-- Transfer"); - } - - VkBool32 isSurfaceSupported; - VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &isSurfaceSupported)); - - if (isSurfaceSupported) - { - hasPresentSupport = true; - SDL_Log("-- Present"); - } - - if (hasGraphicsSupport and hasComputeSupport and hasTransferSupport and hasPresentSupport) - { - physicalDeviceInUse = physicalDevice; - directQueueFamilyIndex = queueFamilyIndex; - break; - } - - ++queueFamilyIndex; - } - } - - ASSERT(physicalDeviceInUse); - - float priority = 1.0f; - VkDeviceQueueCreateInfo queueCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = directQueueFamilyIndex, - .queueCount = 1, - .pQueuePriorities = &priority, - }; - - VkPhysicalDeviceVulkan13Features constexpr features13 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, - .pNext = nullptr, - .synchronization2 = true, - .dynamicRendering = true, - }; - - VkPhysicalDeviceFeatures features = { - .depthClamp = true, - .samplerAnisotropy = true, - }; - - std::array enabledDeviceExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME - }; - - VkDeviceCreateInfo const deviceCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = &features13, - .flags = 0, - .queueCreateInfoCount = 1, - .pQueueCreateInfos = &queueCreateInfo, - .enabledLayerCount = 0, - .ppEnabledLayerNames = nullptr, - .enabledExtensionCount = enabledDeviceExtensions.size(), - .ppEnabledExtensionNames = enabledDeviceExtensions.data(), - .pEnabledFeatures = &features, - }; - - VK_CHECK(vkCreateDevice(physicalDeviceInUse, &deviceCreateInfo, nullptr, &device)); - vkGetDeviceQueue(device, directQueueFamilyIndex, 0, &directQueue); - } - - VkFormat swapchainFormat; - VkExtent2D swapchainExtent = { WIDTH, HEIGHT }; - VkSwapchainKHR swapchain; - std::vector swapchainImages; - std::vector swapchainViews; - { - VkSurfaceCapabilitiesKHR capabilities; - VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDeviceInUse, surface, &capabilities)); - - // Image Count Calculation - uint32_t swapchainImageCount = NUM_FRAMES; - if (capabilities.maxImageCount > 0) - { - swapchainImageCount = std::min(swapchainImageCount, capabilities.maxImageCount); - } - swapchainImageCount = std::max(swapchainImageCount, capabilities.minImageCount); - - // Image Size calculation - { - auto [minWidth, minHeight] = capabilities.minImageExtent; - auto [maxWidth, maxHeight] = capabilities.maxImageExtent; - swapchainExtent.width = Clamp(swapchainExtent.width, minWidth, maxWidth); - swapchainExtent.height = Clamp(swapchainExtent.height, minHeight, maxHeight); - } - - uint32_t surfaceFormatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, nullptr); - std::vector surfaceFormats(surfaceFormatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, surfaceFormats.data()); - - VkSurfaceFormatKHR format; - format.format = VK_FORMAT_UNDEFINED; - for (auto& surfaceFormat : surfaceFormats) - { - if (surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - { - SDL_Log("Color Space SRGB Found %d", surfaceFormat.format); - if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_SRGB) { - format = surfaceFormat; - break; - } - if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB) { - format = surfaceFormat; - break; - } - if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM) { - format = surfaceFormat; - } - } - } - ASSERT(format.format != VK_FORMAT_UNDEFINED); - swapchainFormat = format.format; - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, nullptr); - std::vector presentModes(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, presentModes.data()); - - VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; - for (VkPresentModeKHR presentModeIter : presentModes) - { - if (presentModeIter == VK_PRESENT_MODE_FIFO_RELAXED_KHR) - { - presentMode = presentModeIter; - break; - } - - if (presentModeIter == VK_PRESENT_MODE_MAILBOX_KHR) - { - presentMode = presentModeIter; - } - } - - VkSwapchainCreateInfoKHR const swapchainCreateInfo = { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = nullptr, - .flags = 0, - .surface = surface, - .minImageCount = swapchainImageCount, - .imageFormat = format.format, - .imageColorSpace = format.colorSpace, - .imageExtent = swapchainExtent, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = presentMode, - .clipped = false, - .oldSwapchain = nullptr, - }; - - VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain)); - - swapchainImageCount = 0; - vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr); - swapchainImages.resize(swapchainImageCount); - vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages.data()); - - for (VkImage image : swapchainImages) { - - VkImageViewCreateInfo const viewCreateInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format.format, - .components = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - } - }; - - VkImageView view; - VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &view)); - - swapchainViews.push_back(view); - } - } - VkPipelineLayout pipelineLayout; VkPipeline trianglePipeline; + + VkImageMemoryBarrier2 acquireToRenderBarrier; + VkDependencyInfo acquireToRenderDependency; + VkImageMemoryBarrier2 renderToPresentBarrier; + VkDependencyInfo renderToPresentDependency; + + void init(RenderDevice const& renderDevice); + void cleanup(RenderDevice const& renderDevice); +}; + +struct AppState +{ + SDL_Window* window; + std::unique_ptr renderDevice; + MiscData miscData; + + [[nodiscard]] bool isInit() const; + void cleanup(); + + AppState(); + + AppState(AppState const& other) = delete; + AppState(AppState&& other) noexcept = delete; + + AppState& operator=(AppState const& other) = delete; + AppState& operator=(AppState&& other) noexcept = delete; + + ~AppState(); +}; + +static_assert(sizeof(AppState) < 1024); + +bool AppState::isInit() const +{ + return window and renderDevice and renderDevice->isInit(); +} + +void AppState::cleanup() +{ + if (!isInit()) return; + + renderDevice->waitIdle(); + + miscData.cleanup(*renderDevice); + renderDevice->cleanup(); + SDL_DestroyWindow(window); +} + +AppState::AppState() +{ + window = SDL_CreateWindow("Blaze Test", WIDTH, HEIGHT, SDL_WINDOW_VULKAN); + if (!window) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", SDL_GetError()); + abort(); + } + + renderDevice = std::make_unique(RenderDevice::CreateInfo{ .window = window }); + if (!renderDevice->isInit()) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "RenderDevice failed to init"); + abort(); + } + + miscData.init(*renderDevice); +} + +AppState::~AppState() +{ + cleanup(); +} + +SDL_AppResult SDL_AppInit(void** pAppState, int, char**) +{ + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + + *pAppState = new AppState{}; + AppState& appState = *static_cast(*pAppState); + + if (!appState.isInit()) + { + return SDL_APP_FAILURE; + } + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void* appstate) +{ + AppState& appState = *static_cast(appstate); + RenderDevice& renderDevice = *appState.renderDevice; + MiscData& misc = appState.miscData; + Frame& currentFrame = renderDevice.frames[renderDevice.frameIndex]; + + VK_CHECK(vkWaitForFences(renderDevice.device, 1, ¤tFrame.frameReadyToReuse, VK_TRUE, std::numeric_limits::max())); + // All resources of frame 'frameIndex' are free. + + uint32_t currentImageIndex; + VK_CHECK(vkAcquireNextImageKHR(renderDevice.device, renderDevice.swapchain, std::numeric_limits::max(), currentFrame.imageAcquiredSemaphore, nullptr, ¤tImageIndex)); + + VK_CHECK(vkResetFences(renderDevice.device, 1, ¤tFrame.frameReadyToReuse)); + VK_CHECK(vkResetCommandPool(renderDevice.device, currentFrame.commandPool, 0)); + + misc.acquireToRenderBarrier.image = renderDevice.swapchainImages[currentImageIndex]; + misc.renderToPresentBarrier.image = renderDevice.swapchainImages[currentImageIndex]; + + VkCommandBuffer cmd = currentFrame.commandBuffer; + + VkCommandBufferBeginInfo constexpr beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = 0, + .pInheritanceInfo = nullptr, + }; + + VkClearColorValue constexpr static BLACK_CLEAR = { + .float32 = { 0.0f, 0.0f, 0.0f, 1.0f }, + }; + + VK_CHECK(vkBeginCommandBuffer(cmd, &beginInfo)); + { + VkRenderingAttachmentInfo const attachmentInfo = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = renderDevice.swapchainViews[currentImageIndex], + .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = nullptr, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {.color = BLACK_CLEAR}, + }; + + VkRenderingInfo renderingInfo = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = {.offset = {0,0}, .extent = renderDevice.swapchainExtent}, + .layerCount = 1, + .viewMask = 0, + .colorAttachmentCount = 1, + .pColorAttachments = &attachmentInfo, + .pDepthAttachment = nullptr, + .pStencilAttachment = nullptr, + }; + + vkCmdPipelineBarrier2(cmd, &misc.acquireToRenderDependency); + vkCmdBeginRendering(cmd, &renderingInfo); + { + VkViewport viewport = { + .x = 0, + .y = static_cast(renderDevice.swapchainExtent.height), + .width = static_cast(renderDevice.swapchainExtent.width), + .height = -static_cast(renderDevice.swapchainExtent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + vkCmdSetViewport(cmd, 0, 1, &viewport); + VkRect2D scissor = { + .offset = {0, 0}, + .extent = renderDevice.swapchainExtent, + }; + vkCmdSetScissor(cmd, 0, 1, &scissor); + + // Render Something? + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.trianglePipeline); + vkCmdDraw(cmd, 3, 1, 0, 0); + + } + vkCmdEndRendering(cmd); + vkCmdPipelineBarrier2(cmd, &misc.renderToPresentDependency); + } + VK_CHECK(vkEndCommandBuffer(cmd)); + + VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo const submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = ¤tFrame.imageAcquiredSemaphore, + .pWaitDstStageMask = &stageMask, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 1, + .pSignalSemaphores = ¤tFrame.renderFinishedSemaphore, + }; + VK_CHECK(vkQueueSubmit(renderDevice.directQueue, 1, &submitInfo, currentFrame.frameReadyToReuse)); + + VkPresentInfoKHR const presentInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = ¤tFrame.renderFinishedSemaphore, + .swapchainCount = 1, + .pSwapchains = &renderDevice.swapchain, + .pImageIndices = ¤tImageIndex, + .pResults = nullptr, + }; + + VK_CHECK(vkQueuePresentKHR(renderDevice.directQueue, &presentInfo)); + + renderDevice.frameIndex = (renderDevice.frameIndex + 1) % NUM_FRAMES; + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void*, SDL_Event* event) +{ + if (event->type == SDL_EVENT_QUIT) + { + return SDL_APP_SUCCESS; + } + + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void* appstate, SDL_AppResult) +{ + AppState *appState = static_cast (appstate); + + appState->cleanup(); + + // Not necessary, this memory will be winked out. + delete appState; +} + +void MiscData::init(RenderDevice const& renderDevice) +{ + VkDevice const device = renderDevice.device; { size_t dataSize; void* rawData = SDL_LoadFile("Triangle.spv", &dataSize); @@ -505,7 +433,7 @@ int main() VkPipelineRenderingCreateInfoKHR const renderingCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, .colorAttachmentCount = 1, - .pColorAttachmentFormats = &swapchainFormat, + .pColorAttachmentFormats = &renderDevice.swapchainFormat, }; VkGraphicsPipelineCreateInfo const graphicsPipelineCreateInfo = { @@ -537,64 +465,7 @@ int main() SDL_free(rawData); } - // Create 1 command pool per frame. - std::array commandPools; - { - VkCommandPoolCreateInfo const commandPoolCreateInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, - .queueFamilyIndex = directQueueFamilyIndex, - }; - for (auto& commandPool : commandPools) { - VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool)); - } - } - - std::array commandBuffers; - { - uint32_t index = 0; - for (auto& commandPool : commandPools) { - VkCommandBufferAllocateInfo const commandBufferAllocateInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .pNext = nullptr, - .commandPool = commandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1, - }; - VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffers[index])); - - ++index; - } - } - - std::array imageAcquiredSemaphores; - std::array renderFinishedSemaphores; - std::array frameInUseFences; - { - VkSemaphoreCreateInfo constexpr semaphoreCreateInfo = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - .pNext = nullptr, - .flags = 0 - }; - for (auto& semaphore : imageAcquiredSemaphores) { - VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); - } - for (auto& semaphore : renderFinishedSemaphores) { - VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); - } - - VkFenceCreateInfo constexpr fenceCreateInfo = { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .pNext = nullptr, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, - }; - for (auto& fence : frameInUseFences) { - VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); - } - } - - VkImageMemoryBarrier2 acquireToRenderBarrier = { + acquireToRenderBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -613,7 +484,7 @@ int main() .layerCount = 1, } }; - VkDependencyInfo acquireToRenderDependency = { + acquireToRenderDependency = { .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .pNext = nullptr, .dependencyFlags = 0, @@ -625,7 +496,7 @@ int main() .pImageMemoryBarriers = &acquireToRenderBarrier, }; - VkImageMemoryBarrier2 renderToPresentBarrier = { + renderToPresentBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -644,7 +515,7 @@ int main() .layerCount = 1, } }; - VkDependencyInfo renderToPresentDependency = { + renderToPresentDependency = { .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .pNext = nullptr, .dependencyFlags = 0, @@ -656,183 +527,12 @@ int main() .pImageMemoryBarriers = &renderToPresentBarrier, }; - uint32_t frameIndex = 0; - bool isRunning = true; - while (isRunning) - { - SDL_PumpEvents(); +} - SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_EVENT_QUIT: - isRunning = false; - break; - default: - break; - } - } - - VkFence fence = frameInUseFences[frameIndex]; - - VK_CHECK(vkWaitForFences(device, 1, &fence, VK_TRUE, std::numeric_limits::max())); - // All resources of frame 'frameIndex' are free. - - VkSemaphore imageAcquiredSemaphore = imageAcquiredSemaphores[frameIndex]; - - uint32_t currentImageIndex; - VK_CHECK(vkAcquireNextImageKHR(device, swapchain, std::numeric_limits::max(), imageAcquiredSemaphore, nullptr, ¤tImageIndex)); - - VK_CHECK(vkResetFences(device, 1, &fence)); - VK_CHECK(vkResetCommandPool(device, commandPools[frameIndex], 0)); - - acquireToRenderBarrier.image = swapchainImages[currentImageIndex]; - renderToPresentBarrier.image = swapchainImages[currentImageIndex]; - - VkCommandBuffer cmd = commandBuffers[frameIndex]; - - VkCommandBufferBeginInfo constexpr beginInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = nullptr, - .flags = 0, - .pInheritanceInfo = nullptr, - }; - - VkClearColorValue constexpr static BLACK_CLEAR = { - .float32 = { 0.0f, 0.0f, 0.0f, 1.0f }, - }; - - VK_CHECK(vkBeginCommandBuffer(cmd, &beginInfo)); - { - VkRenderingAttachmentInfo const attachmentInfo = { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = swapchainViews[currentImageIndex], - .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = nullptr, - .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {.color = BLACK_CLEAR}, - }; - - VkRenderingInfo renderingInfo = { - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .pNext = nullptr, - .flags = 0, - .renderArea = {.offset = {0,0}, .extent = swapchainExtent}, - .layerCount = 1, - .viewMask = 0, - .colorAttachmentCount = 1, - .pColorAttachments = &attachmentInfo, - .pDepthAttachment = nullptr, - .pStencilAttachment = nullptr, - }; - - vkCmdPipelineBarrier2(cmd, &acquireToRenderDependency); - vkCmdBeginRendering(cmd, &renderingInfo); - { - VkViewport viewport = { - .x = 0, - .y = static_cast(swapchainExtent.height), - .width = static_cast(swapchainExtent.width), - .height = -static_cast(swapchainExtent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - vkCmdSetViewport(cmd, 0, 1, &viewport); - VkRect2D scissor = { - .offset = {0, 0}, - .extent = swapchainExtent, - }; - vkCmdSetScissor(cmd, 0, 1, &scissor); - - // Render Something? - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, trianglePipeline); - vkCmdDraw(cmd, 3, 1, 0, 0); - - } - vkCmdEndRendering(cmd); - vkCmdPipelineBarrier2(cmd, &renderToPresentDependency); - } - VK_CHECK(vkEndCommandBuffer(cmd)); - - VkSemaphore renderingFinishedSemaphore = renderFinishedSemaphores[frameIndex]; - - VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo const submitInfo = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = nullptr, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &imageAcquiredSemaphore, - .pWaitDstStageMask = &stageMask, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - .signalSemaphoreCount = 1, - .pSignalSemaphores = &renderingFinishedSemaphore, - }; - VK_CHECK(vkQueueSubmit(directQueue, 1, &submitInfo, fence)); - - VkPresentInfoKHR const presentInfo = { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = nullptr, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &renderingFinishedSemaphore, - .swapchainCount = 1, - .pSwapchains = &swapchain, - .pImageIndices = ¤tImageIndex, - .pResults = nullptr, - }; - - VK_CHECK(vkQueuePresentKHR(directQueue, &presentInfo)); - - frameIndex = (frameIndex + 1) % NUM_FRAMES; - } - - VK_CHECK(vkDeviceWaitIdle(device)); - - for (VkFence fence : frameInUseFences) - { - vkDestroyFence(device, fence, nullptr); - } - - for (VkSemaphore semaphore : imageAcquiredSemaphores) - { - vkDestroySemaphore(device, semaphore, nullptr); - } - - for (VkSemaphore semaphore : renderFinishedSemaphores) - { - vkDestroySemaphore(device, semaphore, nullptr); - } - - for (VkCommandPool commandPool : commandPools) - { - vkDestroyCommandPool(device, commandPool, nullptr); - } - - for (VkImageView view : swapchainViews) - { - vkDestroyImageView(device, view, nullptr); - } +void MiscData::cleanup(RenderDevice const& renderDevice) +{ + VkDevice const device = renderDevice.device; vkDestroyPipeline(device, trianglePipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - - vkDestroySwapchainKHR(device, swapchain, nullptr); - - vkDestroyDevice(device, nullptr); - - SDL_Vulkan_DestroySurface(instance, surface, nullptr); - - vkDestroyInstance(instance, nullptr); - - volkFinalize(); - - SDL_DestroyWindow(window); - - SDL_Quit(); -} \ No newline at end of file +} diff --git a/Blaze.sln.DotSettings b/Blaze.sln.DotSettings new file mode 100644 index 0000000..1c66e89 --- /dev/null +++ b/Blaze.sln.DotSettings @@ -0,0 +1,2 @@ + + <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy></NamingElement> \ No newline at end of file diff --git a/Blaze.vcxproj b/Blaze.vcxproj index 017513e..b9659c6 100644 --- a/Blaze.vcxproj +++ b/Blaze.vcxproj @@ -149,6 +149,8 @@ + + @@ -173,6 +175,13 @@ + + + + + + + diff --git a/Blaze.vcxproj.filters b/Blaze.vcxproj.filters index d3a09f9..f1a08ec 100644 --- a/Blaze.vcxproj.filters +++ b/Blaze.vcxproj.filters @@ -21,6 +21,12 @@ Source Files + + Source Files + + + Source Files + @@ -44,4 +50,21 @@ Shader Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + \ No newline at end of file diff --git a/Frame.cpp b/Frame.cpp new file mode 100644 index 0000000..a22bdf9 --- /dev/null +++ b/Frame.cpp @@ -0,0 +1,66 @@ +#include "Frame.h" + +#include + +#include "MacroUtils.h" +#include "RenderDevice.h" + +bool Frame::isInit() const +{ + return static_cast(commandPool); +} + +Frame::Frame(RenderDevice const& renderDevice) +{ + VkDevice const device = renderDevice.device; + + VkCommandPoolCreateInfo const commandPoolCreateInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = renderDevice.directQueueFamilyIndex, + }; + VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool)); + + VkCommandBufferAllocateInfo const commandBufferAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer)); + + VkSemaphoreCreateInfo constexpr semaphoreCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = nullptr, + .flags = 0 + }; + VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAcquiredSemaphore)); + VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphore)); + + VkFenceCreateInfo constexpr fenceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &frameReadyToReuse)); +} + +void Frame::cleanup(RenderDevice const& renderDevice) +{ + if (!isInit()) return; + + VkDevice const device = renderDevice.device; + + vkDestroyCommandPool(device, Take(commandPool), nullptr); + vkDestroyFence(device, Take(frameReadyToReuse), nullptr); + vkDestroySemaphore(device, Take(imageAcquiredSemaphore), nullptr); + vkDestroySemaphore(device, Take(renderFinishedSemaphore), nullptr); +} + +Frame::~Frame() +{ + // Manual Cleanup Required. + ASSERT(not isInit()); +} diff --git a/Frame.h b/Frame.h new file mode 100644 index 0000000..0926393 --- /dev/null +++ b/Frame.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +struct RenderDevice; + +struct Frame +{ + VkCommandPool commandPool; + VkCommandBuffer commandBuffer; + VkSemaphore imageAcquiredSemaphore; + VkSemaphore renderFinishedSemaphore; + VkFence frameReadyToReuse; + + [[nodiscard]] bool isInit() const; + + explicit Frame(RenderDevice const& renderDevice); + + void cleanup(RenderDevice const& renderDevice); + + ~Frame(); +}; + + diff --git a/MacroUtils.h b/MacroUtils.h new file mode 100644 index 0000000..d4ddca9 --- /dev/null +++ b/MacroUtils.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#define G_ASSERT(COND) \ +do { \ + auto _result = (COND); \ + if (not _result) { \ + __debugbreak(); \ + assert(_result && #COND); \ + } \ +} while(false) + +#define ASSERT(COND) G_ASSERT(COND) + +#define VK_CHECK(RESULT) \ +do { \ + auto _result = (RESULT); \ + if (_result != VK_SUCCESS) { \ + SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, \ + "" #RESULT " failed with %d at %s:%d", \ + _result, __FILE__, __LINE__); \ + __debugbreak(); \ + assert(_result == VK_SUCCESS); \ + } \ +} while(false) + +#define Take(OBJ) std::exchange(OBJ, {}) \ No newline at end of file diff --git a/MathUtil.h b/MathUtil.h new file mode 100644 index 0000000..211b9b3 --- /dev/null +++ b/MathUtil.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +template +T Clamp(T const val, T const minVal, T const maxVal) +{ + return std::min(maxVal, std::max(val, minVal)); +} \ No newline at end of file diff --git a/MemoryUtils.h b/MemoryUtils.h new file mode 100644 index 0000000..3059f97 --- /dev/null +++ b/MemoryUtils.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include + +using global_allocator_t = foonathan::memory::memory_stack; + +using subsystem_allocator_t = foonathan::memory::memory_stack>; +using temporary_allocator_t = foonathan::memory::temporary_allocator; \ No newline at end of file diff --git a/RenderDevice.cpp b/RenderDevice.cpp new file mode 100644 index 0000000..0993f09 --- /dev/null +++ b/RenderDevice.cpp @@ -0,0 +1,369 @@ +#include "RenderDevice.h" + +#include "MacroUtils.h" + +#include + +#include + +#include "Frame.h" +#include "MathUtil.h" + +RenderDevice::RenderDevice(CreateInfo const& createInfo) +{ + ASSERT(createInfo.window); + + volkInitialize(); + + // Create Instance + { + VkApplicationInfo constexpr applicationInfo = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = "Test", + .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), + .pEngineName = "Blaze", + .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), + .apiVersion = VK_API_VERSION_1_3, + }; + + uint32_t instanceExtensionCount; + char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount); + + VkInstanceCreateInfo const instanceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = 0, + .ppEnabledLayerNames = nullptr, + .enabledExtensionCount = instanceExtensionCount, + .ppEnabledExtensionNames = instanceExtensions, + }; + + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + volkLoadInstance(instance); + } + + // Create Surface + ASSERT(SDL_Vulkan_CreateSurface(createInfo.window, instance, nullptr, &surface)); + + // Create Device and Queue + { + uint32_t physicalDeviceCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); + SDL_Log("Found %u GPUs", physicalDeviceCount); + + std::vector physicalDevices(physicalDeviceCount); + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data())); + + + std::vector queueFamilyProperties; + for (VkPhysicalDevice const physicalDevice : physicalDevices) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + + SDL_Log("GPU: %s", properties.deviceName); + + SDL_Log("- API Version %d.%d.%d", + VK_API_VERSION_MAJOR(properties.apiVersion), + VK_API_VERSION_MINOR(properties.apiVersion), + VK_API_VERSION_PATCH(properties.apiVersion)); + + constexpr static uint32_t API_PATCH_BITS = 0xFFF; + if ((properties.apiVersion & (~API_PATCH_BITS)) < VK_API_VERSION_1_3) + { + continue; + } + + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) + { + continue; + } + + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); + + for (uint32_t queueFamilyIndex = 0; VkQueueFamilyProperties const qfp : queueFamilyProperties) + { + bool hasGraphicsSupport = false; + bool hasComputeSupport = false; + bool hasTransferSupport = false; + bool hasPresentSupport = false; + + SDL_Log("- Queue [%d]", queueFamilyIndex); + if (qfp.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + hasGraphicsSupport = true; + SDL_Log("-- Graphic"); + } + if (qfp.queueFlags & VK_QUEUE_COMPUTE_BIT) + { + hasComputeSupport = true; + SDL_Log("-- Compute"); + } + if (qfp.queueFlags & VK_QUEUE_TRANSFER_BIT) + { + hasTransferSupport = true; + SDL_Log("-- Transfer"); + } + + VkBool32 isSurfaceSupported; + VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &isSurfaceSupported)); + + if (isSurfaceSupported) + { + hasPresentSupport = true; + SDL_Log("-- Present"); + } + + if (hasGraphicsSupport and hasComputeSupport and hasTransferSupport and hasPresentSupport) + { + physicalDeviceInUse = physicalDevice; + directQueueFamilyIndex = queueFamilyIndex; + break; + } + + ++queueFamilyIndex; + } + } + + ASSERT(physicalDeviceInUse); + + float priority = 1.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = directQueueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &priority, + }; + + VkPhysicalDeviceVulkan13Features constexpr features13 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .pNext = nullptr, + .synchronization2 = true, + .dynamicRendering = true, + }; + + VkPhysicalDeviceFeatures features = { + .depthClamp = true, + .samplerAnisotropy = true, + }; + + std::array enabledDeviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + VkDeviceCreateInfo const deviceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = &features13, + .flags = 0, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queueCreateInfo, + .enabledLayerCount = 0, + .ppEnabledLayerNames = nullptr, + .enabledExtensionCount = static_cast(enabledDeviceExtensions.size()), + .ppEnabledExtensionNames = enabledDeviceExtensions.data(), + .pEnabledFeatures = &features, + }; + + VK_CHECK(vkCreateDevice(physicalDeviceInUse, &deviceCreateInfo, nullptr, &device)); + vkGetDeviceQueue(device, directQueueFamilyIndex, 0, &directQueue); + } + + // Swapchain creation + swapchainExtent = { createInfo.width, createInfo.height }; + { + VkSurfaceCapabilitiesKHR capabilities; + VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDeviceInUse, surface, &capabilities)); + + // Image Count Calculation + uint32_t swapchainImageCount = 3; + if (capabilities.maxImageCount > 0) + { + swapchainImageCount = std::min(swapchainImageCount, capabilities.maxImageCount); + } + swapchainImageCount = std::max(swapchainImageCount, capabilities.minImageCount); + + // Image Size calculation + { + auto [minWidth, minHeight] = capabilities.minImageExtent; + auto [maxWidth, maxHeight] = capabilities.maxImageExtent; + swapchainExtent.width = Clamp(swapchainExtent.width, minWidth, maxWidth); + swapchainExtent.height = Clamp(swapchainExtent.height, minHeight, maxHeight); + } + + uint32_t surfaceFormatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, nullptr); + std::vector surfaceFormats(surfaceFormatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, surfaceFormats.data()); + + VkSurfaceFormatKHR format = { + .format = VK_FORMAT_UNDEFINED, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + }; + for (auto& surfaceFormat : surfaceFormats) + { + if (surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + SDL_Log("Color Space SRGB Found %d", surfaceFormat.format); + if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_SRGB) { + format = surfaceFormat; + break; + } + if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB) { + format = surfaceFormat; + break; + } + if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM) { + format = surfaceFormat; + } + } + } + ASSERT(format.format != VK_FORMAT_UNDEFINED); + swapchainFormat = format.format; + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, nullptr); + std::vector presentModes(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, presentModes.data()); + + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + for (VkPresentModeKHR presentModeIter : presentModes) + { + if (presentModeIter == VK_PRESENT_MODE_FIFO_RELAXED_KHR) + { + presentMode = presentModeIter; + break; + } + + if (presentModeIter == VK_PRESENT_MODE_MAILBOX_KHR) + { + presentMode = presentModeIter; + } + } + + VkSwapchainCreateInfoKHR const swapchainCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = surface, + .minImageCount = swapchainImageCount, + .imageFormat = format.format, + .imageColorSpace = format.colorSpace, + .imageExtent = swapchainExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = false, + .oldSwapchain = nullptr, + }; + + VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain)); + + swapchainImageCount = 0; + vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr); + SDL_Log("%llu", swapchainImages.size()); + swapchainImages.resize(swapchainImageCount); + vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages.data()); + + swapchainViews.reserve(swapchainImageCount); + for (auto& image : swapchainImages) { + + VkImageViewCreateInfo const viewCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format.format, + .components = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + + VkImageView view; + VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &view)); + swapchainViews.push_back(view); + } + } + + // Init frames. + { + auto count = static_cast(swapchainImages.size()); + frames.reserve(count); + for (uint32_t i = 0; i != count; ++i) + { + frames.emplace_back(*this); + } + } +} + +RenderDevice::~RenderDevice() +{ + cleanup(); +} + +inline bool RenderDevice::isInit() const +{ + return instance and device; +} + +void RenderDevice::cleanup() +{ + if (not isInit()) + return; + + for (Frame& frame : frames) + { + frame.cleanup(*this); + } + frames.clear(); + + for (auto const& view : swapchainViews) + { + vkDestroyImageView(device, view, nullptr); + } + swapchainViews.clear(); + swapchainImages.clear(); + + vkDestroySwapchainKHR(device, Take(swapchain), nullptr); + + vkDestroyDevice(Take(device), nullptr); + + SDL_Vulkan_DestroySurface(instance, Take(surface), nullptr); + + vkDestroyInstance(Take(instance), nullptr); + + volkFinalize(); +} + +void RenderDevice::waitIdle() const +{ + VK_CHECK(vkDeviceWaitIdle(device)); +} + +uint32_t RenderDevice::getNumFrames() const +{ + return static_cast(frames.size()); +} diff --git a/RenderDevice.h b/RenderDevice.h new file mode 100644 index 0000000..2749792 --- /dev/null +++ b/RenderDevice.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include + +#include +#include + +struct Frame; + +/// The Rendering backend abstraction +/// If this fails to initialize, we crash +/// +/// TODO: Fail elegantly. +struct RenderDevice +{ + struct CreateInfo + { + SDL_Window* window = nullptr; + uint32_t width = 640; + uint32_t height = 480; + }; + + VkInstance instance; + VkSurfaceKHR surface; + VkPhysicalDevice physicalDeviceInUse; + VkDevice device; + VkQueue directQueue; + uint32_t directQueueFamilyIndex; + // TODO: Pack? + + VkFormat swapchainFormat; + VkExtent2D swapchainExtent; + VkSwapchainKHR swapchain; + std::vector swapchainImages; + std::vector swapchainViews; + + std::vector frames; + uint32_t frameIndex = 0; + + [[nodiscard]] bool isInit() const; + void cleanup(); + void waitIdle() const; + [[nodiscard]] uint32_t getNumFrames() const; + + explicit RenderDevice(CreateInfo const& createInfo); + + RenderDevice(RenderDevice const&) = delete; + RenderDevice& operator=(RenderDevice const&) = delete; + + RenderDevice(RenderDevice&&) noexcept = delete; + RenderDevice& operator=(RenderDevice&&) noexcept = delete; + + ~RenderDevice(); +}; \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index b5dc1c5..b008f48 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,5 @@ { "dependencies": [ - "fmt", "volk", "shader-slang", {