6#include <unordered_map>
13#include "core/memory/slot_allocator.h"
14#include "core/memory/sparse_set.h"
15#include "core/memory/types.h"
16#include "core/memory/type_registry.h"
17#include "resource/asset_ref.h"
18#include "resource/asset_type.h"
19#include "resource/resource.h"
20#include "resource/resource_handle.h"
22namespace Vkm::Engine {
34inline constexpr bool IS_ENGINE_ASSET = ASSET_TYPE<T> != AssetType::Count || std::is_same_v<T, FontAsset>;
46class ResourceManager {
49 ~ResourceManager() =
default;
51 ResourceManager(
const ResourceManager& other) =
delete;
52 ResourceManager& operator=(
const ResourceManager& other) =
delete;
54 ResourceManager(ResourceManager && other) =
delete;
55 ResourceManager& operator=(ResourceManager && other) =
delete;
71 template<
typename ResourceType>
72 auto add(ResourceType && resource) {
73 using T = std::remove_cv_t<std::remove_reference_t<ResourceType>>;
75 std::is_base_of_v<Resource, T>,
76 "ResourceManager stores only types deriving from Resource."
78 auto& slot = getSlot<T>();
80 if (resource.m_name.empty()) ensureUniqueName(slot, resource.m_name);
82 if (
const auto taken = slot.nameIndex.find(resource.m_name); taken != slot.nameIndex.end()) {
83 return replaceAt<T>(slot, taken->second, std::forward<ResourceType>(resource));
87 std::string indexName = resource.m_name;
90 Resource& stored = storageOf<T>(slot).add(key.
index, std::forward<ResourceType>(resource));
91 stored.m_uid = mintUid();
93 slot.nameIndex.emplace(std::move(indexName), key.
index);
106 template<
typename ResourceType>
107 auto add(ResourceType && resource, std::string name) {
108 resource.m_name = std::move(name);
109 return add(std::forward<ResourceType>(resource));
120 template<
typename ResourceType>
121 auto addPrivate(ResourceType && resource, std::string name) {
122 resource.m_hidden =
true;
123 resource.m_name = std::move(name);
124 return add(std::forward<ResourceType>(resource));
137 template<
typename HandleType>
139 using T =
typename HandleType::resource_t;
140 auto& slot = getSlot<T>();
141 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::remove invalid handle");
142 if (!slot.allocator.has(handle.key))
return;
144 const T& res = storageOfConst<T>(slot).get(handle.key.index);
145 dropNameIndex(slot, res.m_name, handle.key.index);
146 storageOf<T>(slot).remove(handle.key.index);
147 slot.allocator.free(handle.key);
159 template<
typename HandleType>
160 bool isAlive(
const HandleType& handle)
const {
161 using T =
typename HandleType::resource_t;
162 const TypedSlot* slot = trySlot<T>();
163 return slot && slot->allocator.
has(handle.key);
173 template<
typename HandleType>
174 const auto&
get(
const HandleType& handle)
const {
175 using T =
typename HandleType::resource_t;
176 const auto& slot = getSlotConst<T>();
177 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::get invalid handle");
178 return storageOfConst<T>(slot).get(handle.key.index);
191 template<
typename HandleType>
192 const auto*
tryGet(
const HandleType& handle)
const {
193 using T =
typename HandleType::resource_t;
194 const TypedSlot* slot = trySlot<T>();
195 if (!slot || !slot->allocator.
has(handle.key))
return static_cast<const T*
>(
nullptr);
196 return &storageOfConst<T>(*slot).get(handle.key.index);
208 template<
typename HandleType>
210 using T =
typename HandleType::resource_t;
211 TypedSlot* slot = m_slots.find<T>();
212 if (!slot || !slot->allocator.
has(handle.key))
return static_cast<T*
>(
nullptr);
213 return &storageOf<T>(*slot).get(handle.key.index);
226 template<
typename HandleType>
227 auto&
edit(
const HandleType& handle) {
228 using T =
typename HandleType::resource_t;
229 auto& slot = getSlot<T>();
230 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::edit invalid handle");
231 return storageOf<T>(slot).get(handle.key.index);
244 template<
typename HandleType>
245 void rename(
const HandleType& handle, std::string newName) {
246 using T =
typename HandleType::resource_t;
247 auto& slot = getSlot<T>();
248 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::rename invalid handle");
249 auto& res = storageOf<T>(slot).get(handle.key.index);
251 dropNameIndex(slot, res.m_name, handle.key.index);
252 ensureUniqueName(slot, newName);
253 res.m_name = std::move(newName);
254 slot.nameIndex[res.m_name] = handle.key.index;
270 template<
typename HandleType>
271 void swapValue(
const HandleType& handle,
typename HandleType::resource_t& value) {
272 using T =
typename HandleType::resource_t;
274 std::is_base_of_v<Resource, T>,
275 "Resource type must inherit from Resource to use swapValue()."
277 auto& slot = getSlot<T>();
278 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::swapValue invalid handle");
279 T& target = storageOf<T>(slot).get(handle.key.index);
280 if (&target == &value)
return;
284 swap(target.m_version, value.m_version);
285 swap(target.m_uid, value.m_uid);
286 swap(target.m_hidden, value.m_hidden);
287 swap(target.m_name, value.m_name);
297 template<
typename HandleType>
299 using T =
typename HandleType::resource_t;
301 std::is_base_of_v<Resource, T>,
302 "Resource type must inherit from Resource to use commit()."
304 auto& slot = getSlot<T>();
305 VKM_ASSERT(slot.allocator.has(handle.key),
"ResourceManager::commit invalid handle");
306 ++storageOf<T>(slot).get(handle.key.index).m_version;
318 const TypedSlot* slot = trySlot<T>();
319 if (!slot)
return {};
321 auto it = slot->nameIndex.find(name);
322 if (it == slot->nameIndex.end())
return {};
351 template<
typename T,
typename Fn>
353 const TypedSlot* slot = trySlot<T>();
355 storageOfConst<T>(*slot).forEach([&](uint32_t index,
const T& res) {
356 fn(Handle<T>{slot->allocator.handleAt(index)}, res);
367 const TypedSlot* slot = trySlot<T>();
368 return slot ? storageOfConst<T>(*slot).size() : 0;
389 void swap(ResourceManager& other)
noexcept;
399 uint64_t
epoch()
const {
return m_epoch; }
404 std::unique_ptr<ISparseSet> storage;
406 std::unordered_map<std::string, uint32_t> nameIndex;
413 void makeEngineSlots();
416 TypedSlot& getSlot() {
419 "ResourceManager stores the engine's asset types only: a slot for a type a "
420 "gameplay module declares would carry the module's code past its unload."
422 return m_slots.ensure<T>([] {
423 auto slot = std::make_unique<TypedSlot>();
424 slot->storage = std::make_unique<SparseSet<T>>();
436 const TypedSlot* trySlot()
const {
437 return m_slots.find<T>();
441 const TypedSlot& getSlotConst()
const {
442 const TypedSlot* slot = trySlot<T>();
443 VKM_ASSERT(slot,
"ResourceManager: type not registered");
453 static const SparseSet<T>& storageOfConst(
const TypedSlot& slot) {
464 static void dropNameIndex(TypedSlot& slot,
const std::string& name, uint32_t index) {
465 auto it = slot.nameIndex.find(name);
466 if (it != slot.nameIndex.end() && it->second == index) {
467 slot.nameIndex.erase(it);
485 template <
typename T,
typename ResourceType>
486 Handle<T> replaceAt(TypedSlot& slot, uint32_t index, ResourceType && resource) {
487 T& target = storageOf<T>(slot).get(index);
491 const uint64_t version = target.m_version;
492 std::string name = std::move(target.m_name);
495 T incoming(std::forward<ResourceType>(resource));
497 swap(target, incoming);
499 target.m_name = std::move(name);
500 target.m_uid = mintUid();
501 target.m_version = version + 1;
502 return Handle<T>{slot.allocator.handleAt(index)};
514 static void ensureUniqueName(
const TypedSlot& slot, std::string& name) {
515 if (name.empty()) name =
"asset";
516 if (slot.nameIndex.find(name) == slot.nameIndex.end())
return;
517 const std::string base = name;
518 for (
int n = 2; ; ++n) {
519 std::string candidate = base +
" (" + std::to_string(n) +
")";
520 if (slot.nameIndex.find(candidate) == slot.nameIndex.end()) {
521 name = std::move(candidate);
535 static uint64_t mintUid();
541 uint64_t m_epoch = 1;
size_t countOfType() const
How many resources of type T the graph holds.
Definition resource_manager.h:366
Handle< T > find(const AssetRef< T > &ref) const
Resolve an authored reference to the asset it names.
Definition resource_manager.h:338
bool isAlive(const HandleType &handle) const
Whether handle still names a live resource of its type.
Definition resource_manager.h:160
void swapValue(const HandleType &handle, typename HandleType::resource_t &value)
Exchange the contents of the asset at handle with value, each keeping its identity,...
Definition resource_manager.h:271
const auto & get(const HandleType &handle) const
The resource handle names; asserts it is live (see isAlive(), tryGet()).
Definition resource_manager.h:174
void clear()
Drop every resource but the fonts, by swap() with an empty manager.
void rename(const HandleType &handle, std::string newName)
Rename a resource, keeping findByName consistent.
Definition resource_manager.h:245
auto * tryEdit(const HandleType &handle)
The asset handle names for editing, or null when it names none.
Definition resource_manager.h:209
void swap(ResourceManager &other) noexcept
Swap the asset graph with another ResourceManager, fonts excepted.
auto & edit(const HandleType &handle)
Mutable access to the resource handle names.
Definition resource_manager.h:227
const auto * tryGet(const HandleType &handle) const
The asset handle names, or null when it names none.
Definition resource_manager.h:192
auto add(ResourceType &&resource, std::string name)
add() under name.
Definition resource_manager.h:107
uint64_t epoch() const
Identity of the current asset graph, bumped by every swap.
Definition resource_manager.h:399
Handle< T > findByName(const std::string &name) const
Find a resource by the name it was added or renamed under, in O(1).
Definition resource_manager.h:317
void forEachOfType(Fn &&fn) const
Visit every live resource of type T; a linear scan for tooling, not hot paths.
Definition resource_manager.h:352
void remove(const HandleType &handle)
Remove a resource: its name mapping, storage and handle slot.
Definition resource_manager.h:138
void commit(const HandleType &handle)
Bump the asset's version, which GLView::sync keys its GPU re-upload on.
Definition resource_manager.h:298
auto addPrivate(ResourceType &&resource, std::string name)
add() under name, setting Resource::isHidden: kept from the user and scene saves.
Definition resource_manager.h:121
auto add(ResourceType &&resource)
Put resource in the graph under its name, stamping its identity.
Definition resource_manager.h:72
Base class for every asset type, carrying the identity the ResourceManager indexes by.
Definition resource.h:17
Lightweight slot allocator that issues generation-safe handles.
Definition slot_allocator.h:18
StorageIndex handleAt(uint32_t index) const
Rebuild the handle naming a sparse slot, generation included.
Definition slot_allocator.h:93
bool has(StorageIndex id) const
Test whether a handle is still valid.
Definition slot_allocator.h:78
Dense-packed storage indexed by external uint32_t keys.
Definition sparse_set.h:50
One slot per type, indexed by typeId<T>() and filled on first use.
Definition type_registry.h:21
An authored reference to an asset, held as the name it is filed under.
Definition asset_ref.h:23
std::string name
Asset name; empty for none.
Definition asset_ref.h:26
A TrueType font baked to a signed-distance-field atlas.
Definition font_asset.h:48
Opaque, type-safe handle for resources, distinguished by ResourceType.
Definition resource_handle.h:16
Opaque handle pairing a sparse-array index with a generation counter.
Definition types.h:14
uint32_t index
Sparse slot index; 0 is null.
Definition types.h:15