Refactor Frames into util.

This commit is contained in:
Anish Bhobe 2024-07-10 20:56:18 +02:00
parent b48fb3168d
commit 52b3c671c6
5 changed files with 225 additions and 139 deletions

View File

@ -2,7 +2,11 @@
cmake_minimum_required(VERSION 3.13) 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_link_libraries(util_helper PRIVATE aster_core)
target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

137
samples/00_util/frame.cpp Normal file
View File

@ -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, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>),
"Waiting for fence {} failed.", frameIndex);
u32 imageIndex;
bool imageAcquired = false;
while (!imageAcquired)
{
auto result = m_Device->m_Device.acquireNextImageKHR(swapchain->m_Swapchain, MaxValue<u64>,
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, &currentFrame->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;
}

47
samples/00_util/frame.h Normal file
View File

@ -0,0 +1,47 @@
// =============================================
// Aster: frame.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
#include "helpers.h"
#include <EASTL/fixed_vector.h>
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<Frame, 4> 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);
};

View File

@ -17,3 +17,26 @@ class PhysicalDevices;
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices); PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice); QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
eastl::vector<u32> ReadFile(cstr fileName); eastl::vector<u32> ReadFile(cstr fileName);
#define AbortIfFailed(RESULT) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedM(RESULT, MSG) \
do \
{ \
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false)

View File

@ -16,6 +16,7 @@
#include "helpers.h" #include "helpers.h"
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "frame.h"
#include "image.h" #include "image.h"
#include "stb_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 VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv";
#define AbortIfFailed(RESULT) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedM(RESULT, MSG) \
do \
{ \
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false)
struct ImageFile struct ImageFile
{ {
void *m_Data = nullptr; void *m_Data = nullptr;
@ -56,7 +34,7 @@ struct ImageFile
u32 m_NumChannels = 0; u32 m_NumChannels = 0;
bool Load(cstr fileName); bool Load(cstr fileName);
usize GetSize() const; [[nodiscard]] usize GetSize() const;
~ImageFile(); ~ImageFile();
}; };
@ -132,19 +110,6 @@ struct Camera
mat4 m_Perspective; 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 int
main(int, char **) main(int, char **)
{ {
@ -484,48 +449,26 @@ main(int, char **)
frames.emplace_back(&device, queueAllocation.m_Family, i); frames.emplace_back(&device, queueAllocation.m_Family, i);
} }
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
Time::Init(); Time::Init();
INFO("Starting loop"); INFO("Starting loop");
u32 frameIndex = 0;
while (window.Poll()) while (window.Poll())
{ {
Frame *currentFrame = &frames[frameIndex];
Time::Update(); Time::Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo.Write(&device, 0, sizeof camera, &camera); ubo.Write(&device, 0, sizeof camera, &camera);
AbortIfFailedMV(device.m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>), Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window);
"Waiting for fence {} failed.", frameIndex);
u32 imageIndex;
auto result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
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<f32>(swapchain.m_Extent.height); viewport.y = Cast<f32>(swapchain.m_Extent.height);
viewport.width = Cast<f32>(swapchain.m_Extent.width); viewport.width = Cast<f32>(swapchain.m_Extent.width);
viewport.height = -Cast<f32>(swapchain.m_Extent.height); viewport.height = -Cast<f32>(swapchain.m_Extent.height);
scissor.extent = swapchain.m_Extent; 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, &currentFrame->m_FrameAvailableFence), "Fence {} reset failed.",
frameIndex);
AbortIfFailedMV(device.m_Device.resetCommandPool(currentFrame->m_Pool, {}), "Command pool {} reset failed.",
frameIndex);
// Frame *currentFrame = &frames[frameIndex];
u32 imageIndex = currentFrame->m_ImageIdx;
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
@ -585,33 +528,7 @@ main(int, char **)
}; };
AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
vk::PresentInfoKHR presentInfo = { currentFrame->Present(commandQueue, &swapchain, &window);
.waitSemaphoreCount = 1,
.pWaitSemaphores = &currentFrame->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<f32>(swapchain.m_Extent.height);
viewport.width = Cast<f32>(swapchain.m_Extent.width);
viewport.height = -Cast<f32>(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;
} }
AbortIfFailed(device.m_Device.waitIdle()); AbortIfFailed(device.m_Device.waitIdle());
@ -627,38 +544,6 @@ main(int, char **)
return 0; 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 Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain) CreatePipeline(const Device *device, const Swapchain *swapchain)
{ {
@ -819,13 +704,3 @@ CreateShader(const Device *device, cstr shaderFile)
"Shader {} could not be created.", shaderFile); "Shader {} could not be created.", shaderFile);
return shaderModule; 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");
}