diff --git a/samples/00_util/CMakeLists.txt b/samples/00_util/CMakeLists.txt index bd190b5..0178f88 100644 --- a/samples/00_util/CMakeLists.txt +++ b/samples/00_util/CMakeLists.txt @@ -2,7 +2,11 @@ cmake_minimum_required(VERSION 3.13) -add_library(util_helper STATIC helpers.h helpers.cpp) +add_library(util_helper STATIC + helpers.h + helpers.cpp + frame.cpp + frame.h) target_link_libraries(util_helper PRIVATE aster_core) target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/samples/00_util/frame.cpp b/samples/00_util/frame.cpp new file mode 100644 index 0000000..25bba57 --- /dev/null +++ b/samples/00_util/frame.cpp @@ -0,0 +1,137 @@ +// ============================================= +// Aster: frame.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "frame.h" + +#include "device.h" +#include "helpers.h" +#include "swapchain.h" + +Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount) + : m_FrameIdx(0) + , m_ImageIdx(0) +{ + m_Device = device; + + const vk::CommandPoolCreateInfo commandPoolCreateInfo = { + .flags = vk::CommandPoolCreateFlagBits::eTransient, + .queueFamilyIndex = queueFamilyIndex, + }; + AbortIfFailedMV(device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool), + "Could not command pool for frame {}", frameCount); + + constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled}; + AbortIfFailedMV(device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence), + "Could not create a fence for frame {}", frameCount); + + constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {}; + AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem), + "Could not create IA semaphore for frame {}.", frameCount); + AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem), + "Could not create RF semaphore for frame {}.", frameCount); + + const vk::CommandBufferAllocateInfo allocateInfo = { + .commandPool = m_Pool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }; + + AbortIfFailed(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer)); + + DEBUG("Frame {} created successfully.", frameCount); +} + +void +Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Window *window) +{ + + vk::PresentInfoKHR presentInfo = { + .waitSemaphoreCount = 1, + .pWaitSemaphores = &m_RenderFinishSem, + .swapchainCount = 1, + .pSwapchains = &swapchain->m_Swapchain, + .pImageIndices = &m_ImageIdx, + .pResults = nullptr, + }; + switch (auto result = commandQueue.presentKHR(&presentInfo)) + { + case vk::Result::eSuccess: + break; + case vk::Result::eErrorOutOfDateKHR: + case vk::Result::eSuboptimalKHR: + INFO("Recreating Swapchain. Cause: {}", result); + swapchain->Create(window); + break; // Present failed. We do nothing. Frame is skipped. + default: + AbortIfFailedM(result, "Swapchain Present failed."); + } +} + +Frame::~Frame() +{ + m_Device->m_Device.destroy(m_RenderFinishSem, nullptr); + m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr); + m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr); + m_Device->m_Device.destroy(m_Pool, nullptr); + + DEBUG("Destoryed Frame"); +} + +// Frame Manager + +FrameManager::FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight) + : m_Device(device) + , m_FramesInFlight(framesInFlight) +{ + for (u32 i = 0; i < framesInFlight; ++i) + { + m_Frames.emplace_back(device, queueFamilyIndex, i); + } +} + +Frame * +FrameManager::GetNextFrame(Swapchain *swapchain, const Window *window) +{ + + Frame *currentFrame = &m_Frames[m_CurrentFrameIdx]; + u32 frameIndex = m_CurrentFrameIdx; + + m_CurrentFrameIdx = (m_CurrentFrameIdx + 1) % m_FramesInFlight; + + AbortIfFailedMV(m_Device->m_Device.waitForFences(1, ¤tFrame->m_FrameAvailableFence, true, MaxValue), + "Waiting for fence {} failed.", frameIndex); + + u32 imageIndex; + bool imageAcquired = false; + while (!imageAcquired) + { + auto result = m_Device->m_Device.acquireNextImageKHR(swapchain->m_Swapchain, MaxValue, + currentFrame->m_ImageAcquireSem, nullptr, &imageIndex); + switch (result) + { + case vk::Result::eSuccess: + case vk::Result::eSuboptimalKHR: // Suboptimal can still render. Better to let this go for semaphores etc. + imageAcquired = true; + break; // Image acquired. Break out of loop. + case vk::Result::eErrorOutOfDateKHR: + INFO("Recreating Swapchain. Cause: {}", result); + swapchain->Create(window); + break; // Image acquire has failed. We move to the next frame. + default: + AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex); + } + } + + // Reset fences here. In case swapchain was out of date, we leave the fences signalled. + AbortIfFailedMV(m_Device->m_Device.resetFences(1, ¤tFrame->m_FrameAvailableFence), "Fence {} reset failed.", + frameIndex); + + AbortIfFailedMV(m_Device->m_Device.resetCommandPool(currentFrame->m_Pool, {}), "Command pool {} reset failed.", + frameIndex); + + currentFrame->m_ImageIdx = imageIndex; + + return currentFrame; +} diff --git a/samples/00_util/frame.h b/samples/00_util/frame.h new file mode 100644 index 0000000..ec9fafd --- /dev/null +++ b/samples/00_util/frame.h @@ -0,0 +1,47 @@ +// ============================================= +// Aster: frame.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" +#include "helpers.h" + +#include + +struct Device; +struct Window; +struct Swapchain; + +struct Frame +{ + // Persistent + const Device *m_Device; + vk::CommandPool m_Pool; + vk::CommandBuffer m_CommandBuffer; + vk::Fence m_FrameAvailableFence; + vk::Semaphore m_ImageAcquireSem; + vk::Semaphore m_RenderFinishSem; + u32 m_FrameIdx; + + // Transient + u32 m_ImageIdx; + + void Present(vk::Queue commandQueue, Swapchain *swapchain, const Window *window); + + Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount); + ~Frame(); +}; + +struct FrameManager +{ + const Device *m_Device; + eastl::fixed_vector m_Frames{}; + u32 m_CurrentFrameIdx = 0; + u32 m_FramesInFlight = 0; + + FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight); + + Frame *GetNextFrame(Swapchain *swapchain, const Window *window); +}; diff --git a/samples/00_util/helpers.h b/samples/00_util/helpers.h index 1b21bf3..28cd67f 100644 --- a/samples/00_util/helpers.h +++ b/samples/00_util/helpers.h @@ -16,4 +16,27 @@ class PhysicalDevices; PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices); QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice); -eastl::vector ReadFile(cstr fileName); \ No newline at end of file +eastl::vector ReadFile(cstr fileName); + +#define AbortIfFailed(RESULT) \ + do \ + { \ + vk::Result _checkResultValue_; \ + ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), "Cause: {}", _checkResultValue_) \ + THEN_ABORT(_checkResultValue_); \ + } while (false) + +#define AbortIfFailedMV(RESULT, MSG, EXTRA) \ + do \ + { \ + vk::Result _checkResultValue_; \ + ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \ + THEN_ABORT(_checkResultValue_); \ + } while (false) + +#define AbortIfFailedM(RESULT, MSG) \ + do \ + { \ + auto _checkResultValue_ = Cast(RESULT); \ + ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ + } while (false) diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index c189cf8..605a168 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -16,6 +16,7 @@ #include "helpers.h" #define STB_IMAGE_IMPLEMENTATION +#include "frame.h" #include "image.h" #include "stb_image.h" @@ -25,29 +26,6 @@ constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv"; -#define AbortIfFailed(RESULT) \ - do \ - { \ - vk::Result _checkResultValue_; \ - ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), "Cause: {}", _checkResultValue_) \ - THEN_ABORT(_checkResultValue_); \ - } while (false) - -#define AbortIfFailedMV(RESULT, MSG, EXTRA) \ - do \ - { \ - vk::Result _checkResultValue_; \ - ERROR_IF(Failed(_checkResultValue_ = Cast(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \ - THEN_ABORT(_checkResultValue_); \ - } while (false) - -#define AbortIfFailedM(RESULT, MSG) \ - do \ - { \ - auto _checkResultValue_ = Cast(RESULT); \ - ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ - } while (false) - struct ImageFile { void *m_Data = nullptr; @@ -56,7 +34,7 @@ struct ImageFile u32 m_NumChannels = 0; bool Load(cstr fileName); - usize GetSize() const; + [[nodiscard]] usize GetSize() const; ~ImageFile(); }; @@ -132,19 +110,6 @@ struct Camera mat4 m_Perspective; }; -struct Frame -{ - const Device *m_Device; - vk::CommandPool m_Pool; - vk::CommandBuffer m_CommandBuffer; - vk::Fence m_FrameAvailableFence; - vk::Semaphore m_ImageAcquireSem; - vk::Semaphore m_RenderFinishSem; - - Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount); - ~Frame(); -}; - int main(int, char **) { @@ -484,48 +449,26 @@ main(int, char **) frames.emplace_back(&device, queueAllocation.m_Family, i); } + FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; + Time::Init(); INFO("Starting loop"); - u32 frameIndex = 0; while (window.Poll()) { - Frame *currentFrame = &frames[frameIndex]; Time::Update(); camera.m_Model *= rotate(mat4{1.0f}, Cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo.Write(&device, 0, sizeof camera, &camera); - AbortIfFailedMV(device.m_Device.waitForFences(1, ¤tFrame->m_FrameAvailableFence, true, MaxValue), - "Waiting for fence {} failed.", frameIndex); - - u32 imageIndex; - auto result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue, - currentFrame->m_ImageAcquireSem, nullptr, &imageIndex); - if (Failed(result)) - { - switch (result) - { - case vk::Result::eErrorOutOfDateKHR: - case vk::Result::eSuboptimalKHR: - INFO("Recreating Swapchain. Cause: {}", result); - swapchain.Create(&window); - viewport.y = Cast(swapchain.m_Extent.height); - viewport.width = Cast(swapchain.m_Extent.width); - viewport.height = -Cast(swapchain.m_Extent.height); - scissor.extent = swapchain.m_Extent; - continue; // Image acquire has failed. We move to the next frame. - default: - AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex); - } - } - // Reset fences here. In case swapchain was out of date, we leave the fences signalled. - AbortIfFailedMV(device.m_Device.resetFences(1, ¤tFrame->m_FrameAvailableFence), "Fence {} reset failed.", - frameIndex); - - AbortIfFailedMV(device.m_Device.resetCommandPool(currentFrame->m_Pool, {}), "Command pool {} reset failed.", - frameIndex); + Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window); + viewport.y = Cast(swapchain.m_Extent.height); + viewport.width = Cast(swapchain.m_Extent.width); + viewport.height = -Cast(swapchain.m_Extent.height); + scissor.extent = swapchain.m_Extent; + // Frame *currentFrame = &frames[frameIndex]; + u32 imageIndex = currentFrame->m_ImageIdx; vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; @@ -585,33 +528,7 @@ main(int, char **) }; AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); - vk::PresentInfoKHR presentInfo = { - .waitSemaphoreCount = 1, - .pWaitSemaphores = ¤tFrame->m_RenderFinishSem, - .swapchainCount = 1, - .pSwapchains = &swapchain.m_Swapchain, - .pImageIndices = &imageIndex, - .pResults = nullptr, - }; - result = commandQueue.presentKHR(&presentInfo); - if (Failed(result)) - { - switch (result) - { - case vk::Result::eErrorOutOfDateKHR: - case vk::Result::eSuboptimalKHR: - INFO("Recreating Swapchain. Cause: {}", result); - swapchain.Create(&window); - viewport.y = Cast(swapchain.m_Extent.height); - viewport.width = Cast(swapchain.m_Extent.width); - viewport.height = -Cast(swapchain.m_Extent.height); - scissor.extent = swapchain.m_Extent; - break; // Present failed. We redo the frame. - default: - AbortIfFailedM(result, "Swapchain Present failed."); - } - } - frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT; + currentFrame->Present(commandQueue, &swapchain, &window); } AbortIfFailed(device.m_Device.waitIdle()); @@ -627,38 +544,6 @@ main(int, char **) return 0; } -Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount) -{ - m_Device = device; - - const vk::CommandPoolCreateInfo commandPoolCreateInfo = { - .flags = vk::CommandPoolCreateFlagBits::eTransient, - .queueFamilyIndex = queueFamilyIndex, - }; - AbortIfFailedMV(device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool), - "Could not command pool for frame {}", frameCount); - - constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled}; - AbortIfFailedMV(device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence), - "Could not create a fence for frame {}", frameCount); - - constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {}; - AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem), - "Could not create IA semaphore for frame {}.", frameCount); - AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem), - "Could not create RF semaphore for frame {}.", frameCount); - - const vk::CommandBufferAllocateInfo allocateInfo = { - .commandPool = m_Pool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1, - }; - - AbortIfFailed(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer)); - - DEBUG("Frame {} created successfully.", frameCount); -} - Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain) { @@ -818,14 +703,4 @@ CreateShader(const Device *device, cstr shaderFile) AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule), "Shader {} could not be created.", shaderFile); return shaderModule; -} - -Frame::~Frame() -{ - m_Device->m_Device.destroy(m_RenderFinishSem, nullptr); - m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr); - m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr); - m_Device->m_Device.destroy(m_Pool, nullptr); - - DEBUG("Destoryed Frame"); } \ No newline at end of file