Begin Consolidation all objects under the systems::Device interface.

Currently clears a screen.
- Merge all resource creation API under Device.
- Begin a basic Context setup.
This commit is contained in:
Anish Bhobe 2025-05-01 13:27:19 +02:00
parent a790c26f1c
commit d82e81d104
29 changed files with 1557 additions and 1255 deletions

View File

@ -40,10 +40,9 @@ struct Device final
void WaitIdle() const;
// Ctor/Dtor
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
Device() = default;
Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
NameString &&name);
~Device();

View File

@ -55,6 +55,29 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
#define FIX(MSG) static_assert(false && ("Unimplemented: " MSG))
#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)
[[nodiscard]] inline bool
Failed(const vk::Result result)
{

View File

@ -21,6 +21,7 @@ struct Instance final
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
// Ctor/Dtor
Instance() = default;
Instance(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
~Instance();

View File

@ -54,5 +54,5 @@ struct PhysicalDevice final
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
{
public:
PhysicalDevices(const Surface *surface, const Instance *context);
PhysicalDevices(const Surface &surface, const Instance &context);
};

View File

@ -17,7 +17,8 @@ struct Surface
NameString m_Name;
// Ctor Dtor
Surface(Instance *context, const Window *window, cstr name);
Surface() = default;
Surface(Instance &context, const Window &window);
~Surface();
// Move

View File

@ -21,7 +21,6 @@ struct Swapchain final
const Device *m_Device;
vk::SwapchainKHR m_Swapchain;
NameString m_Name;
vk::Extent2D m_Extent;
vk::Format m_Format;
eastl::fixed_vector<vk::Image, 4> m_Images;
@ -29,11 +28,12 @@ struct Swapchain final
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
void Create(const Surface *window, Size2D size);
void Create(const Surface &surface, Size2D size);
void RegisterResizeCallback(FnResizeCallback &&callback);
// Ctor/Dtor
Swapchain(const Surface *window, const Device *device, Size2D size, NameString &&name);
Swapchain() = default;
Swapchain(const Surface &surface, const Device &device, Size2D size);
~Swapchain();
// Move

View File

@ -20,6 +20,8 @@ struct Window final
static std::atomic_uint64_t m_WindowCount;
static std::atomic_bool m_IsGlfwInit;
static void SetupLibrary();
static cstr *GetInstanceExtensions(u32 *extensionCount);
// Methods
[[nodiscard]] bool

View File

@ -4,10 +4,6 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core
INTERFACE
"buffer_manager.h"
"image_manager.h"
"view_manager.h"
"sampler_manager.h"
"device.h"
"resource.h"
"resource_manager.h"
"commit_manager.h")

View File

@ -1,35 +0,0 @@
// =============================================
// Aster: buffer_manager.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "aster/core/buffer.h"
namespace systems
{
template <std::derived_from<Buffer> TTo, std::derived_from<Buffer> TFrom>
static Ref<TTo>
CastBuffer(const Ref<TFrom> &from)
{
if constexpr (not concepts::BufferInto<TFrom, TTo>)
assert(TTo::FLAGS & from->m_Flags);
return std::reinterpret_pointer_cast<TTo>(from);
}
class BufferManager final
{
const Device *m_Device = nullptr;
public:
explicit BufferManager(const Device *device);
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr) const;
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr) const;
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr) const;
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr) const;
};
} // namespace systems

View File

@ -7,16 +7,16 @@
#include "aster/aster.h"
#include "buffer_manager.h"
#include "sampler_manager.h"
#include "view_manager.h"
#include "aster/util/freelist.h"
#include "EASTL/deque.h"
#include "EASTL/intrusive_hash_map.h"
#include "resource.h"
#include "EASTL/vector.h"
#include "aster/core/buffer.h"
#include "aster/core/image_view.h"
#include "aster/core/sampler.h"
namespace systems
{

View File

@ -0,0 +1,627 @@
// =============================================
// Aster: device.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "resource.h"
#include "aster/aster.h"
#include "aster/core/buffer.h"
#include "aster/core/device.h"
#include "aster/core/image.h"
#include "aster/core/image_view.h"
#include "aster/core/instance.h"
#include "aster/core/physical_device.h"
#include "aster/core/sampler.h"
#include "aster/core/swapchain.h"
#include "aster/core/pipeline.h"
#include <EASTL/hash_map.h>
#include <EASTL/optional.h>
#include <EASTL/vector_map.h>
#include <optional>
constexpr static u32 MAX_FRAMES_IN_FLIGHT = 3;
struct Window;
using CoreDevice = Device;
template <>
struct eastl::hash<vk::SamplerCreateInfo>
{
usize
operator()(const vk::SamplerCreateInfo &createInfo) const noexcept
{
usize hash = HashAny(createInfo.flags);
hash = HashCombine(hash, HashAny(createInfo.magFilter));
hash = HashCombine(hash, HashAny(createInfo.minFilter));
hash = HashCombine(hash, HashAny(createInfo.mipmapMode));
hash = HashCombine(hash, HashAny(createInfo.addressModeU));
hash = HashCombine(hash, HashAny(createInfo.addressModeV));
hash = HashCombine(hash, HashAny(createInfo.addressModeW));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3
hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable));
hash =
HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution
hash = HashCombine(hash, HashAny(createInfo.compareEnable));
hash = HashCombine(hash, HashAny(createInfo.compareOp));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough.
hash =
HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
hash = HashCombine(hash, HashAny(createInfo.borderColor));
hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates));
return hash;
}
};
namespace systems
{
// ====================================================================================================
#pragma region Creation Structs
// ====================================================================================================
// ----------------------------------------------------------------------------------------------------
#pragma region Image
// ----------------------------------------------------------------------------------------------------
struct Texture2DCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct TextureCubeCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
u32 m_Side = 0;
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct AttachmentCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
struct DepthStencilImageCreateInfo
{
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
#pragma endregion
// ----------------------------------------------------------------------------------------------------
#pragma region View
// ----------------------------------------------------------------------------------------------------
template <concepts::AnyImage TImage>
struct ViewCreateInfo
{
using ImageType = TImage;
Ref<ImageType> m_Image;
cstr m_Name;
vk::ImageViewType m_ViewType = vk::ImageViewType::e2D;
vk::ComponentMapping m_Components = {};
vk::ImageAspectFlags m_AspectMask = {};
eastl::optional<u8> m_MipLevelCount = eastl::nullopt;
eastl::optional<u8> m_LayerCount = eastl::nullopt;
u8 m_BaseMipLevel = 0;
u8 m_BaseLayer = 0;
[[nodiscard]] u8
GetMipLevelCount() const
{
return m_MipLevelCount.value_or(m_Image->m_MipLevels - m_BaseMipLevel);
}
[[nodiscard]] u8
GetLayerCount() const
{
return m_LayerCount.value_or(m_Image->m_LayerCount - m_BaseLayer);
}
explicit
operator vk::ImageViewCreateInfo() const
{
return {
.image = m_Image->m_Image,
.viewType = m_ViewType,
.format = m_Image->m_Format,
.components = m_Components,
.subresourceRange =
{
.aspectMask = m_AspectMask,
.baseMipLevel = m_BaseMipLevel,
.levelCount = GetMipLevelCount(),
.baseArrayLayer = m_BaseLayer,
.layerCount = GetLayerCount(),
},
};
}
explicit
operator ViewCreateInfo<Image>() const
{
return {
.m_Image = CastImage<Image>(m_Image),
.m_Name = m_Name,
.m_ViewType = m_ViewType,
.m_Components = m_Components,
.m_AspectMask = m_AspectMask,
.m_MipLevelCount = m_MipLevelCount,
.m_LayerCount = m_LayerCount,
.m_BaseMipLevel = m_BaseMipLevel,
.m_BaseLayer = m_BaseLayer,
};
}
};
#pragma endregion
// ----------------------------------------------------------------------------------------------------
#pragma region Sampler
// ----------------------------------------------------------------------------------------------------
struct SamplerCreateInfo
{
cstr m_Name = nullptr;
vk::SamplerCreateFlags m_Flags = {};
vk::Filter m_MagFilter = vk::Filter::eLinear;
vk::Filter m_MinFilter = vk::Filter::eLinear;
vk::SamplerMipmapMode m_MipmapMode = vk::SamplerMipmapMode::eLinear;
vk::SamplerAddressMode m_AddressModeU = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeV = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeW = vk::SamplerAddressMode::eRepeat;
vk::BorderColor m_BorderColor = vk::BorderColor::eFloatOpaqueBlack;
vk::CompareOp m_CompareOp = vk::CompareOp::eNever;
f32 m_MipLodBias = 0.0f;
f32 m_MaxAnisotropy = 16.0f;
f32 m_MinLod = 0;
f32 m_MaxLod = VK_LOD_CLAMP_NONE;
bool m_AnisotropyEnable = true;
bool m_CompareEnable = false;
bool m_NormalizedCoordinates = true;
explicit
operator vk::SamplerCreateInfo() const
{
return {
.flags = m_Flags,
.magFilter = m_MagFilter,
.minFilter = m_MinFilter,
.mipmapMode = m_MipmapMode,
.addressModeU = m_AddressModeU,
.addressModeV = m_AddressModeV,
.addressModeW = m_AddressModeW,
.mipLodBias = m_MipLodBias,
.anisotropyEnable = m_AnisotropyEnable,
.maxAnisotropy = m_MaxAnisotropy,
.compareEnable = m_CompareEnable,
.compareOp = m_CompareOp,
.minLod = m_MinLod,
.maxLod = m_MaxLod,
.borderColor = m_BorderColor,
.unnormalizedCoordinates = !m_NormalizedCoordinates,
};
}
};
#pragma endregion
// ----------------------------------------------------------------------------------------------------
#pragma region Pipeline
// ----------------------------------------------------------------------------------------------------
// TODO
#pragma endregion
// ----------------------------------------------------------------------------------------------------
#pragma region Device
// ----------------------------------------------------------------------------------------------------
PhysicalDevice DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices);
using PhysicalDeviceSelectorFn = PhysicalDevice (*)(const PhysicalDevices &);
static_assert(std::convertible_to<decltype(DefaultPhysicalDeviceSelector), PhysicalDeviceSelectorFn>);
struct DeviceCreateInfo
{
cstr m_AppName = "Aster App";
Version m_AppVersion = {0, 1, 0};
PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector;
std::span<u8> m_PipelineCacheData = {};
std::reference_wrapper<Window> m_Window;
cstr m_Name = "Primary";
Features m_Features;
};
#pragma endregion
#pragma endregion
class Device;
struct Frame;
class Context
{
protected:
vk::CommandBuffer m_Cmd;
friend Device;
friend Frame;
explicit Context(const vk::CommandBuffer cmd)
: m_Cmd{cmd}
{
}
public:
void Begin();
void End();
};
class GraphicsContext : public Context
{
protected:
friend Device;
friend Frame;
explicit GraphicsContext(const vk::CommandBuffer cmd)
: Context{cmd}
{
}
public:
void BindPipeline(vk::Pipeline pipeline);
void DrawIndexed(u32 indexCount);
[[deprecated]]
void Dependency(const vk::DependencyInfo& dependencyInfo);
[[deprecated]]
void BeginRendering(const vk::RenderingInfo& renderingInfo);
void EndRendering();
};
struct Frame
{
// Persistent
::Device *m_Device;
// TODO: ThreadSafe
vk::CommandPool m_Pool;
vk::Fence m_FrameAvailableFence;
vk::Semaphore m_ImageAcquireSem;
vk::Semaphore m_RenderFinishSem;
u32 m_FrameIdx;
// Transient
u32 m_ImageIdx;
vk::Image m_SwapchainImage;
vk::ImageView m_SwapchainImageView;
GraphicsContext CreateGraphicsContext();
};
class Device final
{
public: // TODO: Temp
std::reference_wrapper<Window> m_Window;
Instance m_Instance;
Surface m_Surface;
::Device m_Device;
Swapchain m_Swapchain;
// TODO: This is single-threaded.
vk::Queue m_GraphicsQueue;
u32 m_GraphicsQueueFamily;
std::array<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames;
u32 m_CurrentFrameIdx = 0;
public:
// ====================================================================================================
// Resource Management
// ====================================================================================================
//
// Buffer Management
// ----------------------------------------------------------------------------------------------------
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr);
//
// Image Management
// ----------------------------------------------------------------------------------------------------
template <concepts::ImageInto<Texture> T>
[[nodiscard]] Ref<T>
CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
return CastImage<T>(CreateTexture2D(createInfo));
}
template <concepts::ImageInto<TextureCube> T>
[[nodiscard]] Ref<T>
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
return CastImage<T>(CreateTextureCube(createInfo));
}
[[nodiscard]] Ref<Image> CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Ref<ImageCube> CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
//
// View Management
// ----------------------------------------------------------------------------------------------------
template <concepts::View TImageView>
Ref<TImageView>
CreateView(const ViewCreateInfo<typename TImageView::ImageType> &createInfo)
{
return CastView<TImageView>(CreateView(ViewCreateInfo<Image>(createInfo)));
}
[[nodiscard]] Ref<ImageView> CreateView(const ViewCreateInfo<Image> &createInfo);
//
// Image - View Combined Management
// ----------------------------------------------------------------------------------------------------
template <concepts::ViewTo<Image> T>
[[nodiscard]] Ref<T>
CreateTexture2DWithView(const Texture2DCreateInfo &createInfo)
{
auto handle = CreateTexture2D(createInfo);
return CastView<T>(handle);
}
template <concepts::ViewTo<ImageCube> T>
[[nodiscard]] Ref<T>
CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo)
{
auto handle = CreateTextureCube(createInfo);
return CastView<T>(handle);
}
[[nodiscard]] Ref<TextureView> CreateTexture2DWithView(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Ref<ImageCubeView> CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Ref<ImageView> CreateAttachmentWithView(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Ref<ImageView> CreateDepthStencilImageWithView(const DepthStencilImageCreateInfo &createInfo);
//
// Sampler Management
// ----------------------------------------------------------------------------------------------------
using Handle = Ref<Sampler>;
using WeakHandle = WeakRef<Sampler>;
eastl::hash_map<vk::SamplerCreateInfo, WeakHandle> m_HashToSamplerIdx;
Ref<Sampler> CreateSampler(const SamplerCreateInfo &createInfo);
//
// Pipeline
// ----------------------------------------------------------------------------------------------------
/*
Pipeline CreatePipeline()
{
// Pipeline Setup
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
{
.stage = vk::ShaderStageFlagBits::eVertex,
.module = vertexShaderModule,
.pName = "main",
},
{
.stage = vk::ShaderStageFlagBits::eFragment,
.module = fragmentShaderModule,
.pName = "main",
},
}};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = 0,
.pSetLayouts = nullptr,
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
};
vk::PipelineLayout pipelineLayout;
vk::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout);
ERROR_IF(Failed(result), "Could not create a pipeline layout. Cause: {}", result) THEN_ABORT(result);
m_Device.SetName(pipelineLayout, "Triangle Layout");
vk::VertexInputBindingDescription inputBindingDescription = Vertex::GetBinding(0);
auto inputAttributeDescription = Vertex::GetAttributes(0);
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &inputBindingDescription,
.vertexAttributeDescriptionCount = Cast<u32>(inputAttributeDescription.size()),
.pVertexAttributeDescriptions = inputAttributeDescription.data(),
};
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
.topology = vk::PrimitiveTopology::eTriangleList,
.primitiveRestartEnable = false,
};
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
.viewportCount = 1,
.scissorCount = 1,
};
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
.depthClampEnable = false,
.rasterizerDiscardEnable = false,
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eNone,
.frontFace = vk::FrontFace::eCounterClockwise,
.depthBiasEnable = false,
.lineWidth = 1.0,
};
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
.rasterizationSamples = vk::SampleCountFlagBits::e1,
.sampleShadingEnable = false,
};
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
.depthTestEnable = false,
.depthWriteEnable = false,
};
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
.blendEnable = false,
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachmentState,
};
eastl::array dynamicStates = {
vk::DynamicState::eScissor,
vk::DynamicState::eViewport,
};
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data(),
};
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
.viewMask = 0,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &swapchain->m_Format,
};
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
.pNext = &renderingCreateInfo,
.stageCount = Cast<u32>(shaderStages.size()),
.pStages = shaderStages.data(),
.pVertexInputState = &vertexInputStateCreateInfo,
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
.pViewportState = &viewportStateCreateInfo,
.pRasterizationState = &rasterizationStateCreateInfo,
.pMultisampleState = &multisampleStateCreateInfo,
.pDepthStencilState = &depthStencilStateCreateInfo,
.pColorBlendState = &colorBlendStateCreateInfo,
.pDynamicState = &dynamicStateCreateInfo,
.layout = pipelineLayout,
};
vk::Pipeline pipeline;
result = m_Device.m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline);
ERROR_IF(Failed(result), "Could not create a graphics pipeline. Cause: {}", result)
THEN_ABORT(result);
m_Device.SetName(pipeline, "Triangle Pipeline");
m_Device.m_Device.destroy(vertexShaderModule, nullptr);
m_Device.m_Device.destroy(fragmentShaderModule, nullptr);
return {&m_Device, pipelineLayout, pipeline, {}};
}
//*/
//
// Frames
// ----------------------------------------------------------------------------------------------------
private:
Frame CreateFrame(u32 frameIndex);
public:
Frame &GetNextFrame();
Size2D GetSwapchainSize() const
{
return {m_Swapchain.m_Extent.width, m_Swapchain.m_Extent.height};
}
using Receipt = u32;
Receipt Submit(Frame& frame, GraphicsContext& graphicsContext);
void Present(Frame &frame);
friend Context;
friend GraphicsContext;
//
// Device Methods
// ----------------------------------------------------------------------------------------------------
template <concepts::VkHandle T>
void
SetName(const T &object, cstr name) const
{
m_Device.SetName(object, name);
}
[[nodiscard]] vk::Queue
GetQueue(u32 familyIndex, u32 queueIndex) const
{
return m_Device.GetQueue(familyIndex, queueIndex);
}
[[nodiscard]] eastl::vector<u8>
DumpPipelineCache() const
{
return m_Device.DumpPipelineCache();
}
void
WaitIdle() const
{
m_Device.WaitIdle();
}
// Inner
// ----------------------------------------------------------------------------------------------------
[[nodiscard]] CoreDevice &
GetInner()
{
return m_Device;
}
[[nodiscard]] vk::Device &
GetHandle()
{
return m_Device.m_Device;
}
// Ctor/Dtor
// ----------------------------------------------------------------------------------------------------
explicit Device(const DeviceCreateInfo &createInfo);
~Device();
PIN_MEMORY(Device);
};
} // namespace systems

View File

@ -1,84 +0,0 @@
// =============================================
// Aster: image_manager.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "aster/core/image.h"
namespace systems
{
template <std::derived_from<Image> TTo, std::derived_from<Image> TFrom>
static Ref<TTo>
CastImage(const Ref<TFrom> &from)
{
if constexpr (not concepts::ImageInto<TFrom, TTo>)
assert(TTo::FLAGS & from->m_Flags_);
return std::reinterpret_pointer_cast<TTo>(from);
}
struct Texture2DCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct TextureCubeCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
u32 m_Side = 0;
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct AttachmentCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
struct DepthStencilImageCreateInfo
{
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
class ImageManager final
{
const Device *m_Device;
public:
explicit ImageManager(const Device *device);
template <concepts::ImageInto<Texture> T>
[[nodiscard]] Ref<T>
CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
auto handle = CreateTexture2D(createInfo);
return CastImage<T>(handle);
}
template <concepts::ImageInto<TextureCube> T>
[[nodiscard]] Ref<T>
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
auto handle = CreateTextureCube(createInfo);
return CastImage<T>(handle);
}
[[nodiscard]] Ref<Image> CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Ref<ImageCube> CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Ref<Image> CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
};
} // namespace systems

View File

@ -5,11 +5,63 @@
#pragma once
#include "aster/core/buffer.h"
#include "aster/core/image.h"
#include "aster/core/image_view.h"
#include <EASTL/intrusive_ptr.h>
namespace systems
{
// ====================================================================================================
#pragma region Util Methods
// ====================================================================================================
#pragma region Buffer
// ----------------------------------------------------------------------------------------------------
template <std::derived_from<Buffer> TTo, std::derived_from<Buffer> TFrom>
static Ref<TTo>
CastBuffer(const Ref<TFrom> &from)
{
if constexpr (not concepts::BufferInto<TFrom, TTo>)
assert(TTo::FLAGS & from->m_Flags);
return std::reinterpret_pointer_cast<TTo>(from);
}
#pragma endregion
#pragma region Image
// ----------------------------------------------------------------------------------------------------
template <std::derived_from<Image> TTo, std::derived_from<Image> TFrom>
static Ref<TTo>
CastImage(const Ref<TFrom> &from)
{
if constexpr (not concepts::ImageInto<TFrom, TTo>)
assert(TTo::FLAGS & from->m_Flags_);
return std::reinterpret_pointer_cast<TTo>(from);
}
#pragma endregion
#pragma region View
// ----------------------------------------------------------------------------------------------------
template <concepts::View TTo, std::derived_from<Image> TFrom>
static Ref<TTo>
CastView(const Ref<View<TFrom>> &from)
{
if constexpr (not concepts::ImageInto<TFrom, typename TTo::ImageType>)
assert(TTo::ImageType::FLAGS & from->m_Image->m_Flags_);
return std::reinterpret_pointer_cast<TTo>(from);
}
#pragma endregion
#pragma endregion
/**
* ResId manages the lifetime of the committed resource.
* @tparam T Type of the committed resource.

View File

@ -1,142 +0,0 @@
// =============================================
// Aster: resource_manager.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "buffer_manager.h"
#include "image_manager.h"
#include "sampler_manager.h"
#include "view_manager.h"
namespace systems
{
class ResourceManager
{
struct CombinedImageViewManager
{
ImageManager *m_ImageManager;
ViewManager *m_ViewManager;
CombinedImageViewManager(ImageManager *imageManager, ViewManager *viewManager)
: m_ImageManager{imageManager}
, m_ViewManager{viewManager}
{
}
template <concepts::ViewTo<Image> T>
[[nodiscard]] Ref<T>
CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
auto handle = CreateTexture2D(createInfo);
return CastView<T>(handle);
}
template <concepts::ViewTo<ImageCube> T>
[[nodiscard]] Ref<T>
CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
auto handle = CreateTextureCube(createInfo);
return CastView<T>(handle);
}
[[nodiscard]] Ref<TextureView>
CreateTexture2D(const Texture2DCreateInfo &createInfo) const
{
return m_ViewManager->CreateView<TextureView>({
.m_Image = CastImage<Texture>(m_ImageManager->CreateTexture2D(createInfo)),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
[[nodiscard]] Ref<ImageCubeView>
CreateTextureCube(const TextureCubeCreateInfo &createInfo) const
{
return m_ViewManager->CreateView<ImageCubeView>({
.m_Image = m_ImageManager->CreateTextureCube(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::eCube,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
[[nodiscard]] Ref<ImageView>
CreateAttachment(const AttachmentCreateInfo &createInfo) const
{
return m_ViewManager->CreateView({
.m_Image = m_ImageManager->CreateAttachment(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
[[nodiscard]] Ref<ImageView>
CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo) const
{
return m_ViewManager->CreateView({
.m_Image = m_ImageManager->CreateDepthStencilImage(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
});
}
};
BufferManager m_Buffers;
ImageManager m_Images;
SamplerManager m_Samplers;
ViewManager m_Views;
CombinedImageViewManager m_CombinedImageViews;
public:
explicit ResourceManager(const Device *device)
: m_Buffers{device}
, m_Images{device}
, m_Samplers{device}
, m_Views{device}
, m_CombinedImageViews{&m_Images, &m_Views}
{
}
BufferManager &
Buffers()
{
return m_Buffers;
}
ImageManager &
Images()
{
return m_Images;
}
ViewManager &
Views()
{
return m_Views;
}
SamplerManager &
Samplers()
{
return m_Samplers;
}
CombinedImageViewManager &
CombinedImageViews()
{
return m_CombinedImageViews;
}
~ResourceManager() = default;
PIN_MEMORY(ResourceManager);
};
} // namespace systems

View File

@ -1,112 +0,0 @@
// =============================================
// Aster: sampler_manager.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "EASTL/hash_map.h"
#include "aster/aster.h"
#include "aster/core/sampler.h"
#include "EASTL/vector_map.h"
template <>
struct eastl::hash<vk::SamplerCreateInfo>
{
usize
operator()(const vk::SamplerCreateInfo &createInfo) const noexcept
{
usize hash = HashAny(createInfo.flags);
hash = HashCombine(hash, HashAny(createInfo.magFilter));
hash = HashCombine(hash, HashAny(createInfo.minFilter));
hash = HashCombine(hash, HashAny(createInfo.mipmapMode));
hash = HashCombine(hash, HashAny(createInfo.addressModeU));
hash = HashCombine(hash, HashAny(createInfo.addressModeV));
hash = HashCombine(hash, HashAny(createInfo.addressModeW));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3
hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable));
hash =
HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution
hash = HashCombine(hash, HashAny(createInfo.compareEnable));
hash = HashCombine(hash, HashAny(createInfo.compareOp));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough.
hash =
HashCombine(hash,
HashAny(Cast<usize>(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
hash = HashCombine(hash, HashAny(createInfo.borderColor));
hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates));
return hash;
}
};
namespace systems
{
struct SamplerCreateInfo
{
cstr m_Name = nullptr;
vk::SamplerCreateFlags m_Flags = {};
vk::Filter m_MagFilter = vk::Filter::eLinear;
vk::Filter m_MinFilter = vk::Filter::eLinear;
vk::SamplerMipmapMode m_MipmapMode = vk::SamplerMipmapMode::eLinear;
vk::SamplerAddressMode m_AddressModeU = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeV = vk::SamplerAddressMode::eRepeat;
vk::SamplerAddressMode m_AddressModeW = vk::SamplerAddressMode::eRepeat;
vk::BorderColor m_BorderColor = vk::BorderColor::eFloatOpaqueBlack;
vk::CompareOp m_CompareOp = vk::CompareOp::eNever;
f32 m_MipLodBias = 0.0f;
f32 m_MaxAnisotropy = 16.0f;
f32 m_MinLod = 0;
f32 m_MaxLod = VK_LOD_CLAMP_NONE;
bool m_AnisotropyEnable = true;
bool m_CompareEnable = false;
bool m_NormalizedCoordinates = true;
explicit
operator vk::SamplerCreateInfo() const
{
return {
.flags = m_Flags,
.magFilter = m_MagFilter,
.minFilter = m_MinFilter,
.mipmapMode = m_MipmapMode,
.addressModeU = m_AddressModeU,
.addressModeV = m_AddressModeV,
.addressModeW = m_AddressModeW,
.mipLodBias = m_MipLodBias,
.anisotropyEnable = m_AnisotropyEnable,
.maxAnisotropy = m_MaxAnisotropy,
.compareEnable = m_CompareEnable,
.compareOp = m_CompareOp,
.minLod = m_MinLod,
.maxLod = m_MaxLod,
.borderColor = m_BorderColor,
.unnormalizedCoordinates = !m_NormalizedCoordinates,
};
}
};
/**
* @class SamplerManager sampler_manager.h
*
* Manages (and caches) objects of sampler. Currently Samplers are never deleted.
*/
class SamplerManager final
{
using Handle = Ref<Sampler>;
using WeakHandle = WeakRef<Sampler>;
eastl::hash_map<vk::SamplerCreateInfo, WeakHandle> m_HashToSamplerIdx;
const Device *m_Device;
public:
explicit SamplerManager(const Device *device);
~SamplerManager();
Ref<Sampler> CreateSampler(const SamplerCreateInfo &createInfo);
};
} // namespace systems

View File

@ -1,105 +0,0 @@
// =============================================
// Aster: view_manager.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "aster/core/image_view.h"
#include <EASTL/optional.h>
namespace systems
{
template <concepts::View TTo, std::derived_from<Image> TFrom>
static Ref<TTo>
CastView(const Ref<View<TFrom>> &from)
{
if constexpr (not concepts::ImageInto<TFrom, typename TTo::ImageType>)
assert(TTo::ImageType::FLAGS & from->m_Image->m_Flags_);
return std::reinterpret_pointer_cast<TTo>(from);
}
template <concepts::AnyImage TImage>
struct ViewCreateInfo
{
using ImageType = TImage;
Ref<ImageType> m_Image;
cstr m_Name;
vk::ImageViewType m_ViewType = vk::ImageViewType::e2D;
vk::ComponentMapping m_Components = {};
vk::ImageAspectFlags m_AspectMask = {};
eastl::optional<u8> m_MipLevelCount = eastl::nullopt;
eastl::optional<u8> m_LayerCount = eastl::nullopt;
u8 m_BaseMipLevel = 0;
u8 m_BaseLayer = 0;
[[nodiscard]] u8
GetMipLevelCount() const
{
return m_MipLevelCount.value_or(m_Image->m_MipLevels - m_BaseMipLevel);
}
[[nodiscard]] u8
GetLayerCount() const
{
return m_LayerCount.value_or(m_Image->m_LayerCount - m_BaseLayer);
}
explicit
operator vk::ImageViewCreateInfo() const
{
return {
.image = m_Image->m_Image,
.viewType = m_ViewType,
.format = m_Image->m_Format,
.components = m_Components,
.subresourceRange =
{
.aspectMask = m_AspectMask,
.baseMipLevel = m_BaseMipLevel,
.levelCount = GetMipLevelCount(),
.baseArrayLayer = m_BaseLayer,
.layerCount = GetLayerCount(),
},
};
}
explicit
operator ViewCreateInfo<Image>() const
{
return {
.m_Image = CastImage<Image>(m_Image),
.m_Name = m_Name,
.m_ViewType = m_ViewType,
.m_Components = m_Components,
.m_AspectMask = m_AspectMask,
.m_MipLevelCount = m_MipLevelCount,
.m_LayerCount = m_LayerCount,
.m_BaseMipLevel = m_BaseMipLevel,
.m_BaseLayer = m_BaseLayer,
};
}
};
class ViewManager final
{
const Device *m_Device;
public:
explicit ViewManager(const Device *device);
template <concepts::View TImageView>
Ref<TImageView>
CreateView(const ViewCreateInfo<typename TImageView::ImageType> &createInfo)
{
return CastView<TImageView>(CreateView(ViewCreateInfo<Image>(createInfo)));
}
[[nodiscard]] Ref<ImageView> CreateView(const ViewCreateInfo<Image> &createInfo) const;
};
} // namespace systems

View File

@ -17,18 +17,12 @@ constexpr eastl::array DEVICE_EXTENSIONS = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
: Device(context, physicalDevice, enabledFeatures, queueAllocations, {}, std::move(name))
{
}
Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
Device::Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
const eastl::span<QueueAllocation> &queueAllocations, const eastl::span<u8> &pipelineCacheData,
NameString &&name)
: m_Name(std::move(name))
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
, m_PhysicalDevice(physicalDevice.m_PhysicalDevice)
, m_ValidationEnabled(context.m_DebugMessenger != nullptr)
{
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
@ -51,10 +45,10 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
});
}
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures->m_Vulkan10Features;
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features;
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features;
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features;
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures.m_Vulkan10Features;
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures.m_Vulkan11Features;
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures.m_Vulkan12Features;
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures.m_Vulkan13Features;
vulkan11Features->pNext = vulkan12Features;
vulkan12Features->pNext = vulkan13Features;
@ -71,7 +65,7 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
THEN_ABORT(result)
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data());
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice.m_DeviceProperties.deviceName.data());
SetName(m_Device, m_Name.data());
@ -85,7 +79,7 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
.physicalDevice = m_PhysicalDevice,
.device = m_Device,
.pVulkanFunctions = &vmaVulkanFunctions,
.instance = context->m_Instance,
.instance = context.m_Instance,
.vulkanApiVersion = ASTER_API_VERSION,
};
@ -110,6 +104,9 @@ Device::Device(const Instance *context, PhysicalDevice *physicalDevice, Features
Device::~Device()
{
if (!m_Device)
return;
m_Device.destroy(m_PipelineCache, nullptr);
if (m_Allocator)
{
@ -156,6 +153,7 @@ Device::Device(Device &&other) noexcept
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
, m_Device(Take(other.m_Device))
, m_Allocator(Take(other.m_Allocator))
, m_PipelineCache(Take(other.m_PipelineCache))
{
}
@ -168,5 +166,6 @@ Device::operator=(Device &&other) noexcept
m_PhysicalDevice = Take(other.m_PhysicalDevice);
m_Device = Take(other.m_Device);
m_Allocator = Take(other.m_Allocator);
m_PipelineCache = Take(other.m_PipelineCache);
return *this;
}

View File

@ -5,6 +5,8 @@
#include "core/instance.h"
#include "core/window.h"
#include <EASTL/array.h>
#include <EASTL/fixed_vector.h>
@ -61,9 +63,9 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat
.pUserData = nullptr,
};
u32 glfwExtensionCount = 0;
cstr *glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
eastl::fixed_vector<cstr, 3> instanceExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
u32 windowExtensionCount = 0;
cstr *windowExtensions = Window::GetInstanceExtensions(&windowExtensionCount);
eastl::fixed_vector<cstr, 3> instanceExtensions(windowExtensions, windowExtensions + windowExtensionCount);
if (enableValidation)
{
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
@ -99,6 +101,9 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat
Instance::~Instance()
{
if (!m_Instance)
return;
if (m_DebugMessenger)
{
m_Instance.destroy(m_DebugMessenger, nullptr);

View File

@ -154,11 +154,10 @@ EnumeratePhysicalDevices(const vk::Instance instance)
return physicalDevices;
}
PhysicalDevices::PhysicalDevices(const Surface *surface, const Instance *context)
PhysicalDevices::PhysicalDevices(const Surface &surface, const Instance &context)
{
auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance);
for (auto physicalDevice : physicalDevices)
for (auto physicalDevices = EnumeratePhysicalDevices(context.m_Instance); auto physicalDevice : physicalDevices)
{
this->emplace_back(surface->m_Surface, physicalDevice);
this->emplace_back(surface.m_Surface, physicalDevice);
}
}

View File

@ -8,13 +8,12 @@
#include "core/instance.h"
#include "core/window.h"
Surface::Surface(Instance *context, const Window *window, cstr name)
: m_Context(context)
, m_Name(name)
Surface::Surface(Instance &context, const Window &window)
: m_Context(&context)
{
VkSurfaceKHR surface;
auto result = Cast<vk::Result>(
glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), window->m_Window, nullptr, &surface));
glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), window.m_Window, nullptr, &surface));
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
THEN_ABORT(result)
ELSE_DEBUG("Surface {} Created", m_Name);
@ -23,15 +22,15 @@ Surface::Surface(Instance *context, const Window *window, cstr name)
Surface::~Surface()
{
if (m_Context && m_Surface)
{
if (!m_Context || !m_Context->m_Instance || !m_Surface)
return;
m_Context->m_Instance.destroy(m_Surface, nullptr);
DEBUG("Surface Destroyed");
m_Surface = nullptr;
m_Context = nullptr;
}
}
Surface::Surface(Surface &&other) noexcept
: m_Context(Take(other.m_Context))

View File

@ -11,9 +11,8 @@
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
Swapchain::Swapchain(const Surface *surface, const Device *device, Size2D size, NameString &&name)
: m_Device(device)
, m_Name(std::move(name))
Swapchain::Swapchain(const Surface &surface, const Device &device, Size2D size)
: m_Device(&device)
, m_Format(vk::Format::eUndefined)
{
this->Create(surface, size);
@ -27,11 +26,11 @@ Swapchain::~Swapchain()
Swapchain::Swapchain(Swapchain &&other) noexcept
: m_Device(other.m_Device)
, m_Swapchain(Take(other.m_Swapchain))
, m_Name(std::move(other.m_Name))
, m_Extent(other.m_Extent)
, m_Format(other.m_Format)
, m_Images(std::move(other.m_Images))
, m_ImageViews(std::move(other.m_ImageViews))
, m_ResizeCallbacks(std::move(other.m_ResizeCallbacks))
{
}
@ -42,29 +41,29 @@ Swapchain::operator=(Swapchain &&other) noexcept
return *this;
m_Device = other.m_Device;
m_Swapchain = Take(other.m_Swapchain);
m_Name = std::move(other.m_Name);
m_Extent = other.m_Extent;
m_Format = other.m_Format;
m_Images = std::move(other.m_Images);
m_ImageViews = std::move(other.m_ImageViews);
m_ResizeCallbacks = std::move(other.m_ResizeCallbacks);
return *this;
}
void
Swapchain::Create(const Surface *surface, Size2D size)
Swapchain::Create(const Surface &surface, Size2D size)
{
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
m_Extent = GetExtent(size, &surfaceCapabilities);
while (m_Extent.width == 0 || m_Extent.height == 0)
{
glfwWaitEvents();
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
m_Extent = GetExtent(size, &surfaceCapabilities);
}
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface->m_Surface);
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface->m_Surface);
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface.m_Surface);
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface.m_Surface);
m_Format = vk::Format::eUndefined;
auto swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
@ -95,16 +94,14 @@ Swapchain::Create(const Surface *surface, Size2D size)
}
u32 swapchainImageCount = 3;
if (surfaceCapabilities.maxImageCount > 0)
{
swapchainImageCount =
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
}
u32 maxImageCount =
glm::max(swapchainImageCount, glm::max(surfaceCapabilities.maxImageCount, surfaceCapabilities.minImageCount));
swapchainImageCount = glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, maxImageCount);
// TODO: Note that different queues might need the images to be shared.
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
.surface = surface->m_Surface,
.surface = surface.m_Surface,
.minImageCount = swapchainImageCount,
.imageFormat = m_Format,
.imageColorSpace = swapchainColorSpace,
@ -120,28 +117,30 @@ Swapchain::Create(const Surface *surface, Size2D size)
};
vk::Device device = m_Device->m_Device;
NameString name = "Swapchain of ";
name += m_Device->m_Name;
vk::SwapchainKHR swapchain;
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
ERROR_IF(Failed(result), "'{}' creation failed. Cause {}", name, result)
THEN_ABORT(result)
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
ELSE_DEBUG("Created '{}'", name);
// Irrelevant on the first run. Required for re-creation.
Cleanup();
m_Swapchain = swapchain;
m_Device->SetName(m_Swapchain, m_Name.data());
m_Device->SetName(m_Swapchain, m_Device->m_Name.data());
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
THEN_ABORT(result);
// Managed by the Swapchain.
m_Images.resize(swapchainImageCount);
m_Images.resize(swapchainImageCount, nullptr);
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
THEN_ABORT(result);
vk::ImageViewCreateInfo viewCreateInfo = {
@ -165,7 +164,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
vk::ImageView imageView;
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", m_Name, index, result)
ERROR_IF(Failed(result), "Failed creating {}'s image view [{}]. Cause {}", name, index, result)
THEN_ABORT(result);
m_ImageViews.push_back(imageView);
@ -173,7 +172,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
++index;
}
DEBUG("Swapchain {} Image Views created.", m_Name);
DEBUG("{} Image Views created.", name);
for (auto &callback : m_ResizeCallbacks)
{
@ -184,24 +183,31 @@ Swapchain::Create(const Surface *surface, Size2D size)
void
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
{
m_ResizeCallbacks.emplace_back(callback);
m_ResizeCallbacks.emplace_back(std::move(callback));
}
void
Swapchain::Cleanup()
{
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
if (!m_Swapchain)
return;
NameString name = "Swapchain of ";
name += m_Device->m_Name;
for (const auto imageView : m_ImageViews)
{
m_Device->m_Device.destroy(imageView, nullptr);
}
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
DEBUG("Swapchain {} Image Views destroyed.", name);
m_ImageViews.clear();
m_Images.clear();
if (m_Swapchain)
{
m_Device->m_Device.destroy(m_Swapchain, nullptr);
m_Swapchain = nullptr;
DEBUG("Swapchain '{}' destroyed.", m_Name);
DEBUG("Swapchain '{}' destroyed.", name);
}
}

View File

@ -11,6 +11,30 @@
std::atomic_uint64_t Window::m_WindowCount = 0;
std::atomic_bool Window::m_IsGlfwInit = false;
void
Window::SetupLibrary()
{
if (!m_IsGlfwInit)
{
if (!glfwInit())
{
const char *error = nullptr;
const auto code = glfwGetError(&error);
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
THEN_ABORT(code);
}
m_WindowCount = 0;
m_IsGlfwInit = true;
}
}
cstr*
Window::GetInstanceExtensions(u32 *extensionCount)
{
SetupLibrary();
return glfwGetRequiredInstanceExtensions(extensionCount);
}
void
Window::RequestExit() const noexcept
{
@ -42,18 +66,7 @@ Window::Window(const cstr title, Size2D extent, const b8 isFullScreen)
{
m_Name = title;
if (!m_IsGlfwInit)
{
if (!glfwInit())
{
const char *error = nullptr;
const auto code = glfwGetError(&error);
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
THEN_ABORT(code);
}
m_WindowCount = 0;
m_IsGlfwInit = true;
}
SetupLibrary();
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
ERROR_IF(!monitor, "No monitor found");

View File

@ -4,8 +4,5 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core
PRIVATE
"buffer_manager.cpp"
"image_manager.cpp"
"view_manager.cpp"
"sampler_manager.cpp"
"device.cpp"
"commit_manager.cpp")

View File

@ -1,160 +0,0 @@
// =============================================
// Aster: buffer_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/buffer_manager.h"
using namespace systems;
Ref<StorageBuffer>
BufferManager::CreateStorageBuffer(const usize size, const cstr name) const
{
// TODO: Storage and Index buffer are set.
// This is hacky and should be improved.
constexpr vk::BufferUsageFlags usage =
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eShaderDeviceAddress | vk::BufferUsageFlagBits::eTransferDst;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<StorageBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<UniformBuffer>
BufferManager::CreateUniformBuffer(const usize size, const cstr name) const
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<UniformBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<StagingBuffer>
BufferManager::CreateStagingBuffer(const usize size, const cstr name) const
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eTransferSrc;
constexpr VmaAllocationCreateFlags createFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<StagingBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<VertexBuffer>
BufferManager::CreateVertexBuffer(const usize size, const cstr name) const
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eVertexBuffer;
constexpr VmaAllocationCreateFlags createFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<VertexBuffer>(Buffer{m_Device, size, usage, createFlags, memoryUsage, name});
}
//
// void
// UniformBuffer::Init(const Device *device, const usize size, const cstr name)
//{
// Allocate(device, size, vk::BufferUsageFlagBits::eUniformBuffer,
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
// VMA_MEMORY_USAGE_AUTO, name);
//}
//
// void
// StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
//{
// Init(device, size, hostVisible, false, name);
//}
//
// void
// StorageBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
//{
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer;
// if (deviceAddress)
// {
// usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
// }
// if (hostVisible)
// {
// Allocate(device, size, usage,
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
// VMA_MEMORY_USAGE_AUTO, name);
// }
// else
// {
// usage |= vk::BufferUsageFlagBits::eTransferDst;
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
// }
//}
//
// void
// StorageIndexBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
//{
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
// if (deviceAddress)
// {
// usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
// }
// if (hostVisible)
// {
// Allocate(device, size, usage,
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
// VMA_MEMORY_USAGE_AUTO, name);
// }
// else
// {
// usage |= vk::BufferUsageFlagBits::eTransferDst;
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
// }
//}
//
// void
// IndirectBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
//{
// vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer |
// vk::BufferUsageFlagBits::eShaderDeviceAddress;
// if (hostVisible)
// {
// Allocate(device, size, usage,
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
// VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
// VMA_MEMORY_USAGE_AUTO, name);
// }
// else
// {
// usage |= vk::BufferUsageFlagBits::eTransferDst;
// Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
// }
//}
//
// void
// VertexBuffer::Init(const Device *device, usize size, cstr name)
//{
// Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, 0,
// VMA_MEMORY_USAGE_AUTO, name);
//}
//
// void
// IndexBuffer::Init(const Device *device, usize size, cstr name)
//{
// Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, 0,
// VMA_MEMORY_USAGE_AUTO, name);
//}
//
// void
// StagingBuffer::Init(const Device *device, usize size, cstr name)
//{
// Allocate(device, size, vk::BufferUsageFlagBits::eTransferSrc,
// VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
// VMA_MEMORY_USAGE_AUTO, name);
//}
BufferManager::BufferManager(const Device *device)
: m_Device{device}
{
}

View File

@ -0,0 +1,694 @@
// =============================================
// Aster: device.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/device.h"
#include "core/queue_allocation.h"
#include "core/window.h"
#include "systems/resource.h"
static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT =
QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute |
QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer;
PhysicalDevice
systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices)
{
for (auto &physicalDevice : physicalDevices)
{
const bool hasAllRequiredQueues =
std::ranges::any_of(physicalDevice.m_QueueFamilies, [](const auto &queueFamilyProp) {
return (queueFamilyProp.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT;
});
const bool isNotCpu = physicalDevice.m_DeviceProperties.deviceType != vk::PhysicalDeviceType::eCpu;
const bool hasPresentMode = !physicalDevice.m_PresentModes.empty();
const bool hasSurfaceFormat = !physicalDevice.m_SurfaceFormats.empty();
if (hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues)
{
return physicalDevice;
}
}
ERROR("No suitable GPU available on the system.")
THEN_ABORT(vk::Result::eErrorUnknown);
}
// ====================================================================================================
#pragma region Buffer Management
// ====================================================================================================
Ref<StorageBuffer>
systems::Device::CreateStorageBuffer(const usize size, const cstr name)
{
// TODO: Storage and Index buffer are set.
// This is hacky and should be improved.
constexpr vk::BufferUsageFlags usage =
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eShaderDeviceAddress | vk::BufferUsageFlagBits::eTransferDst;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<StorageBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<UniformBuffer>
systems::Device::CreateUniformBuffer(const usize size, const cstr name)
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<UniformBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<StagingBuffer>
systems::Device::CreateStagingBuffer(const usize size, const cstr name)
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eTransferSrc;
constexpr VmaAllocationCreateFlags createFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<StagingBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
Ref<VertexBuffer>
systems::Device::CreateVertexBuffer(const usize size, const cstr name)
{
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eVertexBuffer;
constexpr VmaAllocationCreateFlags createFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
return std::make_shared<VertexBuffer>(Buffer{&m_Device, size, usage, createFlags, memoryUsage, name});
}
#pragma endregion
// ====================================================================================================
#pragma region Image Management
// ====================================================================================================
vk::ImageCreateInfo ToImageCreateInfo(const systems::Texture2DCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const systems::TextureCubeCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const systems::AttachmentCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const systems::DepthStencilImageCreateInfo &createInfo);
namespace usage_flags
{
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags STORAGE =
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
} // namespace usage_flags
Ref<Image>
systems::Device::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
Image::Flags flags = {};
if (createInfo.m_IsSampled)
flags |= Image::FlagBits::eSampled;
if (createInfo.m_IsStorage)
flags |= Image::FlagBits::eStorage;
m_Device.SetName(image, createInfo.m_Name);
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format, flags,
layerCount, mipLevels);
}
Ref<ImageCube>
systems::Device::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
Image::Flags flags = Image::FlagBits::eCube;
if (createInfo.m_IsSampled)
flags |= Image::FlagBits::eSampled;
if (createInfo.m_IsStorage)
flags |= Image::FlagBits::eStorage;
m_Device.SetName(image, createInfo.m_Name);
return CastImage<ImageCube>(std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent,
imageCreateInfo.format, flags, layerCount, mipLevels));
}
Ref<Image>
systems::Device::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device.SetName(image, createInfo.m_Name);
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
Image::Flags{}, layerCount, mipLevels);
}
Ref<Image>
systems::Device::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device.SetName(image, createInfo.m_Name);
return std::make_shared<Image>(&m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
Image::Flags{}, layerCount, mipLevels);
}
vk::ImageCreateInfo
ToImageCreateInfo(const systems::Texture2DCreateInfo &createInfo)
{
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
extent.width, extent.height, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = mipLevels,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const systems::TextureCubeCreateInfo &createInfo)
{
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
.imageType = vk::ImageType::e2D,
.format = format,
.extent = {side, side, 1},
.mipLevels = mipLevels,
.arrayLayers = 6,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const systems::AttachmentCreateInfo &createInfo)
{
auto &[format, extent, name] = createInfo;
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const systems::DepthStencilImageCreateInfo &createInfo)
{
auto &[extent, name] = createInfo;
constexpr auto format = vk::Format::eD24UnormS8Uint;
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}
#pragma endregion
// ====================================================================================================
#pragma region View Management
// ====================================================================================================
Ref<ImageView>
systems::Device::CreateView(const ViewCreateInfo<Image> &createInfo)
{
const auto layerCount = createInfo.GetLayerCount();
const auto mipCount = createInfo.GetMipLevelCount();
ERROR_IF((createInfo.m_BaseLayer + layerCount) > createInfo.m_Image->m_LayerCount, "Invalid Layer Access")
THEN_ABORT(-1);
ERROR_IF((createInfo.m_BaseMipLevel + mipCount) > createInfo.m_Image->m_MipLevels, "Invalid Mip Level Access")
THEN_ABORT(-1);
vk::ImageView view;
const auto imageViewCreateInfo = Cast<vk::ImageViewCreateInfo>(createInfo);
auto result = m_Device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
m_Device.SetName(view, createInfo.m_Name);
return std::make_shared<ImageView>(createInfo.m_Image, view, createInfo.m_Image->m_Extent, createInfo.m_BaseLayer,
layerCount, createInfo.m_BaseMipLevel, mipCount);
}
#pragma endregion
// ====================================================================================================
#pragma region Image - View Combined Management
// ====================================================================================================
Ref<TextureView>
systems::Device::CreateTexture2DWithView(const Texture2DCreateInfo &createInfo)
{
return CreateView<TextureView>({
.m_Image = CastImage<Texture>(CreateTexture2D(createInfo)),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
Ref<ImageCubeView>
systems::Device::CreateTextureCubeWithView(const TextureCubeCreateInfo &createInfo)
{
return CreateView<ImageCubeView>({
.m_Image = CreateTextureCube(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::eCube,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
Ref<ImageView>
systems::Device::CreateAttachmentWithView(const AttachmentCreateInfo &createInfo)
{
return CreateView({
.m_Image = CreateAttachment(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
});
}
Ref<ImageView>
systems::Device::CreateDepthStencilImageWithView(const DepthStencilImageCreateInfo &createInfo)
{
return CreateView({
.m_Image = CreateDepthStencilImage(createInfo),
.m_Name = createInfo.m_Name,
.m_ViewType = vk::ImageViewType::e2D,
.m_AspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
});
}
#pragma endregion
// ====================================================================================================
#pragma region Sampler
// ====================================================================================================
Ref<Sampler>
systems::Device::CreateSampler(const SamplerCreateInfo &createInfo)
{
auto vkCreateInfo = Cast<vk::SamplerCreateInfo>(createInfo);
if (const auto iter = m_HashToSamplerIdx.find(vkCreateInfo);
iter != m_HashToSamplerIdx.end() && !iter->second.expired())
{
return iter->second.lock();
}
auto object = std::make_shared<Sampler>(&m_Device, vkCreateInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(vkCreateInfo, object);
return object;
}
#pragma endregion
QueueAllocation
FindAppropriateQueueAllocation(const PhysicalDevice &physicalDevice)
{
for (auto &queueFamilyInfo : physicalDevice.m_QueueFamilies)
{
if ((queueFamilyInfo.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT)
{
return {
.m_Family = queueFamilyInfo.m_Index,
.m_Count = queueFamilyInfo.m_Count,
};
}
}
ERROR("No suitable queue family on the GPU.")
THEN_ABORT(vk::Result::eErrorUnknown);
}
systems::Device::Device(const DeviceCreateInfo &createInfo)
: m_Window{createInfo.m_Window}
{
assert(createInfo.m_AppName);
assert(createInfo.m_PhysicalDeviceSelector);
m_Instance = Instance{createInfo.m_AppName, createInfo.m_AppVersion};
m_Surface = Surface{m_Instance, createInfo.m_Window};
const auto physicalDevices = PhysicalDevices{m_Surface, m_Instance};
auto physicalDevice = createInfo.m_PhysicalDeviceSelector(physicalDevices);
Features features = createInfo.m_Features;
// TODO: Add the other queues
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(physicalDevice);
m_Device = ::Device{
m_Instance, physicalDevice, features, {&queueAllocation, 1}, createInfo.m_PipelineCacheData, createInfo.m_Name};
m_GraphicsQueueFamily = queueAllocation.m_Family;
m_GraphicsQueue = m_Device.GetQueue(m_GraphicsQueueFamily, 0);
m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()};
u32 index = 0;
for (auto &frame : m_Frames)
{
frame = CreateFrame(index++);
}
}
systems::Device::~Device()
{
for (auto &frame : m_Frames)
{
m_Device.m_Device.destroy(Take(frame.m_FrameAvailableFence), nullptr);
m_Device.m_Device.destroy(Take(frame.m_ImageAcquireSem), nullptr);
m_Device.m_Device.destroy(Take(frame.m_RenderFinishSem), nullptr);
m_Device.m_Device.destroy(Take(frame.m_Pool), nullptr);
}
m_HashToSamplerIdx.clear();
}
// ====================================================================================================
#pragma region Frames
// ====================================================================================================
systems::Frame
systems::Device::CreateFrame(u32 frameIndex)
{
vk::CommandPool pool;
vk::Fence fence;
vk::Semaphore acquire;
vk::Semaphore finished;
NameString name = "Frame ";
name += Cast<char>(frameIndex + '0');
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = m_GraphicsQueueFamily,
};
AbortIfFailedMV(m_Device.m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &pool),
"Could not command pool for frame {}", frameIndex);
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
AbortIfFailedMV(m_Device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence),
"Could not create a fence for frame {}", frameIndex);
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &acquire),
"Could not create IA semaphore for frame {}.", frameIndex);
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &finished),
"Could not create RF semaphore for frame {}.", frameIndex);
m_Device.SetName(pool, name.c_str());
m_Device.SetName(fence, name.c_str());
m_Device.SetName(acquire, name.c_str());
m_Device.SetName(finished, name.c_str());
DEBUG("Frame {} created successfully.", frameIndex);
return Frame{
.m_Device = &m_Device,
.m_Pool = pool,
.m_FrameAvailableFence = fence,
.m_ImageAcquireSem = acquire,
.m_RenderFinishSem = finished,
.m_FrameIdx = frameIndex,
};
}
systems::Frame &
systems::Device::GetNextFrame()
{
Frame &currentFrame = m_Frames[m_CurrentFrameIdx];
u32 frameIndex = m_CurrentFrameIdx;
m_CurrentFrameIdx = (m_CurrentFrameIdx + 1) % MAX_FRAMES_IN_FLIGHT;
AbortIfFailedMV(m_Device.m_Device.waitForFences(1, &currentFrame.m_FrameAvailableFence, true, MaxValue<u64>),
"Waiting for fence {} failed.", frameIndex);
u32 imageIndex = 0;
bool imageAcquired = false;
while (!imageAcquired)
{
switch (auto result = m_Device.m_Device.acquireNextImageKHR(
m_Swapchain.m_Swapchain, MaxValue<u64>, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex))
{
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:
DEBUG("Recreating Swapchain. Cause: {}", result);
m_Swapchain.Create(m_Surface, m_Window.get().GetSize());
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;
currentFrame.m_SwapchainImage = m_Swapchain.m_Images[imageIndex];
currentFrame.m_SwapchainImageView = m_Swapchain.m_ImageViews[imageIndex];
return currentFrame;
}
systems::Device::Receipt
systems::Device::Submit(Frame &frame, GraphicsContext &graphicsContext)
{
// TODO Consider a semaphore pool that you can 'pick' a semaphore from and use for submits.
// This can be used as a wait-semaphore for the next submit if you say 'depends on'.
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &frame.m_ImageAcquireSem,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &graphicsContext.m_Cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &frame.m_RenderFinishSem,
};
auto result = m_GraphicsQueue.submit(1, &submitInfo, frame.m_FrameAvailableFence);
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
THEN_ABORT(result);
return 0;
}
void
systems::Device::Present(Frame &frame)
{
vk::PresentInfoKHR presentInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &frame.m_RenderFinishSem,
.swapchainCount = 1,
.pSwapchains = &m_Swapchain.m_Swapchain,
.pImageIndices = &frame.m_ImageIdx,
.pResults = nullptr,
};
switch (auto result = m_GraphicsQueue.presentKHR(&presentInfo))
{
case vk::Result::eSuccess:
break;
case vk::Result::eErrorOutOfDateKHR:
case vk::Result::eSuboptimalKHR:
DEBUG("Recreating Swapchain. Cause: {}", result);
m_Swapchain.Create(m_Surface, m_Window.get().GetSize());
break; // Present failed. We do nothing. Frame is skipped.
default:
AbortIfFailedM(result, "Swapchain Present failed.");
}
}
systems::GraphicsContext
systems::Frame::CreateGraphicsContext()
{
vk::CommandBuffer cmd;
const vk::CommandBufferAllocateInfo allocateInfo{
.commandPool = m_Pool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailedMV(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &cmd), "Command buffer {} alloc failed.",
m_FrameIdx);
return GraphicsContext{cmd};
}
#pragma endregion
// ====================================================================================================
#pragma region Context Impl
// ====================================================================================================
void
systems::Context::Begin()
{
vk::CommandBufferBeginInfo commandBufferBeginInfo = {
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit,
};
auto result = m_Cmd.begin(&commandBufferBeginInfo);
ERROR_IF(Failed(result), "Could not begin context") THEN_ABORT(result);
}
void
systems::Context::End()
{
auto result = m_Cmd.end();
ERROR_IF(Failed(result), "Could not end context") THEN_ABORT(result);
}
void
systems::GraphicsContext::BindPipeline(vk::Pipeline pipeline)
{
m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
}
void
systems::GraphicsContext::DrawIndexed(u32 indexCount)
{
m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0);
}
void
systems::GraphicsContext::Dependency(const vk::DependencyInfo &dependencyInfo)
{
m_Cmd.pipelineBarrier2(&dependencyInfo);
}
void
systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo)
{
m_Cmd.beginRendering(&renderingInfo);
}
void
systems::GraphicsContext::EndRendering()
{
m_Cmd.endRendering();
}
#pragma endregion

View File

@ -1,236 +0,0 @@
// =============================================
// Aster: image_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/image_manager.h"
#include "core/device.h"
using namespace systems;
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo);
namespace usage_flags
{
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags STORAGE =
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
} // namespace usage_flags
ImageManager::ImageManager(const Device *device)
: m_Device{device}
{
}
Ref<Image>
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
Image::Flags flags = {};
if (createInfo.m_IsSampled)
flags |= Image::FlagBits::eSampled;
if (createInfo.m_IsStorage)
flags |= Image::FlagBits::eStorage;
m_Device->SetName(image, createInfo.m_Name);
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format, flags,
layerCount, mipLevels);
}
Ref<ImageCube>
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
Image::Flags flags = Image::FlagBits::eCube;
if (createInfo.m_IsSampled)
flags |= Image::FlagBits::eSampled;
if (createInfo.m_IsStorage)
flags |= Image::FlagBits::eStorage;
m_Device->SetName(image, createInfo.m_Name);
return CastImage<ImageCube>(std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent,
imageCreateInfo.format, flags, layerCount, mipLevels));
}
Ref<Image>
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(image, createInfo.m_Name);
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
Image::Flags{}, layerCount, mipLevels);
}
Ref<Image>
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage rawImage;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::Image image = rawImage;
u8 layerCount = Cast<u8>(imageCreateInfo.arrayLayers);
u8 mipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(image, createInfo.m_Name);
return std::make_shared<Image>(m_Device, image, allocation, imageCreateInfo.extent, imageCreateInfo.format,
Image::Flags{}, layerCount, mipLevels);
}
vk::ImageCreateInfo
ToImageCreateInfo(const Texture2DCreateInfo &createInfo)
{
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
extent.width, extent.height, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = mipLevels,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const TextureCubeCreateInfo &createInfo)
{
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
.imageType = vk::ImageType::e2D,
.format = format,
.extent = {side, side, 1},
.mipLevels = mipLevels,
.arrayLayers = 6,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const AttachmentCreateInfo &createInfo)
{
auto &[format, extent, name] = createInfo;
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo)
{
auto &[extent, name] = createInfo;
constexpr auto format = vk::Format::eD24UnormS8Uint;
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}

View File

@ -1,36 +0,0 @@
// =============================================
// Aster: sampler_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/sampler_manager.h"
#include "core/device.h"
using namespace systems;
SamplerManager::SamplerManager(const Device *device)
: m_Device{device}
{
}
SamplerManager::~SamplerManager()
{
m_HashToSamplerIdx.clear();
}
Ref<Sampler>
SamplerManager::CreateSampler(const SamplerCreateInfo &createInfo)
{
auto vkCreateInfo = Cast<vk::SamplerCreateInfo>(createInfo);
if (const auto iter = m_HashToSamplerIdx.find(vkCreateInfo); iter != m_HashToSamplerIdx.end() && !iter->second.expired())
{
return iter->second.lock();
}
auto object = std::make_shared<Sampler>(m_Device, vkCreateInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(vkCreateInfo, object);
return object;
}

View File

@ -1,37 +0,0 @@
// =============================================
// Aster: view_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/view_manager.h"
#include "core/device.h"
using namespace systems;
ViewManager::ViewManager(const Device *device)
: m_Device{device}
{
}
Ref<ImageView>
ViewManager::CreateView(const ViewCreateInfo<Image> &createInfo) const
{
const auto layerCount = createInfo.GetLayerCount();
const auto mipCount = createInfo.GetMipLevelCount();
ERROR_IF((createInfo.m_BaseLayer + layerCount) > createInfo.m_Image->m_LayerCount, "Invalid Layer Access")
THEN_ABORT(-1);
ERROR_IF((createInfo.m_BaseMipLevel + mipCount) > createInfo.m_Image->m_MipLevels, "Invalid Mip Level Access")
THEN_ABORT(-1);
vk::ImageView view;
const auto imageViewCreateInfo = Cast<vk::ImageViewCreateInfo>(createInfo);
auto result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
m_Device->SetName(view, createInfo.m_Name);
return std::make_shared<ImageView>(createInfo.m_Image, view, createInfo.m_Image->m_Extent, createInfo.m_BaseLayer,
layerCount, createInfo.m_BaseMipLevel, mipCount);
}

View File

@ -8,18 +8,16 @@
#include "aster/core/buffer.h"
#include "aster/core/constants.h"
#include "aster/core/instance.h"
#include "aster/core/device.h"
#include "aster/core/physical_device.h"
#include "aster/core/pipeline.h"
#include "aster/core/swapchain.h"
#include "aster/core/window.h"
#include "aster/systems/device.h"
#include "helpers.h"
#include "aster/systems/resource_manager.h"
#include <EASTL/array.h>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv";
@ -57,61 +55,21 @@ struct Vertex
}
};
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 **)
{
MIN_LOG_LEVEL(Logger::LogType::eInfo);
Window window = {"Triangle (Aster)", {640, 480}};
Instance context = {"Triangle", VERSION};
Surface surface = {&context, &window, "Primary"};
systems::Device device{{
.m_AppName = "Triangle",
.m_Window = window,
.m_Name = "Primary",
.m_Features = {.m_Vulkan12Features = {.bufferDeviceAddress = true},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}},
}};
PhysicalDevices physicalDevices = {&surface, &context};
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {
.m_Vulkan12Features = {.bufferDeviceAddress = true},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
Pipeline pipeline = CreatePipeline(&device, &swapchain);
systems::ResourceManager resourceManager{&device};
vk::CommandPool copyPool;
vk::CommandBuffer copyBuffer;
{
vk::CommandPoolCreateInfo poolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueAllocation.m_Family,
};
auto result = device.m_Device.createCommandPool(&poolCreateInfo, nullptr, &copyPool);
ERROR_IF(Failed(result), "Copy command pool creation failed. Cause: {}", result) THEN_ABORT(result);
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
.commandPool = copyPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
result = device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, &copyBuffer);
ERROR_IF(Failed(result), "Copy command buffer allocation failed. Cause: {}", result) THEN_ABORT(result);
}
Pipeline pipeline = CreatePipeline(&device.m_Device, &device.m_Swapchain);
// eastl::array<Vertex, 3> vertices{};
eastl::array vertices = {
@ -119,22 +77,24 @@ main(int, char **)
Vertex{.m_Position = {0.5f, -0.5f, 0.0f}, .m_Color = {0.0f, 1.0f, 0.0f}},
Vertex{.m_Position = {0.0f, 0.5f, 0.0f}, .m_Color = {0.0f, 0.0f, 1.0f}},
};
auto vbo = resourceManager.Buffers().CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO");
auto vbo = device.CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO");
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
Size2D swapchainSize = device.GetSwapchainSize();
// Persistent variables
vk::Viewport viewport = {
.x = 0,
.y = Cast<f32>(swapchain.m_Extent.height),
.width = Cast<f32>(swapchain.m_Extent.width),
.height = -Cast<f32>(swapchain.m_Extent.height),
.y = Cast<f32>(swapchainSize.m_Height),
.width = Cast<f32>(swapchainSize.m_Width),
.height = -Cast<f32>(swapchainSize.m_Height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
vk::Rect2D scissor = {
.offset = {0, 0},
.extent = swapchain.m_Extent,
.extent = Cast<vk::Extent2D>(swapchainSize),
};
vk::ImageSubresourceRange subresourceRange = {
@ -151,8 +111,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo topOfThePipeDependency = {
@ -166,8 +126,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eNone,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo renderToPresentDependency = {
@ -175,70 +135,34 @@ main(int, char **)
.pImageMemoryBarriers = &renderToPresentBarrier,
};
// Frames
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{
frames.emplace_back(&device, queueAllocation.m_Family, i);
}
INFO("Starting loop");
u32 frameIndex = 0;
while (window.Poll())
{
Frame *currentFrame = &frames[frameIndex];
systems::Frame &currentFrame = device.GetNextFrame();
auto result = device.m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>);
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
THEN_ABORT(result);
vk::CommandBuffer cmd;
u32 imageIndex;
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(&surface, window.GetSize());
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;
continue; // Image acquire has failed. We move to the next frame.
default:
ERROR("Waiting for swapchain image {} failed. Cause: {}", frameIndex, result)
THEN_ABORT(result);
}
}
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
result = device.m_Device.resetFences(1, &currentFrame->m_FrameAvailableFence);
ERROR_IF(Failed(result), "Fence {} reset failed. Cause: {}", frameIndex, result)
THEN_ABORT(result);
const vk::CommandBufferAllocateInfo allocateInfo{
.commandPool = currentFrame.m_Pool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailedMV(device.m_Device.m_Device.allocateCommandBuffers(&allocateInfo, &cmd),
"Command buffer {} alloc failed.", currentFrame.m_FrameIdx);
result = device.m_Device.resetCommandPool(currentFrame->m_Pool, {});
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
THEN_ABORT(result);
auto context = currentFrame.CreateGraphicsContext();
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
topOfThePipeBarrier.image = currentFrame.m_SwapchainImage;
renderToPresentBarrier.image = currentFrame.m_SwapchainImage;
topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage;
context.Begin();
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
result = cmd.begin(&beginInfo);
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
THEN_ABORT(result);
cmd.pipelineBarrier2(&topOfThePipeDependency);
context.Dependency(topOfThePipeDependency);
// Render
vk::RenderingAttachmentInfo attachmentInfo = {
.imageView = currentImageView,
.imageView = currentFrame.m_SwapchainImageView,
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
@ -247,115 +171,37 @@ main(int, char **)
};
vk::RenderingInfo renderingInfo = {
.renderArea = {.extent = swapchain.m_Extent},
.renderArea = {.extent = scissor.extent},
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &attachmentInfo,
};
cmd.beginRendering(&renderingInfo);
context.BeginRendering(renderingInfo);
cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
usize offsets = 0;
cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
cmd.draw(3, 1, 0, 0);
// cmd.setViewport(0, 1, &viewport);
// cmd.setScissor(0, 1, &scissor);
// cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
// usize offsets = 0;
// cmd.bindVertexBuffers(0, 1, &vbo->m_Buffer, &offsets);
// cmd.draw(3, 1, 0, 0);
cmd.endRendering();
context.EndRendering();
cmd.pipelineBarrier2(&renderToPresentDependency);
context.Dependency(renderToPresentDependency);
result = cmd.end();
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
THEN_ABORT(result);
context.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,
};
result = commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence);
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
THEN_ABORT(result);
device.Submit(currentFrame, context);
vk::PresentInfoKHR presentInfo = {
.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(&surface, window.GetSize());
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:
ERROR("Command queue present failed. Cause: {}", result)
THEN_ABORT(result);
}
}
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
device.Present(currentFrame);
}
device.WaitIdle();
device.m_Device.destroy(copyPool, nullptr);
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,
};
vk::Result result = device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool);
ERROR_IF(Failed(result), "Could not command pool for frame {}. Cause: {}", frameCount, result)
THEN_ABORT(result);
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
result = device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence);
ERROR_IF(Failed(result), "Could not create a fence for frame {}. Cause: {}", frameCount, result)
THEN_ABORT(result);
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem);
ERROR_IF(Failed(result), "Could not create IA semaphore for frame {}. Cause: {}", frameCount, result)
THEN_ABORT(result);
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem);
ERROR_IF(Failed(result), "Could not create RF semaphore for frame {}. Cause: {}", frameCount, result)
THEN_ABORT(result);
const vk::CommandBufferAllocateInfo allocateInfo = {
.commandPool = m_Pool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
result = m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer);
ERROR_IF(Failed(result), "Command buffer allocation failed. Cause: {}", result)
THEN_ABORT(result);
DEBUG("Frame {} created successfully.", frameCount);
}
Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain)
{
@ -498,13 +344,3 @@ CreateShader(const Device *device, cstr shaderFile)
THEN_ABORT(result);
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");
}