AssetContainer

This commit is contained in:
2026-05-16 16:47:01 +02:00
parent 2c83dcc0bc
commit 87d59a00c1
8 changed files with 199 additions and 203 deletions

View File

@@ -24,13 +24,19 @@ class Asset
public:
using FLAT_ASSET = typename ASSET::FLAT;
Asset()
Asset():
m_header(::Flat::Bigfoot::AssetHeaderT {.uuid = UUID {},
.name = eastl::string {},
.type_id = TypeID(),
.type_name = eastl::string {TypeName()},
.version = 0}),
m_hardRefCount(0),
m_softRefCount(0)
{
m_header.type_id = GetTypeID();
m_header.type_name = GetTypeName();
}
explicit Asset(const eastl::span<const std::byte> p_flatbuffer)
explicit Asset(const eastl::span<const std::byte> p_flatbuffer):
Asset()
{
#ifdef BIGFOOT_NOT_OPTIMIZED
flatbuffers::Verifier verifier {std::bit_cast<std::uint8_t*>(p_flatbuffer.data()), p_flatbuffer.size()};
@@ -56,7 +62,7 @@ class Asset
~Asset() = default;
[[nodiscard]]
const ::Flat::Bigfoot::AssetHeaderT& GetHeader() const
const ::Flat::Bigfoot::AssetHeaderT& Header() const
{
return m_header;
}
@@ -102,7 +108,7 @@ class Asset
}
[[nodiscard]]
static std::uint64_t GetTypeID()
static std::uint64_t TypeID()
{
static const std::uint64_t typeID = rapidhash(FLAT_ASSET::GetFullyQualifiedName(),
std::strlen(FLAT_ASSET::GetFullyQualifiedName()));
@@ -110,7 +116,7 @@ class Asset
}
[[nodiscard]]
static eastl::string_view GetTypeName()
static eastl::string_view TypeName()
{
return FLAT_ASSET::GetFullyQualifiedName();
}
@@ -121,6 +127,9 @@ class Asset
private:
::Flat::Bigfoot::AssetHeaderT m_header;
typename FLAT_ASSET::NativeTableType m_asset;
std::uint32_t m_hardRefCount;
std::uint32_t m_softRefCount;
};
} // namespace Bigfoot

View File

@@ -6,9 +6,10 @@
*********************************************************************/
#ifndef BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
#define BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
#include <Engine/Asset/Asset.hpp>
#include <Engine/EngineAssertHandler.hpp>
#include <System/UUID/UUID.hpp>
#include <Utils/Containers/SlotMap.hpp>
#include <ankerl/unordered_dense.h>
@@ -20,86 +21,82 @@ template<class ASSET>
class AssetContainer
{
public:
AssetContainer()
AssetContainer() = default;
AssetContainer(const AssetContainer& p_container) = default;
AssetContainer(AssetContainer&& p_container) = default;
~AssetContainer() = default;
template<class... ARGS>
[[nodiscard]]
typename SlotMap<ASSET>::SlotKey Insert(ARGS&&... p_args)
{
CRITICAL_ASSERT(EngineAssertHandler, !ms_instance, "Asset container already exists!");
ms_instance = this;
const typename SlotMap<ASSET>::SlotKey slotKey = m_assets.Insert(std::forward<ARGS>(p_args)...);
m_uuidToSlotKey.insert({m_assets.Get(slotKey)->Header().uuid, slotKey});
return slotKey;
}
AssetContainer(const AssetContainer&) = delete;
AssetContainer& operator=(const AssetContainer&) = delete;
~AssetContainer()
[[nodiscard]]
ASSET* Get(const typename SlotMap<ASSET>::SlotKey p_key)
{
ms_instance = nullptr;
return m_assets.Get(p_key);
}
struct Entry
[[nodiscard]]
const ASSET* Get(const typename SlotMap<ASSET>::SlotKey p_key) const
{
std::uint32_t m_hardRefCount = 0;
std::uint32_t m_softRefCount = 0;
};
void IncrementHardRefCount(const UUID& p_uuid)
{
++m_entries[p_uuid].m_hardRefCount;
return m_assets.Get(p_key);
}
void DecrementHardRefCount(const UUID& p_uuid)
[[nodiscard]]
typename SlotMap<ASSET>::SlotKey SlotKey(const UUID& p_uuid) const
{
if (const auto entry = m_entries.find(p_uuid); entry != m_entries.end())
if (const auto it = m_uuidToSlotKey.find(p_uuid); it != m_uuidToSlotKey.end())
{
CRITICAL_ASSERT(
EngineAssertHandler,
entry->second.m_hardRefCount > 0,
"Double release detected! Decrementing a hard reference to an asset that already does not have any "
"more hardrefs");
--entry->second.m_hardRefCount;
return it->second;
}
return typename SlotMap<ASSET>::SlotKey {};
}
[[nodiscard]]
bool Has(const typename SlotMap<ASSET>::SlotKey p_key) const
{
return m_assets.Has(p_key);
}
[[nodiscard]]
bool Has(const UUID& p_uuid) const
{
return m_uuidToSlotKey.contains(p_uuid);
}
void Remove(const typename SlotMap<ASSET>::SlotKey p_key)
{
if (const ASSET* asset = Get(p_key))
{
const UUID uuid = asset->Header().uuid;
m_assets.Remove(p_key);
m_uuidToSlotKey.erase(uuid);
}
}
void IncrementSoftRefCount(const UUID& p_uuid)
void Remove(const UUID& p_uuid)
{
++m_entries[p_uuid].m_softRefCount;
}
void DecrementSoftRefCount(const UUID& p_uuid)
{
if (const auto entry = m_entries.find(p_uuid); entry != m_entries.end())
if (const auto it = m_uuidToSlotKey.find(p_uuid); it != m_uuidToSlotKey.end())
{
CRITICAL_ASSERT(
EngineAssertHandler,
entry->second.m_softRefCount > 0,
"Double release detected! Decrementing a soft reference to an asset that already does not have any "
"more softrefs");
--entry->second.m_softRefCount;
Remove(it->second);
}
}
[[nodiscard]]
std::uint32_t HardRefCount(const UUID& p_uuid) const
{
const auto entry = m_entries.find(p_uuid);
return entry != m_entries.end() ? entry->second.m_hardRefCount : 0;
}
[[nodiscard]]
std::uint32_t SoftRefCount(const UUID& p_uuid) const
{
const auto entry = m_entries.find(p_uuid);
return entry != m_entries.end() ? entry->second.m_softRefCount : 0;
}
[[nodiscard]]
static AssetContainer& Get()
{
return *ms_instance;
}
AssetContainer& operator=(const AssetContainer& p_container) = default;
AssetContainer& operator=(AssetContainer&& p_container) = default;
private:
ankerl::unordered_dense::segmented_map<UUID, Entry> m_entries;
ankerl::unordered_dense::segmented_map<UUID, typename SlotMap<ASSET>::SlotKey> m_uuidToSlotKey;
inline static constinit AssetContainer* ms_instance = nullptr;
SlotMap<ASSET> m_assets;
};
} // namespace Bigfoot

View File

@@ -24,19 +24,11 @@
Bigfoot::HardReference<AssetType> UnPackHardReference##AssetName(const Flat::Bigfoot::HardReference& p_hardRef); \
}
#define ASSET_CONTAINER(AssetName, AssetType) \
namespace Bigfoot \
{ \
inline AssetContainer<AssetType> g_##AssetName##Container; \
}
#define ASSET_REF_DECL(AssetName, AssetType) \
ASSET_SOFT_REF_DECL(AssetName, AssetType) \
ASSET_HARD_REF_DECL(AssetName, AssetType)
#define ASSET_DECL(AssetName, AssetType) \
ASSET_REF_DECL(AssetName, AssetType) \
ASSET_CONTAINER(AssetName, AssetType)
#define ASSET_DECL(AssetName, AssetType) ASSET_REF_DECL(AssetName, AssetType)
/****************************************************************************************/

View File

@@ -80,7 +80,7 @@ class HardReference
{
if (m_uuid)
{
AssetContainer<ASSET>::Get().IncrementHardRefCount(m_uuid);
// AssetContainer<ASSET>::Get().IncrementHardRefCount(m_uuid);
}
}
@@ -88,7 +88,7 @@ class HardReference
{
if (m_uuid)
{
AssetContainer<ASSET>::Get().DecrementHardRefCount(m_uuid);
// AssetContainer<ASSET>::Get().DecrementHardRefCount(m_uuid);
}
}
@@ -161,7 +161,7 @@ class SoftReference
{
if (m_uuid)
{
AssetContainer<ASSET>::Get().IncrementSoftRefCount(m_uuid);
// AssetContainer<ASSET>::Get().IncrementSoftRefCount(m_uuid);
}
}
@@ -169,7 +169,7 @@ class SoftReference
{
if (m_uuid)
{
AssetContainer<ASSET>::Get().DecrementSoftRefCount(m_uuid);
// AssetContainer<ASSET>::Get().DecrementSoftRefCount(m_uuid);
}
}

View File

@@ -6,11 +6,7 @@
*********************************************************************/
#ifndef BIGFOOT_UTILS_SINGLETON_HPP
#define BIGFOOT_UTILS_SINGLETON_HPP
#include <EASTL/array.h>
#include <EASTL/bit.h>
#include <EASTL/optional.h>
#include <EASTL/type_traits.h>
#include <EASTL/utility.h>
namespace Bigfoot
{
@@ -51,9 +47,6 @@ class Singleton
/**
* Constructor.
*
* Initializes the singleton; this lifetime owns the instance and
* finalizes it on destruction.
*
* \param p_args Arguments for the singleton
*/
template<typename... ARGS>
@@ -62,53 +55,16 @@ class Singleton
Initialize(std::forward<ARGS>(p_args)...);
}
struct EmptyLifetime
{
};
static inline constexpr EmptyLifetime EMPTY_LIFETIME;
/**
* Non-owning constructor.
*
* Does not initialize the singleton and owns nothing. Such a lifetime
* only becomes an owner if it later receives ownership through move
* assignment.
*/
explicit Lifetime(EmptyLifetime) noexcept:
m_owner(false)
{
}
Lifetime(const Lifetime& p_lifetime) = delete;
explicit Lifetime(Lifetime&& p_lifetime) noexcept:
m_owner(eastl::exchange(p_lifetime.m_owner, false))
{
}
Lifetime(Lifetime&& p_lifetime) = delete;
~Lifetime()
{
if (m_owner)
{
Finalize();
}
Finalize();
}
Lifetime& operator=(const Lifetime& p_lifetime) = delete;
Lifetime& operator=(Lifetime&& p_lifetime) noexcept
{
m_owner = eastl::exchange(p_lifetime.m_owner, false);
return *this;
}
private:
/**
* Whether this lifetime owns the singleton instance or not.
*/
bool m_owner = true;
Lifetime& operator=(Lifetime&& p_lifetime) noexcept = delete;
};
Singleton& operator=(const Singleton& p_singleton) = delete;

View File

@@ -6,8 +6,6 @@
*********************************************************************/
#include <Engine/Asset/Asset.hpp>
#include <Utils/TargetMacros.h>
#include <EngineTests/Asset/AssetA.hpp>
#include <EngineTests/Asset/AssetB.hpp>
#include <EngineTests/Asset/AssetC.hpp>
@@ -40,19 +38,19 @@ TEST_F(AssetFixture, AssetATests)
AssetA assetA_dup {test};
EXPECT_EQ(assetA.GetHeader().uuid, assetA_dup.GetHeader().uuid);
EXPECT_EQ(assetA.Header().uuid, assetA_dup.Header().uuid);
EXPECT_EQ(assetA.GetHeader().type_id, AssetA::GetTypeID());
EXPECT_EQ(assetA.GetHeader().type_id, assetA_dup.GetHeader().type_id);
EXPECT_EQ(assetA.Header().type_id, AssetA::TypeID());
EXPECT_EQ(assetA.Header().type_id, assetA_dup.Header().type_id);
EXPECT_EQ(assetA.GetHeader().type_name, AssetA::GetTypeName());
EXPECT_EQ(assetA.GetHeader().type_name, assetA_dup.GetHeader().type_name);
EXPECT_EQ(assetA.Header().type_name, AssetA::TypeName());
EXPECT_EQ(assetA.Header().type_name, assetA_dup.Header().type_name);
EXPECT_EQ(assetA.GetHeader().name, name);
EXPECT_EQ(assetA.GetHeader().name, assetA_dup.GetHeader().name);
EXPECT_EQ(assetA.Header().name, name);
EXPECT_EQ(assetA.Header().name, assetA_dup.Header().name);
EXPECT_EQ(assetA.GetHeader().version, version);
EXPECT_EQ(assetA.GetHeader().version, assetA_dup.GetHeader().version);
EXPECT_EQ(assetA.Header().version, version);
EXPECT_EQ(assetA.Header().version, assetA_dup.Header().version);
EXPECT_EQ(assetA.GetAsset().health, health);
EXPECT_EQ(assetA.GetAsset().health, assetA_dup.GetAsset().health);
@@ -93,34 +91,34 @@ TEST_F(AssetFixture, AssetBTests)
AssetB assetB;
assetB.SetName(nameB);
assetB.SetVersion(versionB);
assetB.GetAsset().asset_a_ref = HardReference<AssetA> {assetA.GetHeader().uuid};
assetB.GetAsset().asset_a_refs = {SoftReference<AssetA> {assetA.GetHeader().uuid},
SoftReference<AssetA> {assetAA.GetHeader().uuid}};
assetB.GetAsset().asset_a_ref = HardReference<AssetA> {assetA.Header().uuid};
assetB.GetAsset().asset_a_refs = {SoftReference<AssetA> {assetA.Header().uuid},
SoftReference<AssetA> {assetAA.Header().uuid}};
const eastl::vector<std::byte> test = assetB.Save();
AssetB assetB_dup {test};
EXPECT_EQ(assetB.GetHeader().uuid, assetB_dup.GetHeader().uuid);
EXPECT_EQ(assetB.Header().uuid, assetB_dup.Header().uuid);
EXPECT_EQ(assetB.GetHeader().type_id, AssetB::GetTypeID());
EXPECT_EQ(assetB.GetHeader().type_id, assetB_dup.GetHeader().type_id);
EXPECT_EQ(assetB.Header().type_id, AssetB::TypeID());
EXPECT_EQ(assetB.Header().type_id, assetB_dup.Header().type_id);
EXPECT_EQ(assetB.GetHeader().type_name, AssetB::GetTypeName());
EXPECT_EQ(assetB.GetHeader().type_name, assetB_dup.GetHeader().type_name);
EXPECT_EQ(assetB.Header().type_name, AssetB::TypeName());
EXPECT_EQ(assetB.Header().type_name, assetB_dup.Header().type_name);
EXPECT_EQ(assetB.GetHeader().name, nameB);
EXPECT_EQ(assetB.GetHeader().name, assetB_dup.GetHeader().name);
EXPECT_EQ(assetB.Header().name, nameB);
EXPECT_EQ(assetB.Header().name, assetB_dup.Header().name);
EXPECT_EQ(assetB.GetHeader().version, versionB);
EXPECT_EQ(assetB.GetHeader().version, assetB_dup.GetHeader().version);
EXPECT_EQ(assetB.Header().version, versionB);
EXPECT_EQ(assetB.Header().version, assetB_dup.Header().version);
EXPECT_EQ(assetB.GetAsset().asset_a_ref, HardReference<AssetA> {assetA.GetHeader().uuid});
EXPECT_EQ(assetB.GetAsset().asset_a_ref, HardReference<AssetA> {assetA.Header().uuid});
EXPECT_EQ(assetB.GetAsset().asset_a_ref, assetB_dup.GetAsset().asset_a_ref);
EXPECT_EQ(assetB.GetAsset().asset_a_refs,
(eastl::vector<SoftReference<AssetA>> {SoftReference<AssetA> {assetA.GetHeader().uuid},
SoftReference<AssetA> {assetAA.GetHeader().uuid}}));
(eastl::vector<SoftReference<AssetA>> {SoftReference<AssetA> {assetA.Header().uuid},
SoftReference<AssetA> {assetAA.Header().uuid}}));
EXPECT_EQ(assetB.GetAsset().asset_a_refs, assetB_dup.GetAsset().asset_a_refs);
}
@@ -156,9 +154,9 @@ TEST_F(AssetFixture, AssetCTests)
AssetB assetB;
assetB.SetName(nameB);
assetB.SetVersion(versionB);
assetB.GetAsset().asset_a_ref = HardReference<AssetA> {assetA.GetHeader().uuid};
assetB.GetAsset().asset_a_refs = {SoftReference<AssetA> {assetA.GetHeader().uuid},
SoftReference<AssetA> {assetAA.GetHeader().uuid}};
assetB.GetAsset().asset_a_ref = HardReference<AssetA> {assetA.Header().uuid};
assetB.GetAsset().asset_a_refs = {SoftReference<AssetA> {assetA.Header().uuid},
SoftReference<AssetA> {assetAA.Header().uuid}};
constexpr eastl::string_view nameC = "General";
constexpr std::uint32_t versionC = 1;
@@ -166,43 +164,43 @@ TEST_F(AssetFixture, AssetCTests)
AssetC assetC;
assetC.SetName(nameC);
assetC.SetVersion(versionC);
assetC.GetAsset().inner_table->asset_a_ref = HardReference<AssetA> {assetA.GetHeader().uuid};
assetC.GetAsset().inner_table->asset_a_refs = {HardReference<AssetA> {assetA.GetHeader().uuid},
HardReference<AssetA> {assetAA.GetHeader().uuid}};
assetC.GetAsset().asset_b_ref = HardReference<AssetB> {assetB.GetHeader().uuid};
assetC.GetAsset().asset_b_refs = {SoftReference<AssetB> {assetB.GetHeader().uuid}};
assetC.GetAsset().inner_table->asset_a_ref = HardReference<AssetA> {assetA.Header().uuid};
assetC.GetAsset().inner_table->asset_a_refs = {HardReference<AssetA> {assetA.Header().uuid},
HardReference<AssetA> {assetAA.Header().uuid}};
assetC.GetAsset().asset_b_ref = HardReference<AssetB> {assetB.Header().uuid};
assetC.GetAsset().asset_b_refs = {SoftReference<AssetB> {assetB.Header().uuid}};
const eastl::vector<std::byte> test = assetC.Save();
AssetC assetC_dup {test};
EXPECT_EQ(assetC.GetHeader().uuid, assetC_dup.GetHeader().uuid);
EXPECT_EQ(assetC.Header().uuid, assetC_dup.Header().uuid);
EXPECT_EQ(assetC.GetHeader().type_id, AssetC::GetTypeID());
EXPECT_EQ(assetC.GetHeader().type_id, assetC_dup.GetHeader().type_id);
EXPECT_EQ(assetC.Header().type_id, AssetC::TypeID());
EXPECT_EQ(assetC.Header().type_id, assetC_dup.Header().type_id);
EXPECT_EQ(assetC.GetHeader().type_name, AssetC::GetTypeName());
EXPECT_EQ(assetC.GetHeader().type_name, assetC_dup.GetHeader().type_name);
EXPECT_EQ(assetC.Header().type_name, AssetC::TypeName());
EXPECT_EQ(assetC.Header().type_name, assetC_dup.Header().type_name);
EXPECT_EQ(assetC.GetHeader().name, nameC);
EXPECT_EQ(assetC.GetHeader().name, assetC_dup.GetHeader().name);
EXPECT_EQ(assetC.Header().name, nameC);
EXPECT_EQ(assetC.Header().name, assetC_dup.Header().name);
EXPECT_EQ(assetC.GetHeader().version, versionC);
EXPECT_EQ(assetC.GetHeader().version, assetC_dup.GetHeader().version);
EXPECT_EQ(assetC.Header().version, versionC);
EXPECT_EQ(assetC.Header().version, assetC_dup.Header().version);
EXPECT_EQ(assetC.GetAsset().asset_b_ref, HardReference<AssetB> {assetB.GetHeader().uuid});
EXPECT_EQ(assetC.GetAsset().asset_b_ref, HardReference<AssetB> {assetB.Header().uuid});
EXPECT_EQ(assetC.GetAsset().asset_b_ref, assetC_dup.GetAsset().asset_b_ref);
EXPECT_EQ(assetC.GetAsset().asset_b_refs,
(eastl::vector<SoftReference<AssetB>> {SoftReference<AssetB> {assetB.GetHeader().uuid}}));
(eastl::vector<SoftReference<AssetB>> {SoftReference<AssetB> {assetB.Header().uuid}}));
EXPECT_EQ(assetC.GetAsset().asset_b_refs, assetC_dup.GetAsset().asset_b_refs);
EXPECT_EQ(assetC.GetAsset().inner_table->asset_a_ref, HardReference<AssetA> {assetA.GetHeader().uuid});
EXPECT_EQ(assetC.GetAsset().inner_table->asset_a_ref, HardReference<AssetA> {assetA.Header().uuid});
EXPECT_EQ(assetC.GetAsset().inner_table->asset_a_ref, assetC_dup.GetAsset().inner_table->asset_a_ref);
EXPECT_EQ(assetC.GetAsset().inner_table->asset_a_refs,
(eastl::vector<HardReference<AssetA>> {HardReference<AssetA> {assetA.GetHeader().uuid},
HardReference<AssetA> {assetAA.GetHeader().uuid}}));
(eastl::vector<HardReference<AssetA>> {HardReference<AssetA> {assetA.Header().uuid},
HardReference<AssetA> {assetAA.Header().uuid}}));
EXPECT_EQ(assetC.GetAsset().inner_table->asset_a_refs, assetC_dup.GetAsset().inner_table->asset_a_refs);
}
} // namespace Bigfoot

View File

@@ -0,0 +1,73 @@
/*********************************************************************
* \file BigFile.cpp
*
* \author Romain BOULLARD
* \date December 2025
*********************************************************************/
#include <Engine/Asset/AssetContainer.hpp>
#include <EngineTests/Asset/AssetA.hpp>
#include <gtest/gtest.h>
namespace Bigfoot
{
class AssetContainerFixture: public ::testing::Test
{
protected:
AssetContainer<AssetA> m_container;
};
/****************************************************************************************/
TEST_F(AssetContainerFixture, Insert)
{
std::ignore = m_container.Insert();
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Get)
{
const auto key = m_container.Insert();
const auto& constContainer = m_container;
const auto validateKey = [&](auto& p_assetContainer)
{
const auto asset = p_assetContainer.Get(key);
EXPECT_NE(asset, nullptr);
};
validateKey(m_container);
validateKey(constContainer);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, SlotKey)
{
const auto key = m_container.Insert();
const auto asset = m_container.Get(key);
EXPECT_EQ(m_container.SlotKey(asset->Header().uuid), key);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Has)
{
const auto key = m_container.Insert();
EXPECT_TRUE(m_container.Has(key));
EXPECT_TRUE(m_container.Has(m_container.Get(key)->Header().uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Remove)
{
const auto key1 = m_container.Insert();
m_container.Remove(key1);
const auto key2 = m_container.Insert();
m_container.Remove(m_container.Get(key2)->Header().uuid);
}
} // namespace Bigfoot

View File

@@ -92,35 +92,6 @@ TEST_F(SingletonFixture, ConstructorAndDestructorShouldBeCalled)
/****************************************************************************************/
TEST_F(SingletonFixture, ConstructorAndDestructorShouldNotBeCalledForEmptyLifetime)
{
::testing::InSequence seq;
EXPECT_CALL(*m_mock, Construct()).Times(0);
EXPECT_CALL(*m_mock, Destruct()).Times(0);
Singleton<SingletonTest>::Lifetime singleton {Singleton<SingletonTest>::Lifetime::EMPTY_LIFETIME};
}
/****************************************************************************************/
TEST_F(SingletonFixture, MovingALifetimeKeepsTheSIngletonAlive)
{
::testing::InSequence seq;
EXPECT_CALL(*m_mock, Construct()).Times(1);
EXPECT_CALL(*m_mock, Destruct()).Times(1);
Singleton<SingletonTest>::Lifetime emptyLifetime {Singleton<SingletonTest>::Lifetime::EMPTY_LIFETIME};
{
Singleton<SingletonTest>::Lifetime singleton {42};
emptyLifetime = std::move(singleton);
}
EXPECT_EQ(Singleton<SingletonTest>::Instance().Data(), 42);
}
/****************************************************************************************/
TEST_F(SingletonFixture, Instance_ShouldReturnTheInstance)
{
Singleton<SingletonTest>::Lifetime singleton {42};