Compare commits

..

2 Commits

Author SHA1 Message Date
kidrigger 16bb54273c Fix error in CMakeLists in model_render.
Fixed Whitespace in light_manager.cpp in samples/03_model_render that
caused a bug on linux.
2025-02-22 01:05:34 +01:00
Anish Bhobe cba6c580cf Added new BufferManager.
1. Added a new Manager template and buffer manager.
2. Fixed all warnings.
2025-02-20 00:31:27 +01:00
24 changed files with 194 additions and 1344 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@ build/
.vs/
.direnv/
.ccls-cache/
*.user

View File

@ -10,7 +10,6 @@ set(CMAKE_CXX_EXTENSIONS OFF)
if (MSVC)
set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "/O3")
add_compile_definitions(_HAS_EXCEPTIONS=0)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(${MSVC_DEFINES})

View File

@ -3,7 +3,6 @@
cmake_minimum_required(VERSION 3.13)
add_subdirectory("core")
add_subdirectory("systems")
add_subdirectory("util")
target_sources(aster_core

View File

@ -17,5 +17,4 @@ INTERFACE
"image.h"
"surface.h"
"size.h"
"type_traits.h"
"window.h")

View File

@ -24,8 +24,6 @@ struct Buffer
[[nodiscard]] bool IsValid() const;
[[nodiscard]] bool IsMapped() const;
[[nodiscard]] bool IsOwned() const;
[[nodiscard]] bool IsCommitted() const;
void SetCommitted(bool committed);
void Destroy(const Device *device);
void Write(const Device *device, usize offset, usize size, const void *data);
@ -42,13 +40,9 @@ struct Buffer
constexpr static usize VALID_BUFFER_BIT = Cast<usize>(1llu << 63);
constexpr static usize OWNED_BIT = 1llu << 62;
constexpr static usize COMMITTED_BIT = 1llu << 61;
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | OWNED_BIT | COMMITTED_BIT);
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | OWNED_BIT);
};
template <>
constexpr bool concepts::GpuResource<Buffer> = true;
// Ensure that m_Size doesn't get used intrusively since it manages the state.
static_assert(offsetof(Buffer, m_Size_) > sizeof(usize));
@ -119,15 +113,3 @@ Buffer::IsOwned() const
{
return m_Size_ & OWNED_BIT;
}
inline bool
Buffer::IsCommitted() const
{
return m_Size_ & COMMITTED_BIT;
}
inline void
Buffer::SetCommitted(const bool committed)
{
m_Size_ = committed ? (m_Size_ | COMMITTED_BIT) : (m_Size_ & ~COMMITTED_BIT);
}

View File

@ -26,8 +26,6 @@
#if !defined(NDEBUG)
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
#endif
#include "type_traits.h"
#include <EASTL/fixed_string.h>
#include <EASTL/string.h>
#include <vk_mem_alloc.h>
@ -177,18 +175,6 @@ ClosestPowerOfTwo(const u32 val)
return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2;
}
[[nodiscard]] constexpr u32
GetMaskOffset(u32 val)
{
u32 count = 0;
while ((val & 1) == 0)
{
count++;
val = val >> 1;
}
return count;
}
template <>
struct fmt::formatter<vk::Result> : nested_formatter<std::string>
{

View File

@ -49,19 +49,13 @@ struct Image
[[nodiscard]] bool IsValid() const;
[[nodiscard]] bool IsOwned() const;
[[nodiscard]] u32 GetMipLevels() const;
[[nodiscard]] bool IsCommitted() const;
void SetCommitted(bool committed);
void Destroy(const Device *device);
constexpr static u8 VALID_BIT = 1u << 7;
constexpr static u8 OWNED_BIT = 1u << 6;
constexpr static u8 COMMITTED_BIT = 1u << 5;
};
template <>
constexpr bool concepts::GpuResource<Image> = true;
struct Texture : Image
{
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr);
@ -122,16 +116,4 @@ inline u32
Image::GetMipLevels() const
{
return m_MipLevels;
}
inline bool
Image::IsCommitted() const
{
return m_Flags_ & COMMITTED_BIT;
}
inline void
Image::SetCommitted(const bool committed)
{
m_Flags_ = committed ? (m_Flags_ | COMMITTED_BIT) : (m_Flags_ & ~COMMITTED_BIT);
}
}

View File

@ -1,36 +0,0 @@
// =============================================
// Aster: type_traits.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
struct Device;
namespace concepts
{
template <typename T>
concept DeviceDestructible = requires(T a, Device *p) {
{ a.Destroy(p) } -> std::convertible_to<void>;
};
template <typename T>
concept Committable = requires(T a, bool v) {
{ a.IsCommitted() } -> std::convertible_to<bool>;
{ a.SetCommitted(v) } -> std::convertible_to<void>;
};
template <typename T>
constexpr bool GpuResource = false;
template <typename T>
concept RenderResource = GpuResource<T> and std::is_default_constructible_v<T> and std::is_trivially_copyable_v<T> and
DeviceDestructible<T> and Committable<T>;
template <typename T>
constexpr bool IsHandle = false;
template <typename THandle>
concept HandleType = IsHandle<THandle> and RenderResource<typename THandle::Type>;
} // namespace concepts

View File

@ -5,6 +5,4 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core
INTERFACE
"manager.h"
"buffer_manager.h"
"image_manager.h"
"render_resource_manager.h")
"buffer_manager.h")

View File

@ -1,5 +1,5 @@
// =============================================
// Aster: buffer_manager.h
// Aster: manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
@ -11,14 +11,11 @@
namespace systems
{
using BufferHandle = Handle<Buffer>;
class BufferManager final : public Manager<Buffer>
{
public:
BufferManager(const Device *device, const u32 maxCount, const u8 binding);
BufferManager(const Device *device, const u32 maxCount);
[[nodiscard]] Handle CreateStorageBuffer(usize size, cstr name = nullptr);
[[nodiscard]] Handle CreateUniformBuffer(usize size, cstr name = nullptr);
Ref CreateStorageBuffer(usize size, cstr name = nullptr);
};
} // namespace systems

View File

@ -1,60 +0,0 @@
// =============================================
// Aster: image_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "aster/core/image.h"
#include "manager.h"
namespace systems
{
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;
};
using ImageHandle = Handle<Image>;
class ImageManager final : public Manager<Image>
{
public:
ImageManager(const Device *device, const u32 maxCount, const u8 binding);
[[nodiscard]] Handle CreateTexture2D(const Texture2DCreateInfo &createInfo);
[[nodiscard]] Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo);
[[nodiscard]] Handle CreateAttachment(const AttachmentCreateInfo &createInfo);
[[nodiscard]] Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
};
} // namespace systems

View File

@ -6,47 +6,56 @@
#pragma once
#include "aster/aster.h"
#include "aster/core/type_traits.h"
#include <EASTL/intrusive_ptr.h>
struct Device;
template <concepts::RenderResource T>
class Handle;
template <concepts::RenderResource T>
template <typename T>
requires std::is_default_constructible_v<T>
class Manager
{
friend Handle<T>;
using InternalType = T;
public:
using Type = T;
using Handle = Handle<Type>;
static_assert(sizeof(Handle) == sizeof(u32));
constexpr static u32 MAX_HANDLES = Handle::INDEX_MASK + 1;
struct Type : InternalType
{
void
AddRef()
{
m_Instance->AddRef(this);
}
/**
* Constructor for the Manager class template.
* @param device Device with which resources are created.
* @param maxCount Max number of resources that can be created (maxCount <= Handle::INDEX_MASK)
* @param binding The shader binding at which this manager will bind its resources.
*/
explicit Manager(const Device *device, const u32 maxCount, const u8 binding)
void
Release()
{
m_Instance->Release(this);
}
u32
GetIndex()
{
return m_Instance->GetIndex(this);
}
};
static_assert(sizeof(Type) == sizeof(InternalType));
using Ref = eastl::intrusive_ptr<Type>;
static Manager *Instance();
explicit Manager(const Device *device, const u32 maxCount)
: m_MaxCount{maxCount}
, m_Binding{binding}
, m_FreeHead{0}
, m_Device{device}
{
assert(!m_Instance);
assert(maxCount <= MAX_HANDLES);
m_Data = new Type[m_MaxCount];
m_RefCount = new std::atomic<u32>[m_MaxCount];
m_RefCount = new u32[m_MaxCount];
for (u32 i = 0; i < m_MaxCount; ++i)
{
*Recast<u32 *>(&m_Data[i]) = (i + 1);
m_RefCount[i] = (i + 1);
}
m_Instance = this;
}
virtual ~Manager()
@ -66,296 +75,73 @@ class Manager
m_MaxCount = 0;
m_FreeHead = 0;
m_Device = nullptr;
m_Instance = nullptr;
}
/**
* @warning only to be used internally.
* @return The only constructed instance of this manager.
*/
static Manager *
Instance()
{
assert(m_Instance);
return m_Instance;
}
PIN_MEMORY(Manager);
private:
Type *m_Data = nullptr; // Data also keeps the freelist during 'not use'.
std::atomic<u32> *m_RefCount = nullptr; // Associated reference count for each of the instances in Data.
u32 m_MaxCount = 0; // Max number of resources supported.
u32 m_FreeHead = 0;
u8 m_Binding = 0;
static Manager *m_Instance;
/**
* User is expected to type-check.
* @param index Actual index of the resource in the m_Data array. Not type checked.
*/
void
AddRef(const u32 index)
AddRef(Type *p)
{
assert(index < m_MaxCount);
auto index = GetIndex(p);
++m_RefCount[index];
}
/**
* User is expected to type-check.
* @param index Actual index of the resource in the m_Data array. Not type checked.
*/
void
Release(const u32 index)
Release(Type *p)
{
assert(index < m_MaxCount);
const u32 rc = --m_RefCount[index];
assert(p->IsValid());
auto index = GetIndex(p);
auto rc = --m_RefCount[index];
// Overflow case.
assert(rc != MaxValue<u32>);
if (rc == 0)
{
// TODO: Don't destroy here. Separate out to a cleanup routine.
m_Data[index].Destroy(m_Device);
p->Destroy(m_Device);
m_RefCount[index] = m_FreeHead;
m_FreeHead = index;
}
}
/**
* User is expected to type-check.
* @param index Actual index of the resource in the m_Data array. Not type checked.
* @return Pointer to the resource at the index.
*/
Type *
Fetch(const u32 index)
u32
GetIndex(Type *p)
{
assert(index < m_MaxCount);
return &m_Data[index];
auto index = p - m_Data;
assert(index >= 0 && index < m_MaxCount);
return Cast<u32>(index);
}
private:
Type *m_Data = nullptr;
u32 *m_RefCount = nullptr;
u32 m_MaxCount = 0;
u32 m_FreeHead = 0;
static Manager *m_Instance;
protected:
const Device *m_Device;
/**
* Internal Method to Allocate a resource on the manager.
* @return [Handle, Type*] Where Type* is available to initialize the resource.
*/
[[nodiscard]] std::pair<Handle, Type *>
void
SetInstance(Manager *instance)
{
assert(!m_Instance);
m_Instance = instance;
}
Ref
Alloc()
{
ERROR_IF(m_FreeHead >= m_MaxCount, "Max buffers allocated.") THEN_ABORT(-1);
const auto index = m_FreeHead;
Type *pAlloc = &m_Data[index];
m_FreeHead = *Recast<u32 *>(pAlloc);
return {Handle{index, m_Binding}, pAlloc};
}
};
template <concepts::RenderResource T>
class Ref
{
public:
using Type = T;
using Handle = Handle<Type>;
using Manager = Manager<Type>;
protected:
Handle m_Handle;
Type *m_Pointer = nullptr;
friend Handle;
void
InitPtr()
{
m_Pointer = m_Handle.Fetch();
}
public:
Type *
Get()
{
assert(m_Pointer);
return m_Pointer;
}
const Type *
Get() const
{
assert(m_Pointer);
return m_Pointer;
}
Type *
operator->()
{
return Get();
}
const Type *
operator->() const
{
return Get();
}
Type &
operator*()
{
return *Get();
}
const Type &
operator*() const
{
return Get();
}
// The only constructor requires a valid construction.
explicit Ref(Handle &&handle)
: m_Handle{std::forward<Handle>(handle)}
{
InitPtr();
}
// The only constructor requires a valid construction.
explicit Ref(const Handle &&handle)
: m_Handle{handle}
{
InitPtr();
}
Ref(const Ref &other) = default;
Ref(Ref &&other) noexcept = default;
Ref &operator=(const Ref &other) = default;
Ref &operator=(Ref &&other) noexcept = default;
~Ref() = default;
};
class RawHandle
{
protected:
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
constexpr static u32 INDEX_MASK = 0x0FFFFFFF;
constexpr static u32 TYPE_MASK = ~INDEX_MASK;
constexpr static u32 TYPE_OFFSET = GetMaskOffset(TYPE_MASK);
u32 m_Internal = INVALID_HANDLE;
RawHandle(const u32 index, const u8 typeId)
: m_Internal{(index & INDEX_MASK) | (typeId & TYPE_MASK)}
{
}
explicit RawHandle(const u32 internal)
: m_Internal{internal}
{
}
public:
[[nodiscard]] bool
IsValid() const
{
return m_Internal != INVALID_HANDLE;
}
[[nodiscard]] u32
GetIndex() const
{
return m_Internal & INDEX_MASK;
}
[[nodiscard]] u32
GetType() const
{
return (m_Internal & TYPE_MASK) >> TYPE_OFFSET;
}
bool
operator==(const RawHandle &other) const
{
return m_Internal == other.m_Internal;
}
};
template <concepts::RenderResource T>
class Handle : public RawHandle
{
public:
using Type = T;
using Manager = Manager<Type>;
protected:
// The only constructor requires a valid construction.
Handle(const u32 index, const u8 typeId)
: RawHandle{index, typeId}
{
AddRef();
}
friend Manager;
friend Ref<T>;
public:
Handle(const Handle &other)
: RawHandle{other}
{
AddRef();
}
Handle(Handle &&other) noexcept
: RawHandle{std::exchange(other.m_Internal, m_Internal)}
{
}
[[nodiscard]] Ref<T>
ToPointer()
{
return Ref{std::move(*this)};
}
[[nodiscard]] Type *
Fetch() const
{
return Manager::Instance()->Fetch(m_Internal);
}
Handle &
operator=(const Handle &other)
{
if (this == &other)
return *this;
m_Internal = other.m_Internal;
AddRef();
return *this;
}
Handle &
operator=(Handle &&other) noexcept
{
if (this == &other)
return *this;
std::swap(m_Internal, other.m_Internal);
return *this;
}
~Handle()
{
if (m_Internal != INVALID_HANDLE)
{
Release();
}
}
protected:
void
AddRef()
{
Manager::Instance()->AddRef(GetIndex());
}
void
Release()
{
Manager::Instance()->Release(GetIndex());
m_FreeHead = m_RefCount[index];
m_RefCount[index] = 0;
return Ref{&m_Data[index]};
}
};

View File

@ -1,157 +0,0 @@
// =============================================
// Aster: render_resource_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "buffer_manager.h"
#include "image_manager.h"
#include "EASTL/deque.h"
#include "EASTL/vector.h"
namespace systems
{
class RenderResourceManager
{
private:
union WriteInfo {
vk::DescriptorBufferInfo uBufferInfo;
vk::DescriptorImageInfo uImageInfo;
vk::BufferView uBufferView;
explicit WriteInfo(const vk::DescriptorBufferInfo &info);
explicit WriteInfo(const vk::DescriptorImageInfo &info);
explicit WriteInfo(const vk::BufferView &info);
};
using WriteCommand = vk::WriteDescriptorSet;
union WriteOwner {
Handle<Buffer> uBufferHandle;
Handle<Image> uImageHandle;
explicit WriteOwner(const Handle<Buffer> &handle);
explicit WriteOwner(const Handle<Image> &handle);
WriteOwner(const WriteOwner &other)
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = other.uBufferHandle;
break;
case IMAGE_BINDING_INDEX:
uImageHandle = other.uImageHandle;
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
WriteOwner(WriteOwner &&other) noexcept
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = std::move(other.uBufferHandle);
break;
case IMAGE_BINDING_INDEX:
uImageHandle = std::move(other.uImageHandle);
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
WriteOwner &
operator=(const WriteOwner &other)
{
if (this == &other)
return *this;
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = other.uBufferHandle;
break;
case IMAGE_BINDING_INDEX:
uImageHandle = other.uImageHandle;
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
return *this;
}
WriteOwner &
operator=(WriteOwner &&other) noexcept
{
if (this == &other)
return *this;
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle = std::move(other.uBufferHandle);
break;
case IMAGE_BINDING_INDEX:
uImageHandle = std::move(other.uImageHandle);
break;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
return *this;
}
~WriteOwner()
{
switch (uRawHandle.GetType())
{
case BUFFER_BINDING_INDEX:
uBufferHandle.~Handle();
return;
case IMAGE_BINDING_INDEX:
uImageHandle.~Handle();
return;
default:
ERROR("Invalid Handle type.") THEN_ABORT(-1);
}
}
private:
RawHandle uRawHandle;
};
public:
RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages);
void Commit(concepts::HandleType auto &handle);
private:
BufferManager m_BufferManager;
ImageManager m_ImageManager;
vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet;
constexpr static u8 BUFFER_BINDING_INDEX = 0;
constexpr static u8 IMAGE_BINDING_INDEX = 1;
eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<WriteOwner> m_WriteOwner;
#if !defined(ASTER_NDEBUG)
usize m_CommitedBufferCount = 0;
usize m_CommitedTextureCount = 0;
usize m_CommitedStorageTextureCount = 0;
#endif
};
} // namespace systems

View File

@ -109,7 +109,7 @@ Remember, we use upside down viewport.
void
TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name)
{
WARN_IF(!IsPowerOfTwo(cubeSide), "Image Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide, name ? name : "<unnamed>");
WARN_IF(!IsPowerOfTwo(cubeSide), "Image {1} is {0}x{0} (Non Power of Two)", cubeSide, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
@ -119,7 +119,7 @@ TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bo
usage |= vk::ImageUsageFlagBits::eTransferSrc;
}
const vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
vk::ImageCreateInfo imageCreateInfo = {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
@ -216,7 +216,7 @@ AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imag
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create attachment image view {}. Cause: {}", name, result) THEN_ABORT(result);
ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Image = image;
m_View = view;

View File

@ -5,6 +5,4 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core
PRIVATE
"manager.cpp"
"buffer_manager.cpp"
"image_manager.cpp"
"render_resource_manager.cpp")
"buffer_manager.cpp")

View File

@ -5,14 +5,12 @@
#include "systems/buffer_manager.h"
Manager<Buffer> *Manager<Buffer>::m_Instance = nullptr;
Manager<Buffer> *Manager<Buffer>::m_Instance;
using namespace systems;
BufferHandle
BufferManager::CreateStorageBuffer(const usize size, const cstr name)
systems::BufferManager::Ref
systems::BufferManager::CreateStorageBuffer(const usize size, const cstr name)
{
auto [handle, object] = Alloc();
Ref object = Alloc();
// TODO: Storage and Index buffer are set.
// This is hacky and should be improved.
@ -24,25 +22,11 @@ BufferManager::CreateStorageBuffer(const usize size, const cstr name)
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
return std::move(handle);
return object;
}
Manager<Buffer>::Handle
BufferManager::CreateUniformBuffer(const usize size, const cstr name)
{
auto [handle, object] = Alloc();
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;
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
return std::move(handle);
}
BufferManager::BufferManager(const Device *device, const u32 maxCount, const u8 binding)
: Manager{device, maxCount, binding}
systems::BufferManager::BufferManager(const Device *device, const u32 maxCount)
: Manager{device, maxCount}
{
SetInstance(this);
}

View File

@ -1,316 +0,0 @@
// =============================================
// Aster: buffer_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/image_manager.h"
#include "core/device.h"
Manager<Image> *Manager<Image>::m_Instance = nullptr;
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
ImageHandle
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
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);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::eCube,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
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);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
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);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
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);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
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 vk::Format 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,
};
}
ImageManager::ImageManager(const Device *device, const u32 maxCount, const u8 binding)
: Manager{device, maxCount, binding}
{
}

View File

@ -1,195 +0,0 @@
// =============================================
// Aster: render_resource_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/render_resource_manager.h"
#include "EASTL/array.h"
#include "core/device.h"
#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)
using namespace systems;
u32
GetHandleInternal(concepts::HandleType auto &handle)
{
return *Recast<u32 *>(&handle);
}
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Buffer> &handle)
: uBufferHandle(handle)
{
}
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Image> &handle)
: uImageHandle(handle)
{
}
RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages)
: m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX}
, m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX}
{
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = maxBuffers,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = maxImages,
},
//vk::DescriptorPoolSize{
// .type = vk::DescriptorType::eStorageImage,
// .descriptorCount = storageTexturesCount,
//},
};
const vk::DescriptorPoolCreateInfo poolCreateInfo = {
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
.maxSets = 1,
.poolSizeCount = Cast<u32>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
};
AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
eastl::array descriptorLayoutBindings = {
vk::DescriptorSetLayoutBinding{
.binding = BUFFER_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = Cast<u32>(maxBuffers),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
vk::DescriptorSetLayoutBinding{
.binding = IMAGE_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = Cast<u32>(maxImages),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
//vk::DescriptorSetLayoutBinding{
// .binding = STORAGE_TEXTURE_BINDING_INDEX,
// .descriptorType = vk::DescriptorType::eStorageImage,
// .descriptorCount = Cast<u32>(storageTexturesCount),
// .stageFlags = vk::ShaderStageFlagBits::eAll,
//},
};
vk::DescriptorBindingFlags bindingFlags =
vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind;
eastl::array<vk::DescriptorBindingFlags, decltype(descriptorLayoutBindings)::count> layoutBindingFlags;
layoutBindingFlags.fill(bindingFlags);
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {
.bindingCount = Cast<u32>(layoutBindingFlags.size()),
.pBindingFlags = layoutBindingFlags.data(),
};
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.pNext = &bindingFlagsCreateInfo,
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
.bindingCount = Cast<u32>(descriptorLayoutBindings.size()),
.pBindings = descriptorLayoutBindings.data(),
};
AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = m_DescriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &m_SetLayout,
};
AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
device->SetName(m_SetLayout, "Bindless Layout");
device->SetName(m_DescriptorPool, "Bindless Pool");
device->SetName(m_DescriptorSet, "Bindless Set");
}
void
systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
{
using HandleType = decltype(handle)::Type;
if constexpr (std::is_same_v<HandleType, Buffer>)
{
const Buffer *buffer = handle.Fetch();
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = buffer->m_Buffer,
.offset = 0,
.range = buffer->GetSize(),
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
}
else if constexpr (std::is_same_v<HandleType, Image>)
{
const Image *image = handle.Fetch();
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = nullptr /* TODO Sampler */,
.imageView = image->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = IMAGE_BINDING_INDEX,
.dstArrayElement = handle.GetIndex(),
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
} else {
static_assert(false && "Type is currently unsupported");
}
m_WriteOwner.emplace_back(handle);
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
: uBufferInfo{info}
{
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info)
: uImageInfo{info}
{
}
RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info)
: uBufferView{info}
{
}

View File

@ -10,9 +10,9 @@
#include "aster/core/context.h"
#include "aster/core/device.h"
#include "aster/core/physical_device.h"
#include "aster/core/window.h"
#include "aster/core/pipeline.h"
#include "aster/core/swapchain.h"
#include "aster/core/window.h"
#include "helpers.h"
@ -83,10 +83,7 @@ main(int, char **)
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {
.m_Vulkan12Features = {.bufferDeviceAddress = true},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
};
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.dynamicRendering = true}};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
@ -179,40 +176,24 @@ main(int, char **)
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::ImageMemoryBarrier2 topOfThePipeBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
vk::ImageMemoryBarrier topOfThePipeBarrier = {
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo topOfThePipeDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &topOfThePipeBarrier,
};
vk::ImageMemoryBarrier2 renderToPresentBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
.dstAccessMask = vk::AccessFlagBits2::eNone,
vk::ImageMemoryBarrier renderToPresentBarrier = {
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo renderToPresentDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &renderToPresentBarrier,
};
// Frames
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{
frames.emplace_back(&device, queueAllocation.m_Family, i);
}
@ -269,7 +250,8 @@ main(int, char **)
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
THEN_ABORT(result);
cmd.pipelineBarrier2(&topOfThePipeDependency);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
// Render
vk::RenderingAttachmentInfo attachmentInfo = {
@ -278,7 +260,7 @@ main(int, char **)
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 0.0f},
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
};
vk::RenderingInfo renderingInfo = {
@ -299,7 +281,8 @@ main(int, char **)
cmd.endRendering();
cmd.pipelineBarrier2(&renderToPresentDependency);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
result = cmd.end();
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)

View File

@ -7,8 +7,6 @@ cmake_minimum_required(VERSION 3.13)
add_executable(box "box.cpp" "stb_image.h")
add_shader(box "shader/box.vert.glsl")
add_shader(box "shader/box.frag.glsl")
add_shader(box "shader/box.vs.hlsl")
add_shader(box "shader/box.ps.hlsl")
target_link_libraries(box PRIVATE aster_core)
target_link_libraries(box PRIVATE util_helper)

View File

@ -9,25 +9,23 @@
#include "aster/core/constants.h"
#include "aster/core/context.h"
#include "aster/core/device.h"
#include "aster/core/image.h"
#include "aster/core/physical_device.h"
#include "aster/core/pipeline.h"
#include "aster/core/swapchain.h"
#include "aster/core/window.h"
#include "aster/core/image.h"
#include "helpers.h"
#define STB_IMAGE_IMPLEMENTATION
#include "aster/systems/buffer_manager.h"
#include "aster/systems/image_manager.h"
#include "frame.h"
#include "stb_image.h"
#include <EASTL/array.h>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv";
constexpr auto VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv";
struct ImageFile
{
@ -78,9 +76,32 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
struct Vertex
{
vec3 m_Position;
f32 m_PositionW = 1.0;
vec2 m_TexCoord0;
vec2 m_Padding0_ = {0.0f, 0.0f};
constexpr static vk::VertexInputBindingDescription
GetBinding(const u32 binding)
{
return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
}
constexpr static eastl::array<vk::VertexInputAttributeDescription, 2>
GetAttributes(const u32 binding)
{
return {
vk::VertexInputAttributeDescription{
.location = 0,
.binding = binding,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, m_Position),
},
vk::VertexInputAttributeDescription{
.location = 1,
.binding = binding,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, m_TexCoord0),
},
};
}
};
struct Camera
@ -106,8 +127,7 @@ main(int, char **)
Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan12Features = {.bufferDeviceAddress = true},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
.m_Vulkan13Features = {.dynamicRendering = true},
};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
@ -115,9 +135,6 @@ main(int, char **)
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
Pipeline pipeline = CreatePipeline(&device, &swapchain);
systems::BufferManager bufferManager{&device, 12, 0};
systems::ImageManager imageManager{&device, 12, 1};
Camera camera = {
.m_Model = {1.0f},
.m_View = glm::lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)),
@ -138,10 +155,6 @@ main(int, char **)
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
},
};
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
@ -222,32 +235,24 @@ main(int, char **)
assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
auto vbo = bufferManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer").ToPointer();
auto crate = imageManager
.CreateTexture2D({
.m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture",
})
.ToPointer();
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
VertexBuffer vbo;
Texture crate;
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Crate Texture");
{
StagingBuffer imageStaging;
StagingBuffer vertexStaging, imageStaging;
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
vertexStaging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
imageStaging.Init(&device, imageFile.GetSize(), "Image Staging");
imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data);
vk::ImageMemoryBarrier2 imageReadyToWrite = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
vk::ImageMemoryBarrier imageReadyToWrite = {
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate->m_Image,
.image = crate.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -257,21 +262,13 @@ main(int, char **)
.layerCount = 1,
},
};
vk::DependencyInfo imageReadyToWriteDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageReadyToWrite,
};
vk::ImageMemoryBarrier2 imageReadyToRead = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader,
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
vk::ImageMemoryBarrier imageReadyToRead = {
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate->m_Image,
.image = crate.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -281,10 +278,6 @@ main(int, char **)
.layerCount = 1,
},
};
vk::DependencyInfo imageReadyToReadDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageReadyToRead,
};
vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {};
@ -292,8 +285,11 @@ main(int, char **)
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(copyBuffer.begin(&beginInfo));
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &imageReadyToWrite);
copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency);
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = vertexStaging.GetSize()};
copyBuffer.copyBuffer(vertexStaging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy);
vk::BufferImageCopy imageCopy = {
.bufferOffset = 0,
@ -309,10 +305,11 @@ main(int, char **)
.imageOffset = {},
.imageExtent = {imageFile.m_Width, imageFile.m_Height, 1},
};
copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate.m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
&imageCopy);
copyBuffer.pipelineBarrier2(&imageReadyToReadDependency);
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {},
0, nullptr, 0, nullptr, 1, &imageReadyToRead);
AbortIfFailed(copyBuffer.end());
@ -330,6 +327,7 @@ main(int, char **)
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr);
vertexStaging.Destroy(&device);
imageStaging.Destroy(&device);
}
@ -353,23 +351,19 @@ main(int, char **)
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
}
auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer();
ubo->Write(&device, 0, sizeof camera, &camera);
UniformBuffer ubo;
ubo.Init(&device, sizeof camera, "Camera UBO");
ubo.Write(&device, 0, sizeof camera, &camera);
vk::DescriptorBufferInfo descriptorBufferInfo = {
.buffer = ubo->m_Buffer,
.buffer = ubo.m_Buffer,
.offset = 0,
.range = ubo->GetSize(),
.range = ubo.GetSize(),
};
vk::DescriptorImageInfo descriptorImageInfo = {
.sampler = sampler,
.imageView = crate->m_View,
.imageView = crate.m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
vk::DescriptorBufferInfo descriptorStorageBufferInfo = {
.buffer = vbo->m_Buffer,
.offset = 0,
.range = vbo->GetSize(),
};
eastl::array writeDescriptors = {
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
@ -387,14 +381,6 @@ main(int, char **)
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &descriptorImageInfo,
},
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &descriptorStorageBufferInfo,
},
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
@ -428,56 +414,35 @@ main(int, char **)
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::ImageMemoryBarrier2 topOfThePipeBarrier = {
// For Color Attachment output ref:
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7193#issuecomment-1875960974
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
vk::ImageMemoryBarrier topOfThePipeBarrier = {
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo topOfThePipeDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &topOfThePipeBarrier,
};
vk::ImageMemoryBarrier2 renderToPresentBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
.dstAccessMask = vk::AccessFlagBits2::eNone,
vk::ImageMemoryBarrier renderToPresentBarrier = {
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::DependencyInfo renderToPresentDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &renderToPresentBarrier,
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
for (auto &depthImage : depthImages)
{
depthImage.Init(&device, swapchain.m_Extent, "Depth");
}
auto initDepthImages = [&imageManager, &depthImages, &frameManager] (const vk::Extent2D extent) {
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) {
for (auto &depthImage : depthImages)
{
depthImages.push_back(
imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer());
depthImage.Destroy(&device);
depthImage.Init(&device, extent, "Depth");
}
};
initDepthImages(swapchain.m_Extent);
auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) {
depthImages.clear();
initDepthImages(extent);
};
swapchain.RegisterResizeCallback(recreateDepthBuffers);
Time::Init();
@ -488,7 +453,7 @@ main(int, char **)
Time::Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo->Write(&device, 0, sizeof camera, &camera);
ubo.Write(&device, 0, sizeof camera, &camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
@ -496,7 +461,7 @@ main(int, char **)
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View;
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View;
topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage;
@ -504,7 +469,8 @@ main(int, char **)
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(cmd.begin(&beginInfo));
cmd.pipelineBarrier2(&topOfThePipeDependency);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
// Render
eastl::array attachmentInfos = {
@ -514,7 +480,7 @@ main(int, char **)
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 0.0f},
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
},
};
@ -541,11 +507,14 @@ main(int, char **)
cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr);
usize offsets = 0;
cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets);
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
cmd.endRendering();
cmd.pipelineBarrier2(&renderToPresentDependency);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
AbortIfFailed(cmd.end());
@ -565,9 +534,17 @@ main(int, char **)
}
device.WaitIdle();
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
}
device.m_Device.destroy(sampler, nullptr);
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr);
crate.Destroy(&device);
vbo.Destroy(&device);
return 0;
}
@ -605,12 +582,6 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
},
vk::DescriptorSetLayoutBinding{
.binding = 2,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
};
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()),
@ -630,7 +601,15 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
device->SetName(pipelineLayout, "Box Layout");
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {};
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,

View File

@ -1,19 +0,0 @@
struct FS_Input {
float2 UV0 : TEXCOORD0;
};
struct FS_Output
{
float4 ColorTarget : SV_Target0;
};
[[vk::binding(1, 0)]] Texture2D<float4> Texture;
[[vk::binding(1, 0)]] SamplerState Sampler;
FS_Output main(FS_Input StageInput) {
FS_Output output;
output.ColorTarget = float4(Texture.Sample(Sampler, StageInput.UV0).rgb, 1.0);
return output;
}

View File

@ -1,36 +0,0 @@
struct VS_Input
{
uint VertexIndex : SV_VertexID;
};
struct VS_Output
{
float2 UV0 : TEXCOORD0;
float4 VertexPosition : SV_Position;
};
struct CameraData {
float4x4 Model;
float4x4 View;
float4x4 Projection;
};
struct VertexData {
float4 Position;
float2 UV0;
};
[[vk::binding(0, 0)]] ConstantBuffer<CameraData> Camera;
[[vk::binding(2, 0)]] StructuredBuffer<VertexData> Vertices;
VS_Output main(VS_Input StageInput) {
VS_Output output;
output.UV0 = Vertices[StageInput.VertexIndex].UV0;
float4 position = Vertices[StageInput.VertexIndex].Position;
output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position)));
return output;
}

View File

@ -23,7 +23,7 @@ TextureManager::Init(const u32 maxCapacity)
TextureHandle
TextureManager::Commit(Texture *texture)
{
ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for committal")
ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for commital")
THEN_ABORT(-1);
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)