AssetContainer tests
Bigfoot / Build & Test Debug with ./ConanProfiles/clang (Unity Build: OFF) (push) Failing after 2s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang (Unity Build: ON) (push) Failing after 2s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Failing after 0s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Failing after 0s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: OFF) (push) Failing after 1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: ON) (push) Failing after 0s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Failing after 0s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Failing after 1s
Bigfoot / Build & Test Release with ./ConanProfiles/clang (Unity Build: OFF) (push) Failing after 0s
Bigfoot / Build & Test Release with ./ConanProfiles/clang (Unity Build: ON) (push) Failing after 0s
Bigfoot / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Failing after 0s
Bigfoot / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Failing after 0s
Bigfoot / Clang Format Checks (push) Failing after 1m44s

This commit is contained in:
2026-08-08 01:28:03 +02:00
parent 6788961ca0
commit 1b32cd07f6
4 changed files with 278 additions and 35 deletions
@@ -27,38 +27,69 @@ class ContainerHandle
~ContainerHandle() = default; ~ContainerHandle() = default;
/*
* Increment the HardRef count
*
*/
void IncrementHardRefCount() void IncrementHardRefCount()
{ {
++m_hardRefCount; ++m_hardRefCount;
} }
/*
* Decrement the HardRef count
*
*/
void DecrementHardRefCount() void DecrementHardRefCount()
{ {
--m_hardRefCount; --m_hardRefCount;
} }
/*
* Increment the SoftRef count
*
*/
void IncrementSoftRefCount() void IncrementSoftRefCount()
{ {
++m_softRefCount; ++m_softRefCount;
} }
/*
* Decrement the SoftRef count
*
*/
void DecrementSoftRefCount() void DecrementSoftRefCount()
{ {
--m_softRefCount; --m_softRefCount;
} }
/*
* Get the Asset
*
* \return The asset, can be nullptr
*/
[[nodiscard]] [[nodiscard]]
AssetBase* GetAsset() AssetBase* GetAsset()
{ {
return m_asset; return m_asset;
} }
/*
* Get the Asset
*
* \return The asset
*/
[[nodiscard]] [[nodiscard]]
const AssetBase* GetAsset() const const AssetBase* GetAsset() const
{ {
return m_asset; return m_asset;
} }
/*
* Set the asset
*
* \param p_asset The asset
*/
void SetAsset(AssetBase* p_asset) void SetAsset(AssetBase* p_asset)
{ {
m_asset = p_asset; m_asset = p_asset;
@@ -68,8 +99,19 @@ class ContainerHandle
ContainerHandle& operator=(ContainerHandle&& p_handle) = default; ContainerHandle& operator=(ContainerHandle&& p_handle) = default;
private: private:
/*
* The HardRef count
*/
std::uint32_t m_hardRefCount = 0; std::uint32_t m_hardRefCount = 0;
/*
* The SoftRef count
*/
std::uint32_t m_softRefCount = 0; std::uint32_t m_softRefCount = 0;
/*
* The asset
*/
AssetBase* m_asset = nullptr; AssetBase* m_asset = nullptr;
}; };
@@ -80,51 +122,135 @@ class AssetContainerBase
public: public:
virtual ~AssetContainerBase() = default; virtual ~AssetContainerBase() = default;
[[nodiscard]] /*
SlotMapKey GetAssetSlotKey(const UUID& p_uuid) const * Check if an Asset is in the AssetContainer or not
{ *
if (const auto it = m_uuidToAssetSlotKey.find(p_uuid); it != m_uuidToAssetSlotKey.end()) * \param p_uuid The UUID to check
{ * \return True if in AssetContainer, false otherwise
return it->second; */
}
return SlotMapKey {};
}
[[nodiscard]] [[nodiscard]]
bool Has(const UUID& p_uuid) const bool Has(const UUID& p_uuid) const
{ {
return m_uuidToAssetSlotKey.contains(p_uuid); const Entry* entry = FindEntry(p_uuid);
return entry != nullptr && entry->m_assetSlotKey.Valid();
} }
/*
* Acquires a ContainerHandle for a given UUID
* Will create it if it does not exists
*
* \param p_uuid The UUID to get ContainerHandle for
* \return A ContainerHandle for the UUID
*/
[[nodiscard]] [[nodiscard]]
ContainerHandle& AcquireHandle(const UUID& p_uuid) ContainerHandle& AcquireContainerHandle(const UUID& p_uuid)
{ {
if (const auto it = m_uuidToContainerHandleSlotKey.find(p_uuid); it != m_uuidToContainerHandleSlotKey.end()) return *m_handles.Get(GetOrCreateEntry(p_uuid).m_handleSlotKey);
}
/*
* Remove an Asset from the AssetContainer
*
* \param p_uuid The UUID to remove
*/
virtual void Remove(const UUID& p_uuid)
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr && entry->m_handleSlotKey.Valid())
{ {
return *m_handles.Get(it->second); m_handles.Remove(entry->m_handleSlotKey);
m_uuidToEntry.erase(p_uuid);
} }
const SlotMapKey key = m_handles.Insert();
m_uuidToContainerHandleSlotKey.emplace(p_uuid, key);
return *m_handles.Get(key);
} }
AssetContainerBase& operator=(const AssetContainerBase& p_container) = delete; AssetContainerBase& operator=(const AssetContainerBase& p_container) = delete;
AssetContainerBase& operator=(AssetContainerBase&& p_container) = default; AssetContainerBase& operator=(AssetContainerBase&& p_container) = default;
protected: protected:
struct Entry
{
/*
* SlotMapKey to the handle
*/
SlotMapKey m_handleSlotKey;
/*
* SlotMapKey to the asset
*/
SlotMapKey m_assetSlotKey;
};
AssetContainerBase() = default; AssetContainerBase() = default;
AssetContainerBase(const AssetContainerBase& p_container) = delete; AssetContainerBase(const AssetContainerBase& p_container) = delete;
AssetContainerBase(AssetContainerBase&& p_container) = default; AssetContainerBase(AssetContainerBase&& p_container) = default;
ankerl::unordered_dense::segmented_map<UUID, SlotMapKey> m_uuidToAssetSlotKey; /*
ankerl::unordered_dense::segmented_map<UUID, SlotMapKey> m_uuidToContainerHandleSlotKey; * Finds an entry matching the Asset
*
* \param p_uuid The UUID to find
* \return Pointer to an Entry, nullptr otherwise
*/
[[nodiscard]]
Entry* FindEntry(const UUID& p_uuid)
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
/*
* Finds an entry matching the Asset
*
* \param p_uuid The UUID to find
* \return Pointer to an Entry, nullptr otherwise
*/
[[nodiscard]]
const Entry* FindEntry(const UUID& p_uuid) const
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
/*
* Finds an entry matching the Asset
* Creates it if it does not exist
*
* \param p_uuid The UUID to find
* \return The entry matching the Asset
*/
[[nodiscard]]
Entry& GetOrCreateEntry(const UUID& p_uuid)
{
auto [it, inserted] = m_uuidToEntry.try_emplace(p_uuid);
if (inserted)
{
it->second.m_handleSlotKey = m_handles.Insert();
}
return it->second;
}
/*
* Get a ContainerHandle
*
* \param p_handleSlotKey The key to the container
* \return The container
*/
[[nodiscard]]
ContainerHandle& FindContainerHandle(const SlotMapKey p_handleSlotKey)
{
return *m_handles.Get(p_handleSlotKey);
}
private: private:
/*
* HandleContainers
*/
StableSlotMap<ContainerHandle> m_handles; StableSlotMap<ContainerHandle> m_handles;
/*
* Every Asset added to the map
*/
ankerl::unordered_dense::segmented_map<UUID, Entry> m_uuidToEntry;
}; };
/****************************************************************************************/ /****************************************************************************************/
@@ -140,6 +266,13 @@ class AssetContainer: public AssetContainerBase
~AssetContainer() = default; ~AssetContainer() = default;
/*
* Inserts an Asset to the AssetContainer
*
* \tparam ARGS Arguments to construct the asset
* \param p_args The arguments
* \return The key to the inserted asset.
*/
template<class... ARGS> template<class... ARGS>
[[nodiscard]] [[nodiscard]]
SlotMapKey Insert(ARGS&&... p_args) SlotMapKey Insert(ARGS&&... p_args)
@@ -147,46 +280,69 @@ class AssetContainer: public AssetContainerBase
SlotMapKey slotKey = m_assets.Insert(std::forward<ARGS>(p_args)...); SlotMapKey slotKey = m_assets.Insert(std::forward<ARGS>(p_args)...);
ASSET* asset = m_assets.Get(slotKey); ASSET* asset = m_assets.Get(slotKey);
if (const auto it = m_uuidToAssetSlotKey.find(asset->GetHeader().uuid); it != m_uuidToAssetSlotKey.end()) Entry& entry = GetOrCreateEntry(asset->GetHeader().uuid);
if (entry.m_assetSlotKey.Valid())
{ {
m_assets.Remove(slotKey); m_assets.Remove(slotKey);
slotKey = it->second; slotKey = entry.m_assetSlotKey;
} }
else else
{ {
m_uuidToAssetSlotKey.emplace(asset->GetHeader().uuid, slotKey); entry.m_assetSlotKey = slotKey;
AcquireHandle(asset->GetHeader().uuid).SetAsset(asset); FindContainerHandle(entry.m_handleSlotKey).SetAsset(asset);
} }
return slotKey; return slotKey;
} }
/*
* Get the Asset
*
* \param p_key The key to the asset
* \return Pointer to the Asset
*/
[[nodiscard]] [[nodiscard]]
ASSET* Get(const SlotMapKey p_key) ASSET* Get(const SlotMapKey p_key)
{ {
return m_assets.Get(p_key); return m_assets.Get(p_key);
} }
/*
* Get the Asset
*
* \param p_key The key to the asset
* \return Pointer to the Asset
*/
[[nodiscard]] [[nodiscard]]
const ASSET* Get(const SlotMapKey p_key) const const ASSET* Get(const SlotMapKey p_key) const
{ {
return m_assets.Get(p_key); return m_assets.Get(p_key);
} }
void Remove(const UUID& p_uuid) /*
* Remove an Asset from the AssetContainer
*
* \param p_uuid The UUID to remove
*/
void Remove(const UUID& p_uuid) override
{ {
if (const auto it = m_uuidToAssetSlotKey.find(p_uuid); it != m_uuidToAssetSlotKey.end()) if (Entry* entry = FindEntry(p_uuid); entry != nullptr && entry->m_assetSlotKey.Valid())
{ {
AcquireHandle(p_uuid).SetAsset(nullptr); FindContainerHandle(entry->m_handleSlotKey).SetAsset(nullptr);
m_assets.Remove(it->second); m_assets.Remove(entry->m_assetSlotKey);
m_uuidToAssetSlotKey.erase(it->first); entry->m_assetSlotKey = SlotMapKey {};
} }
AssetContainerBase::Remove(p_uuid);
} }
AssetContainer& operator=(const AssetContainer& p_container) = delete; AssetContainer& operator=(const AssetContainer& p_container) = delete;
AssetContainer& operator=(AssetContainer&& p_container) = default; AssetContainer& operator=(AssetContainer&& p_container) = default;
private: private:
/*
* The assets
*/
StableSlotMap<ASSET> m_assets; StableSlotMap<ASSET> m_assets;
}; };
} // namespace Bigfoot } // namespace Bigfoot
@@ -118,7 +118,7 @@ class AssetLibrary
{ {
for (const auto& [typeID, container]: m_assetContainers) for (const auto& [typeID, container]: m_assetContainers)
{ {
if (container->GetAssetSlotKey(p_uuid).Valid()) if (container->Has(p_uuid))
{ {
return container; return container;
} }
@@ -138,7 +138,7 @@ class AssetLibrary
{ {
for (const auto& [typeID, container]: m_assetContainers) for (const auto& [typeID, container]: m_assetContainers)
{ {
if (container->GetAssetSlotKey(p_uuid).Valid()) if (container->Has(p_uuid))
{ {
return container; return container;
} }
@@ -116,7 +116,7 @@ class HardReference
{ {
if (!m_handle) if (!m_handle)
{ {
m_handle = &assetContainer->AcquireHandle(m_uuid); m_handle = &assetContainer->AcquireContainerHandle(m_uuid);
} }
m_handle->IncrementSoftRefCount(); m_handle->IncrementSoftRefCount();
} }
@@ -234,7 +234,7 @@ class SoftReference
{ {
if (!m_handle) if (!m_handle)
{ {
m_handle = &assetContainer->AcquireHandle(m_uuid); m_handle = &assetContainer->AcquireContainerHandle(m_uuid);
} }
m_handle->IncrementSoftRefCount(); m_handle->IncrementSoftRefCount();
} }
@@ -19,4 +19,91 @@ class AssetContainerFixture: public ::testing::Test
/****************************************************************************************/ /****************************************************************************************/
TEST_F(AssetContainerFixture, Has_ShouldReturnTrueIfTheContainerHasTheAsset)
{
AssetA asset1;
std::ignore = m_containerA.Insert(asset1);
EXPECT_TRUE(m_containerA.Has(asset1.GetHeader().uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Has_ShouldReturnFalseIfTheContainerDoesNotHaveTheAsset)
{
AssetA asset1;
EXPECT_FALSE(m_containerA.Has(asset1.GetHeader().uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, AcquireHandle_ShouldReturnTheAssociatedContainerHandle)
{
AssetA asset1;
std::ignore = m_containerA.Insert(asset1);
const ContainerHandle& handle = m_containerA.AcquireContainerHandle(asset1.GetHeader().uuid);
EXPECT_EQ(static_cast<const AssetA*>(handle.GetAsset())->GetHeader().uuid, asset1.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, AcquireHandle_ShouldCreateTheAssociatedContainerHandleIfItDoesNotExists)
{
AssetA asset1;
const ContainerHandle& handle = m_containerA.AcquireContainerHandle(asset1.GetHeader().uuid);
EXPECT_EQ(handle.GetAsset(), nullptr);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Insert_ShouldReturnValidAndUniqueSlotKeyOnInsert)
{
AssetA asset1;
const SlotMapKey slotKey = m_containerA.Insert(asset1);
EXPECT_TRUE(slotKey.Valid());
EXPECT_NE(slotKey, m_containerA.Insert());
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Insert_ShouldReturnTheSameSlotKeyInCaseOfDuplicatedUUID)
{
AssetA asset;
const SlotMapKey slotKey = m_containerA.Insert(asset);
EXPECT_EQ(slotKey, m_containerA.Insert(asset));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Get_ShouldReturnTheAssetWithTheSlotKey)
{
AssetA asset;
const SlotMapKey slotKey = m_containerA.Insert(asset);
EXPECT_EQ(m_containerA.Get(slotKey)->GetHeader().uuid, asset.GetHeader().uuid);
EXPECT_EQ(const_cast<const AssetContainer<AssetA>&>(m_containerA).Get(slotKey)->GetHeader().uuid,
asset.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Get_ShouldReturnNullptrIfSlotKeyNotInContainer)
{
EXPECT_EQ(m_containerA.Get(SlotMapKey {1, 0}), nullptr);
EXPECT_EQ(const_cast<const AssetContainer<AssetA>&>(m_containerA).Get(SlotMapKey {1, 0}), nullptr);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Remove_ShouldRemoveTheAsset)
{
AssetA asset;
const SlotMapKey slotKey = m_containerA.Insert(asset);
m_containerA.Remove(asset.GetHeader().uuid);
EXPECT_EQ(m_containerA.Get(slotKey), nullptr);
EXPECT_FALSE(m_containerA.Has(asset.GetHeader().uuid));
}
} // namespace Bigfoot } // namespace Bigfoot