360 lines
8.5 KiB
C++
360 lines
8.5 KiB
C++
// =============================================
|
|
// 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 "sampler_manager.h"
|
|
|
|
#include "aster/util/intrusive_slist.h"
|
|
|
|
#include "EASTL/deque.h"
|
|
#include "EASTL/intrusive_hash_map.h"
|
|
#include "EASTL/bonus/fixed_ring_buffer.h"
|
|
|
|
#include "resource.h"
|
|
|
|
namespace systems
|
|
{
|
|
|
|
class CommitManager
|
|
{
|
|
private:
|
|
template <concepts::Manageable T>
|
|
struct HandleMapper
|
|
{
|
|
using Type = T;
|
|
using Manager = Manager<Type>;
|
|
using Handle = typename Manager::Handle;
|
|
using Resource = ResId<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<Resource, 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 {Resource{i}, false};
|
|
}
|
|
|
|
Entry &data = m_FreeList.Pop();
|
|
|
|
data.mKey = object;
|
|
data.m_CommitCount = 1;
|
|
|
|
m_InUse.insert(data);
|
|
|
|
auto i = GetIndex(data);
|
|
|
|
return {Resource{i}, true};
|
|
}
|
|
|
|
Handle
|
|
GetHandle(const Resource &res)
|
|
{
|
|
return m_Data[res.m_Index].mKey;
|
|
}
|
|
|
|
void
|
|
AddRef(const Resource &commit)
|
|
{
|
|
m_Data.at(commit.m_Index).AddRef();
|
|
}
|
|
|
|
void
|
|
Release(const Resource &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 {
|
|
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;
|
|
|
|
// using WriteOwner = std::variant<Handle<Buffer>, Handle<Image>>;
|
|
|
|
public:
|
|
const Device *m_Device;
|
|
|
|
CommitManager(const Device *device, u32 maxBuffers, u32 maxImages, u32 maxStorageImages,
|
|
Ref<Sampler> defaultSampler);
|
|
~CommitManager();
|
|
|
|
PIN_MEMORY(CommitManager);
|
|
|
|
// Commit Buffer
|
|
ResId<Buffer> CommitBuffer(const Ref<Buffer> &buffer);
|
|
|
|
// Commit Storage Images
|
|
ResId<StorageImage> CommitStorageImage(const concepts::StorageImageRef auto &image)
|
|
{
|
|
return CommitStorageImage(CastImage<StorageImage>(image));
|
|
}
|
|
ResId<StorageImage> CommitStorageImage(const Ref<StorageImage> &image);
|
|
|
|
// Sampled Images
|
|
ResId<Texture>
|
|
CommitTexture(const concepts::SampledImageRef auto &image, const Ref<Sampler> &sampler)
|
|
{
|
|
return CommitTexture(CastImage<Texture>(image), sampler);
|
|
}
|
|
|
|
ResId<Texture>
|
|
CommitTexture(const concepts::SampledImageRef auto &image)
|
|
{
|
|
return CommitTexture(CastImage<Texture>(image));
|
|
}
|
|
|
|
ResId<Texture> CommitTexture(const Ref<Texture> &handle);
|
|
ResId<Texture> CommitTexture(const Ref<Texture> &image, const Ref<Sampler> &sampler);
|
|
|
|
void Update();
|
|
|
|
Ref<Buffer>
|
|
FetchHandle(const ResId<Buffer> &id)
|
|
{
|
|
return m_Buffers.GetHandle(id);
|
|
}
|
|
|
|
Ref<Texture>
|
|
FetchHandle(const ResId<Texture> &id)
|
|
{
|
|
return m_Images.GetHandle(id);
|
|
}
|
|
|
|
Ref<StorageImage>
|
|
FetchHandle(const ResId<StorageImage> &id)
|
|
{
|
|
return m_StorageImages.GetHandle(id);
|
|
}
|
|
|
|
[[nodiscard]] const vk::DescriptorSetLayout &
|
|
GetDescriptorSetLayout() const
|
|
{
|
|
return m_SetLayout;
|
|
}
|
|
|
|
[[nodiscard]] const vk::DescriptorSet &
|
|
GetDescriptorSet() const
|
|
{
|
|
return m_DescriptorSet;
|
|
}
|
|
|
|
static CommitManager &Instance()
|
|
{
|
|
assert(m_Instance);
|
|
return *m_Instance;
|
|
}
|
|
|
|
private:
|
|
vk::DescriptorPool m_DescriptorPool;
|
|
vk::DescriptorSetLayout m_SetLayout;
|
|
vk::DescriptorSet m_DescriptorSet;
|
|
|
|
constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
|
|
constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
|
|
constexpr static u8 STORAGE_IMAGE_BINDING_INDEX = 0x2;
|
|
|
|
HandleMapper<Buffer> m_Buffers;
|
|
HandleMapper<Texture> m_Images;
|
|
HandleMapper<StorageImage> m_StorageImages;
|
|
|
|
Ref<Sampler> m_DefaultSampler;
|
|
|
|
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
|
eastl::deque<WriteInfo> m_WriteInfos;
|
|
// eastl::vector<WriteOwner> m_WriteOwner;
|
|
|
|
static CommitManager *m_Instance;
|
|
friend ResId<Buffer>;
|
|
friend ResId<Texture>;
|
|
friend ResId<StorageImage>;
|
|
|
|
void
|
|
AddRef(const ResId<Buffer> &handle)
|
|
{
|
|
m_Buffers.AddRef(handle);
|
|
}
|
|
void
|
|
AddRef(const ResId<Texture> &handle)
|
|
{
|
|
m_Images.AddRef(handle);
|
|
}
|
|
void
|
|
AddRef(const ResId<StorageImage> &handle)
|
|
{
|
|
m_StorageImages.AddRef(handle);
|
|
}
|
|
|
|
void
|
|
Release(const ResId<Buffer> &handle)
|
|
{
|
|
m_Buffers.Release(handle);
|
|
}
|
|
void
|
|
Release(const ResId<Texture> &handle)
|
|
{
|
|
m_Images.Release(handle);
|
|
}
|
|
void
|
|
Release(const ResId<StorageImage> &handle)
|
|
{
|
|
m_StorageImages.Release(handle);
|
|
}
|
|
};
|
|
|
|
template <concepts::Manageable T>
|
|
void
|
|
ResId<T>::AddRef() const
|
|
{
|
|
CommitManager::Instance().AddRef(*this);
|
|
}
|
|
|
|
template <concepts::Manageable T>
|
|
void
|
|
ResId<T>::Release() const
|
|
{
|
|
CommitManager::Instance().Release(*this);
|
|
}
|
|
|
|
} // namespace systems
|