Compare commits

...

2 Commits

Author SHA1 Message Date
Anish Bhobe afec1e3e32 Reimplemented RenderResourceManager. 2025-03-24 22:31:47 +01:00
Anish Bhobe 396810d203 RenderResourceManager handles images and bindless. 2025-03-02 19:19:43 +01:00
30 changed files with 1064 additions and 808 deletions

View File

@ -18,4 +18,5 @@ INTERFACE
"surface.h" "surface.h"
"size.h" "size.h"
"type_traits.h" "type_traits.h"
"window.h") "window.h"
"sampler.h")

View File

@ -13,45 +13,66 @@ struct Device;
struct Buffer struct Buffer
{ {
const Device *m_Device = nullptr; ///< Will be used for book-keeping when buffer is invalid.
vk::Buffer m_Buffer = nullptr; vk::Buffer m_Buffer = nullptr;
VmaAllocation m_Allocation = nullptr; VmaAllocation m_Allocation = nullptr;
// If the buffer is host visible, it should be (and stay) mapped. u8 *m_Mapped = nullptr; ///< If the buffer is host visible, it should be (and stay) mapped.
u8 *m_Mapped = nullptr;
[[nodiscard]] usize GetSize() const; usize m_Size = 0;
[[nodiscard]] bool IsHostVisible() const; std::atomic<u32> m_RefCount = 0;
[[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); [[nodiscard]] bool
void Write(const Device *device, usize offset, usize size, const void *data); IsHostVisible() const
{
return m_Mapped;
}
[[nodiscard]] bool
IsValid() const
{
return m_Buffer;
}
[[nodiscard]] bool
IsMapped() const
{
return m_Mapped;
}
void
AddRef()
{
assert(++m_RefCount > 0);
}
void
Release()
{
const auto rc = --m_RefCount;
assert(rc < MaxValue<u32>);
if (rc == 0)
{
Destroy();
}
}
[[nodiscard]] bool
IsReferenced()
{
return m_RefCount;
}
void Destroy();
void Write(usize offset, usize size, const void *data);
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name); VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
uptr uptr GetDeviceAddress(const Device *device) const;
GetDeviceAddress(const Device *device);
// Buffer.size is used for bookkeeping
// If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`.
usize m_Size_ = 0;
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);
}; };
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));
struct UniformBuffer : Buffer struct UniformBuffer : Buffer
{ {
void Init(const Device *device, usize size, cstr name = nullptr); void Init(const Device *device, usize size, cstr name = nullptr);
@ -89,45 +110,3 @@ struct StagingBuffer : Buffer
{ {
void Init(const Device *device, usize size, cstr name = nullptr); void Init(const Device *device, usize size, cstr name = nullptr);
}; };
inline usize
Buffer::GetSize() const
{
return m_Size_ & SIZE_MASK;
}
inline bool
Buffer::IsHostVisible() const
{
return IsMapped();
}
inline bool
Buffer::IsValid() const
{
return m_Size_ & VALID_BUFFER_BIT;
}
inline bool
Buffer::IsMapped() const
{
return m_Mapped;
}
inline bool
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

@ -211,3 +211,13 @@ struct fmt::formatter<eastl::fixed_string<TType, TCount, TOverflow>> : nested_fo
return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); }); return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); });
} }
}; };
namespace aster
{
template <typename TRef, typename TVal>
TVal &
Deref(TRef ref)
{
return ref.Deref();
}
}

View File

@ -35,33 +35,56 @@ ToOffset3D(const vk::Extent3D &extent)
struct Image struct Image
{ {
const Device *m_Device = nullptr;
vk::Image m_Image = nullptr; vk::Image m_Image = nullptr;
vk::ImageView m_View = nullptr;
VmaAllocation m_Allocation = nullptr; VmaAllocation m_Allocation = nullptr;
vk::ImageView m_View = nullptr;
vk::Extent3D m_Extent; vk::Extent3D m_Extent;
// Image.m_MipLevels_ is used for bookkeeping std::atomic<u32> m_RefCount;
// If the image is Invalid, the remaining data in Image is used intrusively by `GpuResourceManager`.
u8 m_EmptyPadding_ = 0; u8 m_EmptyPadding_ = 0;
u8 m_Flags_ = 0; u8 m_Flags_ = 0;
u8 m_LayerCount = 0; u8 m_LayerCount = 0;
u8 m_MipLevels = 0; u8 m_MipLevels = 0;
[[nodiscard]] bool IsValid() const; [[nodiscard]] bool
[[nodiscard]] bool IsOwned() const; IsValid() const
[[nodiscard]] u32 GetMipLevels() const; {
[[nodiscard]] bool IsCommitted() const; return m_Image;
void SetCommitted(bool committed); }
void Destroy(const Device *device); [[nodiscard]] bool
IsReferenced() const
{
return m_RefCount;
}
constexpr static u8 VALID_BIT = 1u << 7; [[nodiscard]] u32
constexpr static u8 OWNED_BIT = 1u << 6; GetMipLevels() const
constexpr static u8 COMMITTED_BIT = 1u << 5; {
return m_MipLevels;
}
void
AddRef()
{
const auto rc = ++m_RefCount;
assert(rc > 0);
}
void
Release()
{
const auto rc = --m_RefCount;
assert(rc < MaxValue<u32>);
if (rc == 0)
{
Destroy();
}
}
void Destroy();
}; };
template <>
constexpr bool concepts::GpuResource<Image> = true;
struct Texture : Image struct Texture : Image
{ {
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr); void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr);
@ -105,33 +128,3 @@ struct StorageTextureCube : StorageTexture
}; };
static_assert(sizeof(StorageTextureCube) == sizeof(Image)); static_assert(sizeof(StorageTextureCube) == sizeof(Image));
inline bool
Image::IsValid() const
{
return m_Flags_ & VALID_BIT;
}
inline bool
Image::IsOwned() const
{
return m_Flags_ & OWNED_BIT;
}
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

@ -0,0 +1,47 @@
// =============================================
// Aster: sampler.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
struct Device;
// TODO Refactor the Buffer Hierarchy
struct Sampler
{
vk::Sampler m_Sampler = nullptr;
std::atomic<u32> m_RefCount = 0;
void Init(const Device *device, const vk::SamplerCreateInfo &samplerCreateInfo, cstr name);
void Destroy(const Device *device);
void
AddRef()
{
const auto rc = ++m_RefCount;
assert(rc > 0);
}
void
Release()
{
const auto rc = --m_RefCount;
assert(rc < MaxValue<u32>);
}
[[nodiscard]] bool
IsReferenced() const
{
return m_RefCount;
}
[[nodiscard]] bool
IsValid() const
{
return m_Sampler;
}
};

View File

@ -9,28 +9,25 @@ struct Device;
namespace concepts namespace concepts
{ {
template <typename T>
concept RefCounted = requires(T a) {
{ a.AddRef() } -> std::same_as<void>;
{ a.Release() } -> std::same_as<void>;
{ a.IsReferenced() } -> std::convertible_to<bool>;
};
template <typename T> template <typename T>
concept DeviceDestructible = requires(T a, Device *p) { concept DeviceDestructible = requires(T a, Device *p) {
{ a.Destroy(p) } -> std::convertible_to<void>; { a.Destroy(p) } -> std::same_as<void>;
}; };
template <typename T> template <typename T>
concept Committable = requires(T a, bool v) { concept SelfDestructible = requires(T a) {
{ a.IsCommitted() } -> std::convertible_to<bool>; { a.Destroy() } -> std::same_as<void>;
{ a.SetCommitted(v) } -> std::convertible_to<void>; { T::m_Device } -> std::convertible_to<const Device *>;
}; };
template <typename T> template <typename T>
constexpr bool GpuResource = false; concept Manageable = std::is_default_constructible_v<T> and (DeviceDestructible<T> or SelfDestructible<T>) and RefCounted<T>;
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 } // namespace concepts

View File

@ -7,4 +7,5 @@ INTERFACE
"manager.h" "manager.h"
"buffer_manager.h" "buffer_manager.h"
"image_manager.h" "image_manager.h"
"sampler_manager.h"
"render_resource_manager.h") "render_resource_manager.h")

View File

@ -11,7 +11,7 @@
namespace systems namespace systems
{ {
using BufferHandle = Handle<Buffer>; using BufferHandle = Manager<Buffer>::Handle;
class BufferManager final : public Manager<Buffer> class BufferManager final : public Manager<Buffer>
{ {

View File

@ -45,7 +45,7 @@ struct DepthStencilImageCreateInfo
cstr m_Name = nullptr; cstr m_Name = nullptr;
}; };
using ImageHandle = Handle<Image>; using ImageHandle = Manager<Image>::Handle;
class ImageManager final : public Manager<Image> class ImageManager final : public Manager<Image>
{ {

View File

@ -8,21 +8,20 @@
#include "aster/aster.h" #include "aster/aster.h"
#include "aster/core/type_traits.h" #include "aster/core/type_traits.h"
#include <EASTL/intrusive_ptr.h>
#include <EASTL/vector.h>
struct Device; struct Device;
template <concepts::RenderResource T> namespace systems
class Handle; {
template <concepts::RenderResource T> template <concepts::Manageable T>
class Manager class Manager
{ {
friend Handle<T>;
public: public:
using Type = T; using Type = T;
using Handle = Handle<Type>; using Handle = eastl::intrusive_ptr<Type>;
static_assert(sizeof(Handle) == sizeof(u32));
constexpr static u32 MAX_HANDLES = Handle::INDEX_MASK + 1;
/** /**
* Constructor for the Manager class template. * Constructor for the Manager class template.
@ -31,19 +30,16 @@ class Manager
* @param binding The shader binding at which this manager will bind its resources. * @param binding The shader binding at which this manager will bind its resources.
*/ */
explicit Manager(const Device *device, const u32 maxCount, const u8 binding) explicit Manager(const Device *device, const u32 maxCount, const u8 binding)
: m_MaxCount{maxCount} : m_Data{maxCount}
, m_Binding{binding} , m_Binding{binding}
, m_Device{device} , m_Device{device}
{ {
assert(!m_Instance); assert(!m_Instance && "Attempting to initialize a second Manager");
assert(maxCount <= MAX_HANDLES);
m_Data = new Type[m_MaxCount]; u32 i = 0;
m_RefCount = new std::atomic<u32>[m_MaxCount]; for (auto& element : m_Data)
for (u32 i = 0; i < m_MaxCount; ++i)
{ {
*Recast<u32 *>(&m_Data[i]) = (i + 1); *Recast<u32 *>(&element) = ++i;
} }
m_Instance = this; m_Instance = this;
@ -51,87 +47,48 @@ class Manager
virtual ~Manager() virtual ~Manager()
{ {
if (!m_Data) for (auto& element: m_Data)
return;
for (u32 i = 0; i < m_MaxCount; ++i)
{ {
m_Data[i].Destroy(m_Device); if constexpr (concepts::SelfDestructible<Type>)
{
element.Destroy();
}
else if constexpr (concepts::DeviceDestructible<Type>)
{
element.Destroy(m_Device);
}
} }
delete[] m_Data;
delete[] m_RefCount;
m_Data = nullptr;
m_RefCount = nullptr;
m_MaxCount = 0;
m_FreeHead = 0; m_FreeHead = 0;
m_Device = nullptr; m_Device = nullptr;
m_Instance = nullptr; m_Instance = nullptr;
} }
/** void
* @warning only to be used internally. Sweep()
* @return The only constructed instance of this manager. requires concepts::DeviceDestructible<Type>
*/
static Manager *
Instance()
{ {
assert(m_Instance); for (i64 i = m_Data.size() - 1; i >= 0; --i)
return m_Instance; {
if (auto *pIter = &m_Data[i]; !pIter->IsValid())
{
pIter->Destroy(m_Device);
*Recast<u32 *>(pIter) = m_FreeHead;
m_FreeHead = i;
}
}
} }
PIN_MEMORY(Manager); PIN_MEMORY(Manager);
private: private:
Type *m_Data = nullptr; // Data also keeps the freelist during 'not use'. eastl::vector<Type> m_Data; // 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; u32 m_FreeHead = 0;
u8 m_Binding = 0; u8 m_Binding = 0;
static Manager *m_Instance; 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)
{
assert(index < m_MaxCount);
++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)
{
assert(index < m_MaxCount);
const u32 rc = --m_RefCount[index];
assert(rc != MaxValue<u32>);
if (rc == 0)
{
// TODO: Don't destroy here. Separate out to a cleanup routine.
m_Data[index].Destroy(m_Device);
}
}
/**
* 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)
{
assert(index < m_MaxCount);
return &m_Data[index];
}
protected: protected:
const Device *m_Device; const Device *m_Device;
@ -139,223 +96,20 @@ class Manager
* Internal Method to Allocate a resource on the manager. * Internal Method to Allocate a resource on the manager.
* @return [Handle, Type*] Where Type* is available to initialize the resource. * @return [Handle, Type*] Where Type* is available to initialize the resource.
*/ */
[[nodiscard]] std::pair<Handle, Type *> [[nodiscard]] Handle
Alloc() Alloc()
{ {
ERROR_IF(m_FreeHead >= m_MaxCount, "Max buffers allocated.") THEN_ABORT(-1); ERROR_IF(m_FreeHead >= m_Data.size(), "Max buffers allocated.") THEN_ABORT(-1);
const auto index = m_FreeHead; const auto index = m_FreeHead;
Type *pAlloc = &m_Data[index]; Type *pAlloc = &m_Data[index];
m_FreeHead = *Recast<u32 *>(pAlloc); m_FreeHead = *Recast<u32 *>(pAlloc);
return {Handle{index, m_Binding}, pAlloc}; memset(pAlloc, 0, sizeof *pAlloc);
} if constexpr (concepts::SelfDestructible<Type>)
};
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(); pAlloc->m_Device = m_Device;
} }
} return {pAlloc};
protected:
void
AddRef()
{
Manager::Instance()->AddRef(GetIndex());
}
void
Release()
{
Manager::Instance()->Release(GetIndex());
} }
}; };
} // namespace systems

View File

@ -8,16 +8,252 @@
#include "aster/aster.h" #include "aster/aster.h"
#include "buffer_manager.h" #include "buffer_manager.h"
#include "image_manager.h" #include "image_manager.h"
#include "sampler_manager.h"
#include "aster/util/intrusive_slist.h"
#include "EASTL/deque.h" #include "EASTL/deque.h"
#include "EASTL/vector.h" #include "EASTL/intrusive_hash_map.h"
#include "EASTL/bonus/fixed_ring_buffer.h"
namespace systems namespace systems
{ {
class RenderResourceManager;
/**
* CommitEntry manages the lifetime of the committed resource.
* @tparam T Type of the committed resource.
*/
template <concepts::Manageable T>
class CommitEntry
{
private:
u32 m_Index;
explicit CommitEntry(const u32 index)
: m_Index{index}
{
AddRef();
}
friend class RenderResourceManager;
public:
CommitEntry(const CommitEntry &other)
: m_Index{other.m_Index}
{
AddRef();
}
CommitEntry(CommitEntry &&other) noexcept
: m_Index{other.m_Index}
{
AddRef();
}
CommitEntry &
operator=(const CommitEntry &other)
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
CommitEntry &
operator=(CommitEntry &&other) noexcept
{
if (this == &other)
return *this;
m_Index = other.m_Index;
AddRef();
return *this;
}
~CommitEntry()
{
Release();
}
private:
void AddRef() const; ///< Increases the refcount in the RenderResourceManager.
void Release() const; ///< Decreases the refcount in the RenderResourceManager.
};
class RenderResourceManager class RenderResourceManager
{ {
private: private:
template <concepts::Manageable T>
struct HandleMapper
{
using Type = T;
using Manager = Manager<Type>;
using Handle = typename Manager::Handle;
using CommitE = CommitEntry<Type>;
struct Entry : public eastl::intrusive_hash_node_key<Handle>
{
std::atomic<u32> m_CommitCount;
void
AddRef()
{
const auto rc = ++m_CommitCount;
assert(rc > 0);
}
void
Release()
{
const auto rc = --m_CommitCount;
assert(rc < MaxValue<u32>);
}
bool
IsReferenced() const
{
return m_CommitCount;
}
bool
operator==(const Entry &other) const
{
return this->mKey == other.mKey;
}
Entry *
Next()
{
return Recast<Entry *>(this->mpNext);
}
void
SetNext(Entry &entry)
{
this->mpNext = &entry;
}
struct Hash
{
usize
operator()(const Handle &e)
{
return eastl::hash<Type *>()(e.get());
}
};
};
static_assert(sizeof(Entry) == 24);
eastl::vector<Entry> m_Data;
IntrusiveStack<Entry> m_FreeList;
eastl::intrusive_hash_map<typename Entry::key_type, Entry, 31, typename Entry::Hash> m_InUse;
std::array<IntrusiveStack<Entry>, 4> m_ToDelete;
u8 m_ToDeleteIndex = 0;
explicit HandleMapper(const u32 maxCount)
: m_Data{maxCount}
{
// Setup freelist
for (auto it = m_Data.rbegin(); it != m_Data.rend(); ++it)
{
m_FreeList.Push(*it);
}
}
~HandleMapper()
{
for (auto & toDelete : m_ToDelete)
{
ClearEntries(toDelete);
}
}
PIN_MEMORY(HandleMapper);
/// Returns a commit, and a bool signifying if it is a new commit.
std::tuple<CommitE, bool>
Create(const Handle &object)
{
// Get-from freelist
assert(!m_FreeList.Empty());
auto it = m_InUse.find(object);
if (it != m_InUse.end())
{
it->AddRef();
auto i = GetIndex(*it);
return {CommitE{i}, false};
}
Entry &data = m_FreeList.Pop();
data.mKey = object;
data.m_CommitCount = 1;
m_InUse.insert(data);
auto i = GetIndex(data);
return {CommitE{i}, true};
}
void
AddRef(const CommitE &commit)
{
m_Data.at(commit.m_Index).AddRef();
}
void
Release(const CommitE &commit)
{
auto &entry = m_Data.at(commit.m_Index);
entry.Release();
if (!entry.IsReferenced())
{
QueueDelete(entry);
}
}
/**
* Sweeps through the delete queue.
* All freed items are cleared. (With a 3 frame delay)
*/
void
Update()
{
m_ToDeleteIndex = (m_ToDeleteIndex + 1) % m_ToDelete.size();
auto &list = m_ToDelete[m_ToDeleteIndex];
ClearEntries(list);
}
private:
u32
GetIndex(const Entry &entry)
{
return Cast<u32>(&entry - m_Data.begin());
}
void
QueueDelete(Entry &entry)
{
m_InUse.remove(entry);
m_ToDelete[m_ToDeleteIndex].Push(entry);
}
void
ClearEntries(IntrusiveStack<Entry>& entries)
{
while (auto item = entries.TryPop())
{
Entry &entry = item.value();
entry.mKey.reset();
entry.m_CommitCount = 0;
}
}
};
union WriteInfo { union WriteInfo {
vk::DescriptorBufferInfo uBufferInfo; vk::DescriptorBufferInfo uBufferInfo;
vk::DescriptorImageInfo uImageInfo; vk::DescriptorImageInfo uImageInfo;
@ -30,128 +266,96 @@ class RenderResourceManager
using WriteCommand = vk::WriteDescriptorSet; using WriteCommand = vk::WriteDescriptorSet;
union WriteOwner { // using WriteOwner = std::variant<Handle<Buffer>, Handle<Image>>;
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: public:
RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages); const Device *m_Device;
void Commit(concepts::HandleType auto &handle); RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
const SamplerHandle &defaultSampler);
~RenderResourceManager();
PIN_MEMORY(RenderResourceManager);
CommitEntry<Buffer> Commit(const BufferHandle &buffer);
CommitEntry<Image> Commit(const ImageHandle &handle);
CommitEntry<Image> Commit(const ImageHandle &image, const SamplerHandle &sampler);
void Update();
[[nodiscard]] const vk::DescriptorSetLayout &
GetDescriptorSetLayout() const
{
return m_SetLayout;
}
[[nodiscard]] const vk::DescriptorSet &
GetDescriptorSet() const
{
return m_DescriptorSet;
}
static RenderResourceManager *
Instance()
{
return m_Instance;
}
private: private:
BufferManager m_BufferManager;
ImageManager m_ImageManager;
vk::DescriptorPool m_DescriptorPool; vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet; vk::DescriptorSet m_DescriptorSet;
constexpr static u8 BUFFER_BINDING_INDEX = 0; constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
constexpr static u8 IMAGE_BINDING_INDEX = 1; constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
HandleMapper<Buffer> m_Buffers;
HandleMapper<Image> m_Images;
SamplerHandle m_DefaultSampler;
eastl::vector<vk::WriteDescriptorSet> m_Writes; eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::deque<WriteInfo> m_WriteInfos; eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<WriteOwner> m_WriteOwner; // eastl::vector<WriteOwner> m_WriteOwner;
#if !defined(ASTER_NDEBUG) static RenderResourceManager *m_Instance;
usize m_CommitedBufferCount = 0; friend CommitEntry<Buffer>;
usize m_CommitedTextureCount = 0; friend CommitEntry<Image>;
usize m_CommitedStorageTextureCount = 0;
#endif void
AddRef(const CommitEntry<Buffer> &handle)
{
m_Buffers.AddRef(handle);
}
void
AddRef(const CommitEntry<Image> &handle)
{
m_Images.AddRef(handle);
}
void
Release(const CommitEntry<Buffer> &handle)
{
m_Buffers.Release(handle);
}
void
Release(const CommitEntry<Image> &handle)
{
m_Images.Release(handle);
}
}; };
template <concepts::Manageable T>
void
CommitEntry<T>::AddRef() const
{
RenderResourceManager::Instance()->AddRef(*this);
}
template <concepts::Manageable T>
void
CommitEntry<T>::Release() const
{
RenderResourceManager::Instance()->Release(*this);
}
} // namespace systems } // namespace systems

View File

@ -0,0 +1,57 @@
// =============================================
// Aster: sampler_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "EASTL/vector_map.h"
#include "aster/aster.h"
#include "aster/core/sampler.h"
#include "manager.h"
namespace systems
{
using SamplerHandle = Manager<Sampler>::Handle;
struct SamplerCreateInfo : vk::SamplerCreateInfo
{
cstr m_Name = nullptr;
SamplerCreateInfo()
: vk::SamplerCreateInfo{
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.0f,
.anisotropyEnable = true,
.maxAnisotropy = 16,
.compareEnable = false,
.minLod = 0,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
.unnormalizedCoordinates = false,
}
{
}
};
/**
* @class SamplerManager sampler_manager.h
*
* Manages (and caches) objects of sampler. Currently Samplers are never deleted.
*/
class SamplerManager final : public Manager<Sampler>
{
eastl::vector_map<usize, Handle> m_HashToSamplerIdx;
public:
SamplerManager(const Device *device, const u32 maxCount, const u8 typeId);
~SamplerManager() override;
SamplerHandle Create(const SamplerCreateInfo &createInfo);
};
} // namespace systems

View File

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
target_sources(aster_core target_sources(aster_core
INTERFACE "logger.h") INTERFACE "logger.h" "intrusive_slist.h")

View File

@ -0,0 +1,127 @@
// =============================================
// Aster: intrusive_slist.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include <optional>
template <typename T>
concept HasNext = requires(T &a) {
{ a.Next() } -> std::same_as<T *>;
{ a.SetNext(a) };
};
struct IntrusiveStackNode
{
IntrusiveStackNode *m_Next;
IntrusiveStackNode *
Next() const
{
return m_Next;
}
void
SetNext(IntrusiveStackNode &a)
{
m_Next = &a;
}
};
template <HasNext T = IntrusiveStackNode>
struct IntrusiveStack
{
using Value = T;
using Reference = T &;
using OptionalRef = std::optional<std::reference_wrapper<T>>;
using ConstReference = const T &;
using Pointer = T *;
Pointer m_Top;
IntrusiveStack()
: m_Top{nullptr}
{
}
IntrusiveStack(IntrusiveStack &&other) noexcept
: m_Top{Take(other.m_Top)}
{
}
IntrusiveStack &
operator=(IntrusiveStack &&other) noexcept
{
if (this == &other)
return *this;
m_Top = Take(other.m_Top);
return *this;
}
DISALLOW_COPY_AND_ASSIGN(IntrusiveStack);
~IntrusiveStack()
{
m_Top = nullptr;
}
[[nodiscard]] bool
Empty() const
{
return !m_Top;
}
[[nodiscard]] Reference
Pop()
{
assert(m_Top);
Reference ref = *m_Top;
m_Top = m_Top->Next();
return ref;
}
[[nodiscard]] OptionalRef
TryPop()
{
if (m_Top)
return Pop();
return std::nullopt;
}
void
Push(Reference ref)
{
ref.SetNext(*m_Top);
m_Top = &ref;
}
[[nodiscard]] ConstReference
Peek() const
{
assert(m_Top);
return *m_Top;
}
[[nodiscard]] Reference
Peek()
{
assert(m_Top);
return *m_Top;
}
[[nodiscard]] OptionalRef
TryPeek()
{
if (m_Top)
return Peek();
return std::nullopt;
}
void
Clear()
{
m_Top = nullptr;
}
};

View File

@ -13,4 +13,5 @@ PRIVATE
"buffer.cpp" "buffer.cpp"
"image.cpp" "image.cpp"
"surface.cpp" "surface.cpp"
"window.cpp") "window.cpp"
"sampler.cpp")

View File

@ -8,21 +8,22 @@
#include "core/device.h" #include "core/device.h"
void void
Buffer::Destroy(const Device *device) Buffer::Destroy()
{ {
if (!IsValid() || !IsOwned()) if (!m_Buffer)
return; return;
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation); vmaDestroyBuffer(m_Device->m_Allocator, Take(m_Buffer), m_Allocation);
m_Size_ = 0; m_Size = 0;
} }
void void
Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name) VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name)
{ {
assert(!IsValid()); assert(!m_Buffer);
assert(size <= SIZE_MASK);
m_Device = device;
vk::BufferCreateInfo bufferCreateInfo = { vk::BufferCreateInfo bufferCreateInfo = {
.size = size, .size = size,
@ -48,7 +49,7 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
// bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible); // bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
m_Buffer = buffer; m_Buffer = buffer;
m_Size_ = size | VALID_BUFFER_BIT | OWNED_BIT; m_Size = size;
m_Allocation = allocation; m_Allocation = allocation;
m_Mapped = Cast<u8 *>(allocationInfo.pMappedData); m_Mapped = Cast<u8 *>(allocationInfo.pMappedData);
@ -56,28 +57,28 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
} }
uptr uptr
Buffer::GetDeviceAddress(const Device *device) Buffer::GetDeviceAddress(const Device *device) const
{ {
vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer}; vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer};
return device->m_Device.getBufferAddress(&addressInfo); return device->m_Device.getBufferAddress(&addressInfo);
} }
void void
Buffer::Write(const Device *device, usize offset, usize size, const void *data) Buffer::Write(usize offset, usize size, const void *data)
{ {
assert(IsHostVisible()); assert(IsHostVisible());
if (!IsMapped()) if (!IsMapped())
{ {
void *mapped; void *mapped;
auto result = Cast<vk::Result>(vmaMapMemory(device->m_Allocator, m_Allocation, &mapped)); auto result = Cast<vk::Result>(vmaMapMemory(m_Device->m_Allocator, m_Allocation, &mapped));
ERROR_IF(Failed(result), "Memory mapping failed. Cause: {}", result); ERROR_IF(Failed(result), "Memory mapping failed. Cause: {}", result);
if (!Failed(result)) if (!Failed(result))
{ {
m_Mapped = Cast<u8 *>(mapped); m_Mapped = Cast<u8 *>(mapped);
memcpy(m_Mapped + offset, data, size); memcpy(m_Mapped + offset, data, size);
vmaUnmapMemory(device->m_Allocator, m_Allocation); vmaUnmapMemory(m_Device->m_Allocator, m_Allocation);
m_Mapped = nullptr; m_Mapped = nullptr;
} }
} }

View File

@ -8,16 +8,13 @@
#include "core/device.h" #include "core/device.h"
void void
Image::Destroy(const Device *device) Image::Destroy()
{ {
if (!IsValid() || !IsOwned()) if (!IsValid())
{
m_Flags_ = 0;
return; return;
}
device->m_Device.destroy(m_View, nullptr); m_Device->m_Device.destroy(m_View, nullptr);
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation); vmaDestroyImage(m_Device->m_Allocator, Take(m_Image), m_Allocation);
m_Flags_ = 0; m_Flags_ = 0;
} }
@ -77,11 +74,11 @@ Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageF
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = imageCreateInfo.extent; m_Extent = imageCreateInfo.extent;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 1; m_LayerCount = 1;
m_MipLevels = mipLevels; m_MipLevels = mipLevels;
@ -109,7 +106,8 @@ Remember, we use upside down viewport.
void void
TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name) 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 Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide,
name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1; const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
@ -163,12 +161,12 @@ TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bo
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = extent; m_Extent = extent;
m_MipLevels = mipLevels; m_MipLevels = mipLevels;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 6; m_LayerCount = 6;
device->SetName(m_Image, name); device->SetName(m_Image, name);
@ -218,12 +216,12 @@ AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imag
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); 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 attachment image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = imageCreateInfo.extent; m_Extent = imageCreateInfo.extent;
m_MipLevels = 1; m_MipLevels = 1;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 1; m_LayerCount = 1;
device->SetName(m_Image, name); device->SetName(m_Image, name);
@ -274,12 +272,12 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create depth 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_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = imageCreateInfo.extent; m_Extent = imageCreateInfo.extent;
m_MipLevels = 1; m_MipLevels = 1;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 1; m_LayerCount = 1;
device->SetName(m_Image, name); device->SetName(m_Image, name);
@ -342,12 +340,12 @@ StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = imageCreateInfo.extent; m_Extent = imageCreateInfo.extent;
m_MipLevels = 1; m_MipLevels = 1;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 1; m_LayerCount = 1;
device->SetName(m_Image, name); device->SetName(m_Image, name);
@ -413,12 +411,12 @@ StorageTextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFor
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Device = device;
m_Image = image; m_Image = image;
m_View = view; m_View = view;
m_Allocation = allocation; m_Allocation = allocation;
m_Extent = imageCreateInfo.extent; m_Extent = imageCreateInfo.extent;
m_MipLevels = mipLevels; m_MipLevels = mipLevels;
m_Flags_ = OWNED_BIT | VALID_BIT;
m_LayerCount = 6; m_LayerCount = 6;
device->SetName(m_Image, name); device->SetName(m_Image, name);

View File

@ -0,0 +1,24 @@
// =============================================
// Aster: sampler.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "core/sampler.h"
#include "core/device.h"
void
Sampler::Destroy(const Device *device)
{
if (!IsValid())
return;
device->m_Device.destroy(Take(m_Sampler), nullptr);
}
void
Sampler::Init(const Device *device, const vk::SamplerCreateInfo &samplerCreateInfo, cstr name)
{
const auto result = device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_Sampler);
ERROR_IF(Failed(result), "Could not create a sampler {}", name ? name : "<unnamed>") THEN_ABORT(-1);
}

View File

@ -7,4 +7,5 @@ PRIVATE
"manager.cpp" "manager.cpp"
"buffer_manager.cpp" "buffer_manager.cpp"
"image_manager.cpp" "image_manager.cpp"
"sampler_manager.cpp"
"render_resource_manager.cpp") "render_resource_manager.cpp")

View File

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

View File

@ -7,10 +7,10 @@
#include "core/device.h" #include "core/device.h"
Manager<Image> *Manager<Image>::m_Instance = nullptr;
using namespace systems; using namespace systems;
Manager<Image> *Manager<Image>::m_Instance = nullptr;
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo); vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);
@ -61,18 +61,17 @@ ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result) ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result); THEN_ABORT(result);
auto [handle, object] = Alloc(); auto object = Alloc();
object->m_Image = image; object->m_Image = image;
object->m_View = view; object->m_View = view;
object->m_Allocation = allocation; object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent; object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers); object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels); object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name); m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle; return object;
} }
ImageHandle ImageHandle
@ -109,18 +108,17 @@ ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result) ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result); THEN_ABORT(result);
auto [handle, object] = Alloc(); auto object = Alloc();
object->m_Image = image; object->m_Image = image;
object->m_View = view; object->m_View = view;
object->m_Allocation = allocation; object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent; object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers); object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels); object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name); m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle; return object;
} }
ImageHandle ImageHandle
@ -157,18 +155,17 @@ ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result) ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result); THEN_ABORT(result);
auto [handle, object] = Alloc(); auto object = Alloc();
object->m_Image = image; object->m_Image = image;
object->m_View = view; object->m_View = view;
object->m_Allocation = allocation; object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent; object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers); object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels); object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name); m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle; return object;
} }
ImageHandle ImageHandle
@ -205,18 +202,17 @@ ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createI
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result) ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result); THEN_ABORT(result);
auto [handle, object] = Alloc(); auto object = Alloc();
object->m_Image = image; object->m_Image = image;
object->m_View = view; object->m_View = view;
object->m_Allocation = allocation; object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent; object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers); object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels); object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name); m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle; return object;
} }
vk::ImageCreateInfo vk::ImageCreateInfo

View File

@ -31,29 +31,19 @@
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false) } while (false)
using namespace systems; using namespace systems;
u32 RenderResourceManager *RenderResourceManager::m_Instance = nullptr;
GetHandleInternal(concepts::HandleType auto &handle)
{
return *Recast<u32 *>(&handle);
}
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Buffer> &handle) RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages,
: uBufferHandle(handle) const SamplerHandle &defaultSampler)
: m_Device{device}
, m_Buffers{maxBuffers}
, m_Images{maxImages}
, m_DefaultSampler{defaultSampler}
{ {
} assert(!m_Instance);
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 = { eastl::array poolSizes = {
vk::DescriptorPoolSize{ vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer, .type = vk::DescriptorType::eStorageBuffer,
@ -63,10 +53,10 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
.type = vk::DescriptorType::eCombinedImageSampler, .type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = maxImages, .descriptorCount = maxImages,
}, },
//vk::DescriptorPoolSize{ // vk::DescriptorPoolSize{
// .type = vk::DescriptorType::eStorageImage, // .type = vk::DescriptorType::eStorageImage,
// .descriptorCount = storageTexturesCount, // .descriptorCount = storageTexturesCount,
//}, // },
}; };
const vk::DescriptorPoolCreateInfo poolCreateInfo = { const vk::DescriptorPoolCreateInfo poolCreateInfo = {
@ -90,12 +80,12 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
.descriptorCount = Cast<u32>(maxImages), .descriptorCount = Cast<u32>(maxImages),
.stageFlags = vk::ShaderStageFlagBits::eAll, .stageFlags = vk::ShaderStageFlagBits::eAll,
}, },
//vk::DescriptorSetLayoutBinding{ // vk::DescriptorSetLayoutBinding{
// .binding = STORAGE_TEXTURE_BINDING_INDEX, // .binding = STORAGE_TEXTURE_BINDING_INDEX,
// .descriptorType = vk::DescriptorType::eStorageImage, // .descriptorType = vk::DescriptorType::eStorageImage,
// .descriptorCount = Cast<u32>(storageTexturesCount), // .descriptorCount = Cast<u32>(storageTexturesCount),
// .stageFlags = vk::ShaderStageFlagBits::eAll, // .stageFlags = vk::ShaderStageFlagBits::eAll,
//}, // },
}; };
vk::DescriptorBindingFlags bindingFlags = vk::DescriptorBindingFlags bindingFlags =
@ -131,52 +121,86 @@ RenderResourceManager::RenderResourceManager(const Device *device, u32 const max
device->SetName(m_SetLayout, "Bindless Layout"); device->SetName(m_SetLayout, "Bindless Layout");
device->SetName(m_DescriptorPool, "Bindless Pool"); device->SetName(m_DescriptorPool, "Bindless Pool");
device->SetName(m_DescriptorSet, "Bindless Set"); device->SetName(m_DescriptorSet, "Bindless Set");
m_Instance = this;
} }
void RenderResourceManager::~RenderResourceManager()
systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
{ {
using HandleType = decltype(handle)::Type; m_Device->m_Device.destroy(m_SetLayout, nullptr);
if constexpr (std::is_same_v<HandleType, Buffer>) m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
#if !defined(ASTER_NDEBUG)
u32 bufferCount = 0;
for (const auto &entry : m_Buffers.m_Data)
{ {
const Buffer *buffer = handle.Fetch(); bufferCount += entry.m_CommitCount;
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>) u32 imageCount = 0;
for (const auto &entry : m_Images.m_Data)
{ {
const Image *image = handle.Fetch(); imageCount += entry.m_CommitCount;
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");
} }
if (bufferCount > 0 || imageCount > 0)
{
WARN("Committed resources at destruction. Buffers: {}, Images: {}", bufferCount, imageCount);
}
#endif
}
m_WriteOwner.emplace_back(handle); CommitEntry<Buffer>
RenderResourceManager::Commit(const BufferHandle &buffer)
{
auto [commit, isNew] = m_Buffers.Create(buffer);
if (!isNew)
return commit;
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = buffer->m_Buffer,
.offset = 0,
.range = buffer->m_Size,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = commit.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
return commit;
}
CommitEntry<Image>
RenderResourceManager::Commit(const ImageHandle &handle)
{
return Commit(handle, m_DefaultSampler);
}
CommitEntry<Image>
RenderResourceManager::Commit(const ImageHandle &image, const SamplerHandle &sampler)
{
auto [commit, isNew] = m_Images.Create(image);
if (!isNew)
return commit;
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = sampler->m_Sampler,
.imageView = image->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = IMAGE_BINDING_INDEX,
.dstArrayElement = commit.m_Index,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
return commit;
} }
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info) RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
@ -193,3 +217,19 @@ RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info)
: uBufferView{info} : uBufferView{info}
{ {
} }
void
RenderResourceManager::Update()
{
// Descriptor Updates
if (!m_Writes.empty())
{
m_Device->m_Device.updateDescriptorSets(Cast<u32>(m_Writes.size()), m_Writes.data(), 0, nullptr);
m_Writes.clear();
m_WriteInfos.clear();
}
m_Buffers.Update();
m_Images.Update();
}

View File

@ -0,0 +1,66 @@
// =============================================
// Aster: sampler_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/sampler_manager.h"
#include "core/device.h"
using namespace systems;
Manager<Sampler> *Manager<Sampler>::m_Instance = nullptr;
usize
HashSamplerCreateInfo(const vk::SamplerCreateInfo &createInfo)
{
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;
}
SamplerManager::SamplerManager(const Device *device, const u32 maxCount, const u8 typeId)
: Manager{device, maxCount, typeId}
{
}
SamplerManager::~SamplerManager()
{
m_HashToSamplerIdx.clear();
}
SamplerHandle
SamplerManager::Create(const SamplerCreateInfo &createInfo)
{
const auto hash = HashSamplerCreateInfo(createInfo);
if (const auto iter = m_HashToSamplerIdx.find(hash); iter != m_HashToSamplerIdx.end())
{
return iter->second;
}
auto object = Alloc();
object->Init(m_Device, createInfo, createInfo.m_Name ? createInfo.m_Name : nullptr);
m_HashToSamplerIdx.emplace(hash, object);
return object;
}

View File

@ -2,4 +2,4 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
target_sources(aster_core PRIVATE "logger.cpp") target_sources(aster_core PRIVATE "logger.cpp" )

View File

@ -122,7 +122,7 @@ main(int, char **)
{ {
StagingBuffer staging; StagingBuffer staging;
staging.Init(&device, vertices.size() * sizeof vertices[0], "Staging"); staging.Init(&device, vertices.size() * sizeof vertices[0], "Staging");
staging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data()); staging.Write(0, vertices.size() * sizeof vertices[0], vertices.data());
vk::Fence fence; vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {}; vk::FenceCreateInfo fenceCreateInfo = {};
@ -133,7 +133,7 @@ main(int, char **)
result = copyBuffer.begin(&beginInfo); result = copyBuffer.begin(&beginInfo);
ERROR_IF(Failed(result), "Copy begin failed. Cause: {}", result) THEN_ABORT(result); ERROR_IF(Failed(result), "Copy begin failed. Cause: {}", result) THEN_ABORT(result);
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = staging.GetSize()}; vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = staging.m_Size};
copyBuffer.copyBuffer(staging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy); copyBuffer.copyBuffer(staging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy);
result = copyBuffer.end(); result = copyBuffer.end();
@ -154,7 +154,7 @@ main(int, char **)
ERROR_IF(Failed(result), "Couldn't reset command pool. Cause: {}", result) THEN_ABORT(result); ERROR_IF(Failed(result), "Couldn't reset command pool. Cause: {}", result) THEN_ABORT(result);
device.m_Device.destroy(fence, nullptr); device.m_Device.destroy(fence, nullptr);
staging.Destroy(&device); staging.Destroy();
} }
// Persistent variables // Persistent variables
@ -352,7 +352,7 @@ main(int, char **)
device.WaitIdle(); device.WaitIdle();
device.m_Device.destroy(copyPool, nullptr); device.m_Device.destroy(copyPool, nullptr);
vbo.Destroy(&device); vbo.Destroy();
return 0; return 0;
} }

View File

@ -20,6 +20,7 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "aster/systems/buffer_manager.h" #include "aster/systems/buffer_manager.h"
#include "aster/systems/image_manager.h" #include "aster/systems/image_manager.h"
#include "aster/systems/render_resource_manager.h"
#include "frame.h" #include "frame.h"
#include "stb_image.h" #include "stb_image.h"
@ -73,7 +74,7 @@ ImageFile::~ImageFile()
} }
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain); Pipeline CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain);
struct Vertex struct Vertex
{ {
@ -106,17 +107,35 @@ main(int, char **)
Features enabledDeviceFeatures = { Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true}, .m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan12Features = {.bufferDeviceAddress = true}, .m_Vulkan12Features =
{
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.shaderStorageBufferArrayNonUniformIndexing = true,
.shaderStorageImageArrayNonUniformIndexing = true,
.descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true,
.bufferDeviceAddress = true,
.bufferDeviceAddressCaptureReplay = true,
},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}, .m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
}; };
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
Pipeline pipeline = CreatePipeline(&device, &swapchain);
systems::BufferManager bufferManager{&device, 12, 0}; systems::BufferManager bufferManager{&device, 12, 0};
systems::ImageManager imageManager{&device, 12, 1}; systems::ImageManager imageManager{&device, 12, 1};
systems::SamplerManager samplerManager{&device, 1, 0xF};
systems::RenderResourceManager renderResourceManager{&device, 12, 12, samplerManager.Create({})};
Pipeline pipeline = CreatePipeline(&renderResourceManager, &swapchain);
Camera camera = { Camera camera = {
.m_Model = {1.0f}, .m_Model = {1.0f},
@ -125,36 +144,6 @@ main(int, char **)
70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f), 70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f),
}; };
vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet;
{
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts.front();
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.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()};
AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool));
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSetLayout,
};
AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &descriptorSet));
}
vk::CommandPool copyPool; vk::CommandPool copyPool;
vk::CommandBuffer copyBuffer; vk::CommandBuffer copyBuffer;
{ {
@ -222,21 +211,21 @@ main(int, char **)
assert(loaded); assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); 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 vbo = bufferManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer");
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
auto crate = imageManager auto crate = imageManager
.CreateTexture2D({ .CreateTexture2D({
.m_Format = vk::Format::eR8G8B8A8Srgb, .m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height}, .m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture", .m_Name = "Crate Texture",
}) });
.ToPointer();
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
{ {
StagingBuffer imageStaging; StagingBuffer imageStaging;
imageStaging.Init(&device, imageFile.GetSize(), "Image Staging"); imageStaging.Init(&device, imageFile.GetSize(), "Image Staging");
imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data); imageStaging.Write(0, imageFile.GetSize(), imageFile.m_Data);
vk::ImageMemoryBarrier2 imageReadyToWrite = { vk::ImageMemoryBarrier2 imageReadyToWrite = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
@ -330,73 +319,11 @@ main(int, char **)
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool."); AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr); device.m_Device.destroy(fence, nullptr);
imageStaging.Destroy(&device); imageStaging.Destroy();
} }
vk::Sampler sampler; auto ubo = bufferManager.CreateStorageBuffer(sizeof camera, "Camera UBO");
{ ubo->Write(0, sizeof camera, &camera);
vk::SamplerCreateInfo samplerCreateInfo = {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.2f,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.minLod = 0,
.maxLod = 4,
.unnormalizedCoordinates = false,
};
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
}
auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer();
ubo->Write(&device, 0, sizeof camera, &camera);
vk::DescriptorBufferInfo descriptorBufferInfo = {
.buffer = ubo->m_Buffer,
.offset = 0,
.range = ubo->GetSize(),
};
vk::DescriptorImageInfo descriptorImageInfo = {
.sampler = sampler,
.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,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &descriptorBufferInfo,
},
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.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);
// Persistent variables // Persistent variables
vk::Viewport viewport = { vk::Viewport viewport = {
@ -462,13 +389,13 @@ main(int, char **)
}; };
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages; eastl::fixed_vector<systems::ImageHandle, MAX_FRAMES_IN_FLIGHT> depthImages;
auto initDepthImages = [&imageManager, &depthImages, &frameManager] (const vk::Extent2D extent) { auto initDepthImages = [&depthImages, &frameManager, &imageManager](const vk::Extent2D extent) {
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
{ {
depthImages.push_back( depthImages.push_back(
imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer()); imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}));
} }
}; };
@ -480,15 +407,29 @@ main(int, char **)
}; };
swapchain.RegisterResizeCallback(recreateDepthBuffers); swapchain.RegisterResizeCallback(recreateDepthBuffers);
struct PCB
{
systems::CommitEntry<Buffer> m_Camera;
systems::CommitEntry<Buffer> m_VertexBuffer;
systems::CommitEntry<Image> m_Texture;
};
PCB pcb = {
.m_Camera = renderResourceManager.Commit(ubo),
.m_VertexBuffer = renderResourceManager.Commit(vbo),
.m_Texture = renderResourceManager.Commit(crate),
};
Time::Init(); Time::Init();
INFO("Starting loop"); INFO("Starting loop");
while (window.Poll()) while (window.Poll())
{ {
Time::Update(); Time::Update();
renderResourceManager.Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo->Write(&device, 0, sizeof camera, &camera); ubo->Write(0, sizeof camera, &camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
@ -540,7 +481,9 @@ main(int, char **)
cmd.setViewport(0, 1, &viewport); cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor); cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&renderResourceManager.GetDescriptorSet(), 0, nullptr);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb);
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0); cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
cmd.endRendering(); cmd.endRendering();
@ -565,17 +508,16 @@ main(int, char **)
} }
device.WaitIdle(); device.WaitIdle();
device.m_Device.destroy(sampler, nullptr);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr); device.m_Device.destroy(copyPool, nullptr);
return 0; return 0;
} }
Pipeline Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain) CreatePipeline(const systems::RenderResourceManager *resourceManager, const Swapchain *swapchain)
{ {
// Pipeline Setup // Pipeline Setup
auto *device = resourceManager->m_Device;
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE); auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE); auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
@ -592,39 +534,18 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
}, },
}}; }};
eastl::array descriptorSetLayoutBinding = { auto descriptorSetLayout = resourceManager->GetDescriptorSetLayout();
vk::DescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
vk::DescriptorSetLayoutBinding{
.binding = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.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()),
.pBindings = descriptorSetLayoutBinding.data(),
};
vk::DescriptorSetLayout descriptorSetLayout;
AbortIfFailed(
device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout));
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eAllGraphics,
.offset = 0,
.size = 12,
};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = 1, .setLayoutCount = 1,
.pSetLayouts = &descriptorSetLayout, .pSetLayouts = &descriptorSetLayout,
.pushConstantRangeCount = 0, .pushConstantRangeCount = 1,
.pPushConstantRanges = nullptr, .pPushConstantRanges = &pcr,
}; };
vk::PipelineLayout pipelineLayout; vk::PipelineLayout pipelineLayout;
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
@ -714,7 +635,7 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
device->m_Device.destroy(vertexShaderModule, nullptr); device->m_Device.destroy(vertexShaderModule, nullptr);
device->m_Device.destroy(fragmentShaderModule, nullptr); device->m_Device.destroy(fragmentShaderModule, nullptr);
return {device, pipelineLayout, pipeline, {descriptorSetLayout}}; return {device, pipelineLayout, pipeline, {}};
} }
vk::ShaderModule vk::ShaderModule

View File

@ -0,0 +1,31 @@
struct VertexData
{
float4 Position;
float2 TexCoord0;
float2 _pad0;
};
struct CameraData
{
float4x4 Model;
float4x4 View;
float4x4 Projection;
};
struct Handle
{
uint value;
uint GetIndex()
{
return value & 0x0FFFFFFF;
}
};
[[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<CameraData> CameraDataBuffer[];
[[vk::binding(1, 0)]] Texture2D<float4> Textures[];
[[vk::binding(1, 0)]] SamplerState Samplers[];

View File

@ -1,3 +1,5 @@
#include "bindless.hlsli"
struct FS_Input { struct FS_Input {
float2 UV0 : TEXCOORD0; float2 UV0 : TEXCOORD0;
}; };
@ -7,13 +9,19 @@ struct FS_Output
float4 ColorTarget : SV_Target0; float4 ColorTarget : SV_Target0;
}; };
[[vk::binding(1, 0)]] Texture2D<float4> Texture; struct PCB
[[vk::binding(1, 0)]] SamplerState Sampler; {
Handle CameraBuffer;
Handle VertexBuffer;
Handle Texture;
};
[[vk::push_constant]] PCB Block;
FS_Output main(FS_Input StageInput) { FS_Output main(FS_Input StageInput) {
FS_Output output; FS_Output output;
output.ColorTarget = float4(Texture.Sample(Sampler, StageInput.UV0).rgb, 1.0); output.ColorTarget = float4(Textures[Block.Texture.GetIndex()].Sample(Samplers[Block.Texture.GetIndex()], StageInput.UV0).rgb, 1.0);
return output; return output;
} }

View File

@ -1,3 +1,5 @@
#include "bindless.hlsli"
struct VS_Input struct VS_Input
{ {
uint VertexIndex : SV_VertexID; uint VertexIndex : SV_VertexID;
@ -9,26 +11,23 @@ struct VS_Output
float4 VertexPosition : SV_Position; float4 VertexPosition : SV_Position;
}; };
struct CameraData { struct PCB
float4x4 Model; {
float4x4 View; Handle CameraBuffer;
float4x4 Projection; Handle VertexBuffer;
Handle Texture;
}; };
struct VertexData { [[vk::push_constant]] PCB Block;
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 main(VS_Input StageInput) {
VS_Output output; VS_Output output;
output.UV0 = Vertices[StageInput.VertexIndex].UV0; CameraData Camera = CameraDataBuffer[Block.CameraBuffer.GetIndex()][0];
float4 position = Vertices[StageInput.VertexIndex].Position; output.UV0 = VertexDataBuffer[Block.VertexBuffer.GetIndex()][StageInput.VertexIndex].TexCoord0;
float4 position = VertexDataBuffer[Block.VertexBuffer.GetIndex()][StageInput.VertexIndex].Position;
output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position))); output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position)));

View File

@ -5,5 +5,5 @@ cmake_minimum_required(VERSION 3.13)
add_subdirectory("00_util") add_subdirectory("00_util")
add_subdirectory("01_triangle") add_subdirectory("01_triangle")
add_subdirectory("02_box") add_subdirectory("02_box")
add_subdirectory("03_model_render") # add_subdirectory("03_model_render")
add_subdirectory("04_scenes") # add_subdirectory("04_scenes")