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", {