BigFile/Sasquatch #1

Merged
rboullard merged 100 commits from BigFile into Development 2026-09-01 16:32:02 +00:00
6 changed files with 407 additions and 80 deletions
Showing only changes of commit e4b2638b08 - Show all commits
@@ -0,0 +1,188 @@
/*********************************************************************
* \file AssetContainer.cpp
*
* \author Romain BOULLARD
* \date August 2026
*********************************************************************/
#include <Engine/Asset/AssetContainer.hpp>
namespace Bigfoot
{
ContainerHandle::ContainerHandle(AssetContainerBase* p_assetContainer, const UUID p_uuid):
m_assetContainer(p_assetContainer),
m_uuid(p_uuid)
{
}
/****************************************************************************************/
std::uint32_t ContainerHandle::GetHardRefCount() const
{
return m_hardRefCount;
}
/****************************************************************************************/
void ContainerHandle::IncrementHardRefCount()
{
if (m_hardRefCount == 0)
{
m_assetContainer->CancelRemoval(m_uuid);
}
++m_hardRefCount;
}
/****************************************************************************************/
void ContainerHandle::DecrementHardRefCount()
{
--m_hardRefCount;
if (m_hardRefCount == 0)
{
m_assetContainer->QueueForRemoval(m_uuid);
}
}
/****************************************************************************************/
std::uint32_t ContainerHandle::GetSoftRefCount() const
{
return m_softRefCount;
}
/****************************************************************************************/
void ContainerHandle::IncrementSoftRefCount()
{
++m_softRefCount;
}
/****************************************************************************************/
void ContainerHandle::DecrementSoftRefCount()
{
--m_softRefCount;
}
/****************************************************************************************/
AssetBase* ContainerHandle::GetAsset()
{
return m_asset;
}
/****************************************************************************************/
const AssetBase* ContainerHandle::GetAsset() const
{
return m_asset;
}
/****************************************************************************************/
void ContainerHandle::SetAsset(AssetBase* p_asset)
{
m_asset = p_asset;
}
/****************************************************************************************/
bool AssetContainerBase::Has(const UUID& p_uuid) const
{
const Entry* entry = FindEntry(p_uuid);
return entry != nullptr && entry->m_assetSlotKey.Valid();
}
/****************************************************************************************/
ContainerHandle* AssetContainerBase::AcquireContainerHandle(const UUID& p_uuid)
{
return GetOrCreateEntry(p_uuid).m_handle;
}
/****************************************************************************************/
void AssetContainerBase::Remove(const UUID& p_uuid)
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr)
{
if (entry->m_assetSlotKey.Valid())
{
entry->m_handle->SetAsset(nullptr);
RemoveAsset(entry->m_assetSlotKey);
entry->m_assetSlotKey = SlotMapKey {};
}
m_handles.Remove(entry->m_handleSlotKey);
m_uuidToEntry.erase(p_uuid);
}
}
/****************************************************************************************/
void AssetContainerBase::QueueForRemoval(const UUID& p_uuid)
{
m_pendingRemovalUuids.push_back(p_uuid);
}
/****************************************************************************************/
void AssetContainerBase::CancelRemoval(const UUID& p_uuid)
{
const auto it = eastl::find(m_pendingRemovalUuids.begin(), m_pendingRemovalUuids.end(), p_uuid);
if (it != m_pendingRemovalUuids.end())
{
m_pendingRemovalUuids.erase_unsorted(it);
}
}
/****************************************************************************************/
void AssetContainerBase::ProcessPendingRemovals()
{
eastl::vector<UUID> toRemove;
toRemove.swap(m_pendingRemovalUuids);
for (const UUID& uuid: toRemove)
{
Remove(uuid);
}
}
/****************************************************************************************/
bool AssetContainerBase::IsMarkedForRemove(const UUID& p_uuid)
{
return eastl::find(m_pendingRemovalUuids.begin(), m_pendingRemovalUuids.end(), p_uuid) !=
m_pendingRemovalUuids.end();
}
/****************************************************************************************/
AssetContainerBase::Entry* AssetContainerBase::FindEntry(const UUID& p_uuid)
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
/****************************************************************************************/
const AssetContainerBase::Entry* AssetContainerBase::FindEntry(const UUID& p_uuid) const
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
/****************************************************************************************/
AssetContainerBase::Entry& AssetContainerBase::GetOrCreateEntry(const UUID& p_uuid)
{
auto [it, inserted] = m_uuidToEntry.try_emplace(p_uuid);
if (inserted)
{
it->second.m_handleSlotKey = m_handles.Insert(this, p_uuid);
it->second.m_handle = m_handles.Get(it->second.m_handleSlotKey);
}
return it->second;
}
} // namespace Bigfoot
@@ -69,6 +69,16 @@ bool AssetLibrary::IsLoaded(const UUID& p_uuid) const
/****************************************************************************************/
void AssetLibrary::ProcessPendingRemovals() {
for (const auto [typeId, container] : m_assetContainers)
{
container->ProcessPendingRemovals();
}
}
/****************************************************************************************/
void SetActiveAssetLibrary(AssetLibrary* p_assetLibrary)
{
g_activeAssetLibrary = p_assetLibrary;
@@ -18,40 +18,41 @@
namespace Bigfoot
{
class AssetContainerBase;
/**
* An indirection that allows References to immediately get the asset, without fetching the actual container
*/
class ContainerHandle
{
public:
/**
* Constructor
*
* \param p_assetContainer The originating assetcontainer
* \param p_uuid The UUID of tha asset
*/
ContainerHandle(AssetContainerBase* p_assetContainer, const UUID p_uuid);
/*
* Get the HardRef count
*
* \return The HardRef count.
*/
[[nodiscard]]
std::uint32_t GetHardRefCount() const
{
return m_hardRefCount;
}
std::uint32_t GetHardRefCount() const;
/*
* Increment the HardRef count
*
*/
void IncrementHardRefCount()
{
++m_hardRefCount;
}
void IncrementHardRefCount();
/*
* Decrement the HardRef count
*
*/
void DecrementHardRefCount()
{
--m_hardRefCount;
}
void DecrementHardRefCount();
/*
* Get the SoftRef count
@@ -59,28 +60,19 @@ class ContainerHandle
* \return The SoftRef count.
*/
[[nodiscard]]
std::uint32_t GetSoftRefCount() const
{
return m_softRefCount;
}
std::uint32_t GetSoftRefCount() const;
/*
* Increment the SoftRef count
*
*/
void IncrementSoftRefCount()
{
++m_softRefCount;
}
void IncrementSoftRefCount();
/*
* Decrement the SoftRef count
*
*/
void DecrementSoftRefCount()
{
--m_softRefCount;
}
void DecrementSoftRefCount();
/*
* Get the Asset
@@ -88,10 +80,7 @@ class ContainerHandle
* \return The asset, can be nullptr
*/
[[nodiscard]]
AssetBase* GetAsset()
{
return m_asset;
}
AssetBase* GetAsset();
/*
* Get the Asset
@@ -99,20 +88,14 @@ class ContainerHandle
* \return The asset
*/
[[nodiscard]]
const AssetBase* GetAsset() const
{
return m_asset;
}
const AssetBase* GetAsset() const;
/*
* Set the asset
*
* \param p_asset The asset
*/
void SetAsset(AssetBase* p_asset)
{
m_asset = p_asset;
}
void SetAsset(AssetBase* p_asset);
private:
/*
@@ -129,6 +112,16 @@ class ContainerHandle
* The asset
*/
AssetBase* m_asset = nullptr;
/**
* The asset container
*/
AssetContainerBase* m_assetContainer = nullptr;
/**
* The UUID of the asset
*/
UUID m_uuid = UUID::NULL_UUID;
};
/****************************************************************************************/
@@ -165,11 +158,7 @@ class AssetContainerBase
* \return True if in AssetContainer, false otherwise
*/
[[nodiscard]]
bool Has(const UUID& p_uuid) const
{
const Entry* entry = FindEntry(p_uuid);
return entry != nullptr && entry->m_assetSlotKey.Valid();
}
bool Has(const UUID& p_uuid) const;
/*
* Acquires a ContainerHandle for a given UUID
@@ -179,30 +168,37 @@ class AssetContainerBase
* \return A ContainerHandle for the UUID
*/
[[nodiscard]]
ContainerHandle* AcquireContainerHandle(const UUID& p_uuid)
{
return GetOrCreateEntry(p_uuid).m_handle;
}
ContainerHandle* AcquireContainerHandle(const UUID& p_uuid);
/*
* Remove an Asset from the AssetContainer
*
* \param p_uuid The UUID to remove
*/
void Remove(const UUID& p_uuid)
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr)
{
if (entry->m_assetSlotKey.Valid())
{
entry->m_handle->SetAsset(nullptr);
RemoveAsset(entry->m_assetSlotKey);
entry->m_assetSlotKey = SlotMapKey {};
}
m_handles.Remove(entry->m_handleSlotKey);
m_uuidToEntry.erase(p_uuid);
}
}
void Remove(const UUID& p_uuid);
/**
* Add an asset to be removed
*
* \param p_uuid UUID of the asset to remove
*/
void QueueForRemoval(const UUID& p_uuid);
/**
* Remove an asset from the pending remove queue
*
* \param p_uuid UUID of the asset to cancel removal
*/
void CancelRemoval(const UUID& p_uuid);
/**
* Process the assets that have been marked for remove
*
*/
void ProcessPendingRemovals();
[[nodiscard]]
bool IsMarkedForRemove(const UUID& p_uuid);
AssetContainerBase& operator=(AssetContainerBase&& p_container) = delete;
@@ -236,11 +232,7 @@ class AssetContainerBase
* \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;
}
Entry* FindEntry(const UUID& p_uuid);
/*
* Finds an entry matching the Asset
@@ -249,11 +241,7 @@ class AssetContainerBase
* \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;
}
const Entry* FindEntry(const UUID& p_uuid) const;
/*
* Finds an entry matching the Asset
@@ -263,17 +251,7 @@ class AssetContainerBase
* \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();
it->second.m_handle = m_handles.Get(it->second.m_handleSlotKey);
}
return it->second;
}
Entry& GetOrCreateEntry(const UUID& p_uuid);
/*
* Remove an asset
@@ -292,6 +270,11 @@ class AssetContainerBase
* Every Asset added to the map
*/
ankerl::unordered_dense::map<UUID, Entry> m_uuidToEntry;
/**
* Assets queued for removal
*/
eastl::vector<UUID> m_pendingRemovalUuids;
};
/****************************************************************************************/
@@ -90,6 +90,12 @@ class AssetLibrary
[[nodiscard]]
bool IsLoaded(const UUID& p_uuid) const;
/**
* Loop through the AssetContainers to remove the pending assets marked for removal
*
*/
void ProcessPendingRemovals();
AssetLibrary& operator=(const AssetLibrary& p_library) = default;
AssetLibrary& operator=(AssetLibrary&& p_library) = delete;
@@ -84,6 +84,40 @@ TEST_F(ContainerHandleFixture, DecrementSoftRefCount_ShouldDecrementTheSoftRefCo
/****************************************************************************************/
TEST_F(ContainerHandleFixture, DecrementHardRefCount_ShouldQueueTheAssetForRemovalWhenItReachesZero)
{
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
EXPECT_TRUE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
TEST_F(ContainerHandleFixture, DecrementHardRefCount_ShouldNotQueueTheAssetForRemovalIfItDoesNotReachZero)
{
m_handle->IncrementHardRefCount();
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
EXPECT_FALSE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
TEST_F(ContainerHandleFixture, IncrementHardRefCount_ShouldCancelAPendingRemovalWhenGoingFromZero)
{
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
ASSERT_TRUE(m_containerA.IsMarkedForRemove(m_uuid));
m_handle->IncrementHardRefCount();
EXPECT_FALSE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
class AssetContainerFixture: public ::testing::Test
{
protected:
@@ -144,4 +178,78 @@ TEST_F(AssetContainerFixture, Remove_ShouldRemoveTheAsset)
EXPECT_EQ(handle->GetAsset(), nullptr);
EXPECT_FALSE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, IsMarkedForRemove_ShouldReturnTrueIfTheAssetIsMarkedForDelete)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
EXPECT_TRUE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, IsMarkedForRemove_ShouldReturnFalseIfTheAssetIsNotMarkedForDelete)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
EXPECT_FALSE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, QueueForRemoval_ShouldMarkTheAssetForRemoval)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
EXPECT_TRUE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, CancelRemoval_ShouldUnmarkAQueuedAsset)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
m_containerA.CancelRemoval(uuid);
EXPECT_FALSE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, ProcessPendingRemovals_ShouldRemoveQueuedAssets)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
m_containerA.ProcessPendingRemovals();
EXPECT_EQ(handle->GetAsset(), nullptr);
EXPECT_FALSE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, ProcessPendingRemovals_ShouldNotRemoveAssetsThatAreNotQueued)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.ProcessPendingRemovals();
EXPECT_NE(handle->GetAsset(), nullptr);
EXPECT_TRUE(m_containerA.Has(uuid));
}
} // namespace Bigfoot
@@ -138,6 +138,38 @@ TEST_F(AssetLibraryFixture, IsLoaded_ShouldReturnFalseIfTheAssetWasRemovedFromIt
/****************************************************************************************/
TEST_F(AssetLibraryFixture, ProcessPendingRemovals_ShouldProcessRemovalsOnEveryContainer)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuidA = m_containerA.Insert()->GetAsset()->GetHeader().uuid;
const UUID uuidB = m_containerB.Insert()->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuidA);
m_containerB.QueueForRemoval(uuidB);
m_library.ProcessPendingRemovals();
EXPECT_FALSE(m_containerA.Has(uuidA));
EXPECT_FALSE(m_containerB.Has(uuidB));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, ProcessPendingRemovals_ShouldNotRemoveAssetsThatAreNotQueued)
{
std::ignore = m_library.Insert(&m_containerA);
const UUID uuid = m_containerA.Insert()->GetAsset()->GetHeader().uuid;
m_library.ProcessPendingRemovals();
EXPECT_TRUE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, SetActiveAssetLibrary_ShouldSetTheActiveAssetLibraryToTheGivenLibrary)
{
SetActiveAssetLibrary(&m_library);