project-aster/samples/04_scenes/main.cpp

586 lines
24 KiB
C++

// =============================================
// Aster: main.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "aster/core/context.h"
#include "aster/core/device.h"
#include "aster/core/physical_device.h"
#include "aster/core/swapchain.h"
#include "aster/core/window.h"
#include "aster/core/image.h"
#include "aster/core/pipeline.h"
#include "helpers.h"
#include "render_resource_manager.h"
#include "asset_loader.h"
#include "camera.h"
#include "core_components.h"
#include "light_manager.h"
#include "ecs_adapter.h"
#include "frame.h"
#include "ibl_helpers.h"
#include "pipeline_utils.h"
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto PIPELINE_CACHE_FILE = "PipelineCacheData.bin";
constexpr auto MODEL_FILE = "model/AlphaBlendModeTest.glb";
constexpr auto MODEL_FILE2 = "model/Box.glb";
constexpr auto BACKDROP_FILE = "image/photo_studio_loft_hall_4k.hdr";
constexpr u32 INIT_WIDTH = 640;
constexpr u32 INIT_HEIGHT = 480;
int
main(int, char *[])
{
MIN_LOG_LEVEL(Logger::LogType::eInfo);
Window window = {"Scene Render [WIP] (Aster)", {INIT_WIDTH, INIT_HEIGHT}};
Context context = {"Scene Render [WIP]", VERSION};
Surface surface = {&context, &window, "Primary Surface"};
PhysicalDevices physicalDevices = {&surface, &context};
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
vk::Extent2D internalResolution = {1920, 1080};
internalResolution.width = (internalResolution.height * INIT_WIDTH) / INIT_HEIGHT;
CameraController cameraController = {vec3{0.0f, 1.0f, 4.0f}, vec3{0.0f, 0.0f, 0.0f}, 70_deg,
Cast<f32>(internalResolution.width) / Cast<f32>(internalResolution.height)};
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {
.m_Vulkan10Features =
{
.multiDrawIndirect = true,
.samplerAnisotropy = true,
.shaderInt64 = true,
},
.m_Vulkan11Features =
{
.shaderDrawParameters = true,
},
.m_Vulkan12Features =
{
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.shaderStorageBufferArrayNonUniformIndexing = true,
.shaderStorageImageArrayNonUniformIndexing = true,
.descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true,
.bufferDeviceAddress = true,
.bufferDeviceAddressCaptureReplay = true,
},
.m_Vulkan13Features =
{
.synchronization2 = true,
.dynamicRendering = true,
},
};
auto attachmentFormat = vk::Format::eR8G8B8A8Srgb;
auto pipelineCacheData = ReadFileBytes(PIPELINE_CACHE_FILE, false);
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures,
{queueAllocation}, pipelineCacheData, "Primary Device"};
vk::Queue graphicsQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(),"Primary Chain"};
RenderResourceManager resourceManager = {&device, 1024};
EcsRegistry registry;
AssetLoader assetLoader = {&resourceManager, &registry, graphicsQueue, queueAllocation.m_Family,
queueAllocation.m_Family};
LightManager lightManager = LightManager{&resourceManager};
Texture environmentHdri;
assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE);
auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri);
auto environment = CreateEnvironment(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env");
resourceManager.Release(envHdriHandle);
lightManager.SetEnvironment(&environment);
eastl::vector<Model> models;
models.emplace_back(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"));
//registry.get<CDynamicTransform>(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j);
UniformBuffer ubo;
constexpr usize uboLightManagerOffset = sizeof cameraController.m_Camera;
constexpr usize uboTotalSize = uboLightManagerOffset + sizeof lightManager.m_MetaInfo;
ubo.Init(&device, uboTotalSize, "Desc1 UBO");
ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera);
ubo.Write(&device, uboLightManagerOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo);
Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager);
Pipeline backgroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager);
vk::DescriptorPool descriptorPool;
vk::DescriptorSet perFrameDescriptor;
{
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts[1];
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 3,
},
};
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool));
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSetLayout,
};
AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &perFrameDescriptor));
}
vk::DescriptorBufferInfo camLightBufferInfo = {
.buffer = ubo.m_Buffer,
.offset = 0,
.range = uboTotalSize,
};
eastl::array writeDescriptors = {
vk::WriteDescriptorSet{
.dstSet = perFrameDescriptor,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &camLightBufferInfo,
},
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
// Persistent variables
vk::Viewport viewport = {
.x = 0,
.y = Cast<f32>(internalResolution.height),
.width = Cast<f32>(internalResolution.width),
.height = -Cast<f32>(internalResolution.height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
vk::Rect2D scissor = {
.offset = {0, 0},
.extent = internalResolution,
};
vk::ImageSubresourceRange subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::ImageMemoryBarrier2 preRenderBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo preRenderDependencies = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &preRenderBarrier,
};
vk::ImageMemoryBarrier2 renderToBlitBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::ImageMemoryBarrier2 acquireToTransferDstBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
eastl::array postRenderBarriers = {
renderToBlitBarrier,
acquireToTransferDstBarrier,
};
vk::DependencyInfo postRenderDependencies = {
.imageMemoryBarrierCount = Cast<u32>(postRenderBarriers.size()),
.pImageMemoryBarriers = postRenderBarriers.data(),
};
vk::ImageMemoryBarrier2 transferDstToGuiRenderBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo preGuiDependencies = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &transferDstToGuiRenderBarrier,
};
vk::ImageMemoryBarrier2 prePresentBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
.dstAccessMask = vk::AccessFlagBits2::eNone,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo prePresentDependencies = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &prePresentBarrier,
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
eastl::fixed_vector<AttachmentImage, MAX_FRAMES_IN_FLIGHT> attachmentImages(frameManager.m_FramesInFlight);
{
auto depthIter = depthImages.begin();
auto attachmentIter = attachmentImages.begin();
for (u32 index = 0; index < frameManager.m_FramesInFlight; ++index)
{
auto name = fmt::format("Depth Frame{}", index);
depthIter->Init(&device, internalResolution, name.c_str());
name = fmt::format("Attachment0 Frame{}", index);
attachmentIter->Init(&device, internalResolution, attachmentFormat, name.c_str());
++depthIter;
++attachmentIter;
}
}
struct NodeData
{
mat4 m_Transform;
uptr m_VertexPositionPtr;
uptr m_VertexDataPtr;
uptr m_MaterialPtr;
u8 m_Padding_[8];
};
eastl::vector<NodeData> nodeData;
eastl::vector<vk::DrawIndexedIndirectCommand> nodeDrawInfo;
eastl::fixed_vector<StorageBuffer, MAX_FRAMES_IN_FLIGHT> nodeBuffers;
eastl::fixed_vector<IndirectBuffer, MAX_FRAMES_IN_FLIGHT> nodeIndirectBuffers;
eastl::fixed_vector<uptr, MAX_FRAMES_IN_FLIGHT> perFrameNodeBufferPtr;
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
{
auto *buffer = &nodeBuffers.push_back();
buffer->Init(&device, sizeof(NodeData) * 100'000, true, true, "Node Buffer");
auto *indirect = &nodeIndirectBuffers.push_back();
indirect->Init(&device, sizeof(vk::DrawIndexedIndirectCommand) * 100'000, true);
perFrameNodeBufferPtr.push_back(buffer->GetDeviceAddress(&device));
}
swapchain.RegisterResizeCallback(
[&cameraController, &internalResolution, &viewport, &scissor](vk::Extent2D extent) {
cameraController.SetAspectRatio(Cast<f32>(extent.width) / Cast<f32>(extent.height));
internalResolution.width = Cast<u32>(Cast<f32>(internalResolution.height) * cameraController.m_AspectRatio);
viewport.y = Cast<f32>(internalResolution.height);
viewport.width = Cast<f32>(internalResolution.width);
viewport.height = -Cast<f32>(internalResolution.height);
scissor.extent = internalResolution;
});
auto sortByParentHier = [&registry](Entity a, Entity b) {
const auto parent = registry.try_get<CParent<CDynamicTransform>>(b);
return parent && parent->m_ParentEntity == a;
};
registry.sort<CParent<CDynamicTransform>>(sortByParentHier);
Time::Init();
auto rootNodeUpdateView = registry.view<CDynamicTransform, CGlobalTransform>(Without<CParent<CDynamicTransform>>{});
auto rootModel = registry.group<CModel>(Get<CDynamicTransform>{});
auto nodeWithParentsUpdateView = registry.view<CDynamicTransform, CParent<CDynamicTransform>, CGlobalTransform>();
nodeWithParentsUpdateView.use<CParent<CDynamicTransform>>();
auto renderableObjectsGroup = registry.group<CGlobalTransform, CMesh, CMaterial>();
lightManager.Update();
ubo.Write(&device, uboLightManagerOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo);
resourceManager.Update();
while (window.Poll())
{
Time::Update();
//u32 index = 0;
//for (auto [entity, dynTrans] : rootModel.each())
//{
// dynTrans.m_Rotation =
// glm::rotate(dynTrans.m_Rotation, Cast<f32>(30_deg * (++index) * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f});
//}
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
u32 imageIndex = currentFrame->m_ImageIdx;
vk::Image currentSwapchainImage = swapchain.m_Images[imageIndex];
vk::ImageView currentSwapchainImageView = swapchain.m_ImageViews[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
DepthImage *currentDepthImage = &depthImages[currentFrame->m_FrameIdx];
AttachmentImage *currentAttachment = &attachmentImages[currentFrame->m_FrameIdx];
// Resize outdated attachments
if (currentAttachment->m_Extent.width != internalResolution.width ||
currentAttachment->m_Extent.height != internalResolution.height)
{
auto name = fmt::format("Depth Frame{}", currentFrame->m_FrameIdx);
currentDepthImage->Destroy(&device);
currentDepthImage->Init(&device, internalResolution, name.c_str());
name = fmt::format("Attachment0 Frame{}", currentFrame->m_FrameIdx);
currentAttachment->Destroy(&device);
currentAttachment->Init(&device, internalResolution, attachmentFormat, name.c_str());
}
vk::ImageView currentDepthImageView = currentDepthImage->m_View;
vk::Image currentImage = currentAttachment->m_Image;
vk::ImageView currentImageView = currentAttachment->m_View;
// Ready the barrier structs
preRenderBarrier.image = currentImage;
postRenderBarriers[0].image = currentImage;
postRenderBarriers[1].image = currentSwapchainImage;
transferDstToGuiRenderBarrier.image = currentSwapchainImage;
prePresentBarrier.image = currentSwapchainImage;
// Write Camera
ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera);
// Update all root dynamic object transforms.
for (auto [entity, dynTransform, globalTransform] : rootNodeUpdateView.each())
{
auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale);
auto rotation = glm::toMat4(dynTransform.m_Rotation);
auto translation = glm::translate(mat4{1.0f}, dynTransform.m_Position);
globalTransform.m_Transform = translation * rotation * scale;
}
// Has been sorted and ordered by parent.
// Update all dynamic object transforms.
for (auto [entity, dynTransform, parent, globalTransform] : nodeWithParentsUpdateView.each())
{
auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale);
auto rotation = glm::toMat4(dynTransform.m_Rotation);
auto translation = glm::translate(mat4{1.0f}, dynTransform.m_Position);
globalTransform.m_Transform =
registry.get<CGlobalTransform>(parent.m_ParentEntity).m_Transform * translation * rotation * scale;
}
u32 objectCount = Cast<u32>(renderableObjectsGroup.size());
nodeData.clear();
nodeDrawInfo.clear();
nodeData.reserve(objectCount);
nodeDrawInfo.reserve(objectCount);
// Write all objects into the node data to be pushed.
for (auto [entity, globalTransform, mesh, material] : renderableObjectsGroup.each())
{
nodeData.push_back({
.m_Transform = globalTransform.m_Transform,
.m_VertexPositionPtr = mesh.m_VertexPositionPtr,
.m_VertexDataPtr = mesh.m_VertexDataPtr,
.m_MaterialPtr = material.m_MaterialPtr,
});
nodeDrawInfo.push_back({
.indexCount = mesh.m_IndexCount,
.instanceCount = 1,
.firstIndex = mesh.m_FirstIndex,
.vertexOffset = 0,
.firstInstance = 0,
});
}
nodeBuffers[currentFrame->m_FrameIdx].Write(&device, 0, objectCount * sizeof(NodeData), nodeData.data());
nodeIndirectBuffers[currentFrame->m_FrameIdx].Write(&device, 0, objectCount * sizeof nodeDrawInfo[0],
nodeDrawInfo.data());
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(cmd.begin(&beginInfo));
cmd.pipelineBarrier2(&preRenderDependencies);
// Render
eastl::array attachmentInfos = {
vk::RenderingAttachmentInfo{
.imageView = currentImageView,
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
},
};
vk::RenderingAttachmentInfo depthAttachment = {
.imageView = currentDepthImageView,
.imageLayout = vk::ImageLayout::eDepthAttachmentOptimal,
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eDontCare,
.clearValue = vk::ClearDepthStencilValue{.depth = 1.0f, .stencil = 0},
};
vk::RenderingInfo renderingInfo = {
.renderArea = {.extent = ToExtent2D(currentAttachment->m_Extent)},
.layerCount = 1,
.colorAttachmentCount = Cast<u32>(attachmentInfos.size()),
.pColorAttachments = attachmentInfos.data(),
.pDepthAttachment = &depthAttachment,
};
cmd.beginRendering(&renderingInfo);
cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&resourceManager.m_DescriptorSet, 0, nullptr);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0,
nullptr);
cmd.bindIndexBuffer(resourceManager.GetIndexBuffer(), 0, vk::IndexType::eUint32);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
auto nodeBufferAddr = perFrameNodeBufferPtr[currentFrame->m_FrameIdx];
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof nodeBufferAddr, &nodeBufferAddr);
cmd.drawIndexedIndirect(nodeIndirectBuffers[currentFrame->m_FrameIdx].m_Buffer, 0, objectCount,
sizeof nodeDrawInfo[0]);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backgroundPipeline.m_Pipeline);
cmd.draw(3, 1, 0, 0);
cmd.endRendering();
cmd.pipelineBarrier2(&postRenderDependencies);
vk::ImageBlit blitRegion = {
.srcSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
std::array{
vk::Offset3D{0, 0, 0},
ToOffset3D(currentAttachment->m_Extent),
},
.dstSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
std::array{
vk::Offset3D{0, 0, 0},
vk::Offset3D{Cast<i32>(swapchain.m_Extent.width), Cast<i32>(swapchain.m_Extent.height), 1},
},
};
cmd.blitImage(currentImage, postRenderBarriers[0].newLayout, currentSwapchainImage,
postRenderBarriers[1].newLayout, 1, &blitRegion, vk::Filter::eLinear);
cmd.pipelineBarrier2(&preGuiDependencies);
cmd.pipelineBarrier2(&prePresentDependencies);
AbortIfFailed(cmd.end());
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &currentFrame->m_ImageAcquireSem,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &currentFrame->m_RenderFinishSem,
};
AbortIfFailed(graphicsQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
currentFrame->Present(graphicsQueue, &swapchain, &surface, window.GetSize());
}
device.WaitIdle();
for (auto buffer : nodeIndirectBuffers)
{
buffer.Destroy(&device);
}
for (auto buffer : nodeBuffers)
{
buffer.Destroy(&device);
}
for (auto depthImage : depthImages)
{
depthImage.Destroy(&device);
}
for (auto attachmentImage : attachmentImages)
{
attachmentImage.Destroy(&device);
}
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr);
for (auto &model : models)
{
model.Destroy(&resourceManager, &registry);
}
}