Asset System (#2)
Bigfoot / Sonarqube (push) Successful in 5m42s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m27s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m11s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 55s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 12s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 48s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 42s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 50s
Bigfoot / Sonarqube (push) Successful in 5m42s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m27s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m11s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 55s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 12s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 48s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 42s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 50s
Only time I'm allowing a merge of single feature inside main (we prefer merging a version) But I need a first Sonarqube run Reviewed-on: #2 Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
This commit was merged in pull request #2.
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
/*********************************************************************
|
||||
* \file SerializationManager.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/SerializationManager/SerializationManager.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
#include <Engine/DataAccessor/DataAccessor.hpp>
|
||||
|
||||
#include <Utils/Algorithm/Graph.hpp>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Bigfoot::SerializationManager
|
||||
{
|
||||
/*
|
||||
* Build the transitive HardReference graph reachable from p_roots
|
||||
*
|
||||
* \param p_roots The assets to start the discovery from
|
||||
* \param p_discovered Every discovered asset that isn't already loaded, including p_roots
|
||||
* \return Every asset's HardReferences
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<UUID>> BuildDependencyGraph(
|
||||
const eastl::span<const UUID> p_roots,
|
||||
ankerl::unordered_dense::set<UUID>& p_discovered)
|
||||
{
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<UUID>> graph;
|
||||
|
||||
eastl::vector<UUID> toQuery;
|
||||
toQuery.reserve(p_roots.size());
|
||||
for (const UUID& uuid: p_roots)
|
||||
{
|
||||
if (!g_activeAssetLibrary->IsLoaded(uuid) && p_discovered.insert(uuid).second)
|
||||
{
|
||||
toQuery.push_back(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
while (!toQuery.empty())
|
||||
{
|
||||
eastl::vector<eastl::vector<UUID>> hardReferencesBatch = g_dataAccessor->GetAssetsHardReferences(toQuery);
|
||||
|
||||
eastl::vector<UUID> nextToQuery;
|
||||
for (std::size_t i = 0; i < toQuery.size(); ++i)
|
||||
{
|
||||
eastl::vector<UUID>& hardReferences = graph[toQuery[i]];
|
||||
hardReferences = std::move(hardReferencesBatch[i]);
|
||||
|
||||
for (const UUID& hardReference: hardReferences)
|
||||
{
|
||||
if (!g_activeAssetLibrary->IsLoaded(hardReference) && p_discovered.insert(hardReference).second)
|
||||
{
|
||||
nextToQuery.push_back(hardReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toQuery = std::move(nextToQuery);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void LoadAssets(const eastl::span<const UUID> p_uuids)
|
||||
{
|
||||
ankerl::unordered_dense::set<UUID> discovered;
|
||||
const ankerl::unordered_dense::map<UUID, eastl::vector<UUID>> graph = BuildDependencyGraph(p_uuids, discovered);
|
||||
|
||||
// Assets that are part of a cyclic HardReference dependency are left out of waves and simply never loaded.
|
||||
eastl::vector<eastl::vector<UUID>> waves;
|
||||
eastl::vector<UUID> cyclicUuids;
|
||||
std::ignore = LayeredTopologicalSort(graph, discovered, waves, cyclicUuids);
|
||||
BIGFOOT_LOG_ERROR_IF(!cyclicUuids.empty(),
|
||||
ENGINE_LOGGER,
|
||||
"Cyclic HardReference dependency detected while loading assets, involved UUIDs: {}",
|
||||
cyclicUuids);
|
||||
|
||||
// Waves are loaded one after another since a wave's assets may HardReference the previous wave's; within a
|
||||
// wave, every asset is independent of the others, so this is where loading could later be parallelized.
|
||||
for (const eastl::vector<UUID>& wave: waves)
|
||||
{
|
||||
const eastl::vector<eastl::vector<std::byte>> assetFlatbuffers = g_dataAccessor->GetAssetFlatbuffers(wave);
|
||||
for (std::size_t i = 0; i < wave.size(); ++i)
|
||||
{
|
||||
const eastl::vector<std::byte>& flatbuffer = assetFlatbuffers[i];
|
||||
if (flatbuffer.empty())
|
||||
{
|
||||
BIGFOOT_LOG_ERROR(ENGINE_LOGGER, "Asset {} could not be found while loading, skipping it", wave[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
flatbuffers::Verifier verifier {std::bit_cast<std::uint8_t*>(flatbuffer.data()), flatbuffer.size()};
|
||||
const bool assetVerified = ::Flat::Bigfoot::VerifyAssetBuffer(verifier);
|
||||
CRITICAL_ASSERT(EngineAssertHandler, assetVerified, "Given asset could not be verified!");
|
||||
#endif
|
||||
|
||||
const ::Flat::Bigfoot::Asset* asset = flatbuffers::GetRoot<::Flat::Bigfoot::Asset>(flatbuffer.data());
|
||||
|
||||
AssetContainerBase* container = g_activeAssetLibrary->GetAssetContainer(
|
||||
flatbuffers::UnPack(*asset->header()->type_id()));
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
container != nullptr,
|
||||
"No AssetContainer registered for asset {}",
|
||||
wave[i]);
|
||||
std::ignore = container->Insert(flatbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void LoadAsset(const UUID& p_uuid)
|
||||
{
|
||||
LoadAssets(eastl::span<const UUID> {&p_uuid, 1});
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
/*
|
||||
* Finds the cyclic dependencies that may exist in Assets
|
||||
*
|
||||
* \param p_uuids The assets we want to explore
|
||||
* \param p_hardReferences The HardReferences of p_uuids, matching by index
|
||||
* \return Every UUID caught in a cyclic dependency
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> FindCyclicDependencies(const eastl::span<const UUID> p_uuids,
|
||||
const eastl::span<const eastl::vector<UUID>> p_hardReferences)
|
||||
{
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<UUID>> graph;
|
||||
ankerl::unordered_dense::set<UUID> discovered;
|
||||
|
||||
eastl::vector<UUID> toQuery;
|
||||
for (std::size_t i = 0; i < p_uuids.size(); ++i)
|
||||
{
|
||||
discovered.insert(p_uuids[i]);
|
||||
graph[p_uuids[i]] = p_hardReferences[i];
|
||||
}
|
||||
for (const eastl::vector<UUID>& hardReferences: p_hardReferences)
|
||||
{
|
||||
for (const UUID& hardReference: hardReferences)
|
||||
{
|
||||
if (discovered.insert(hardReference).second)
|
||||
{
|
||||
toQuery.push_back(hardReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!toQuery.empty())
|
||||
{
|
||||
eastl::vector<eastl::vector<UUID>> hardReferencesBatch = g_dataAccessor->GetAssetsHardReferences(toQuery);
|
||||
|
||||
eastl::vector<UUID> nextToQuery;
|
||||
for (std::size_t i = 0; i < toQuery.size(); ++i)
|
||||
{
|
||||
eastl::vector<UUID>& hardReferences = graph[toQuery[i]];
|
||||
hardReferences = std::move(hardReferencesBatch[i]);
|
||||
|
||||
for (const UUID& hardReference: hardReferences)
|
||||
{
|
||||
if (discovered.insert(hardReference).second)
|
||||
{
|
||||
nextToQuery.push_back(hardReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toQuery = std::move(nextToQuery);
|
||||
}
|
||||
|
||||
eastl::vector<eastl::vector<UUID>> waves;
|
||||
eastl::vector<UUID> cyclicUuids;
|
||||
std::ignore = LayeredTopologicalSort(graph, discovered, waves, cyclicUuids);
|
||||
return cyclicUuids;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool SaveAsset(const UUID& p_uuid)
|
||||
{
|
||||
AssetContainerBase* container = g_activeAssetLibrary->GetAssetContainer(p_uuid);
|
||||
CRITICAL_ASSERT(EngineAssertHandler, container != nullptr, "No AssetContainer registered for asset {}", p_uuid);
|
||||
const AssetBase* asset = container->AcquireContainerHandle(p_uuid)->GetAsset();
|
||||
|
||||
const eastl::vector<std::byte> flatbuffer = asset->Pack();
|
||||
const eastl::vector<UUID> hardReferences = asset->GetHardReferences(flatbuffer);
|
||||
|
||||
const eastl::vector<UUID> cyclicUuids = FindCyclicDependencies(
|
||||
eastl::span<const UUID> {&p_uuid, 1},
|
||||
eastl::span<const eastl::vector<UUID>> {&hardReferences, 1});
|
||||
BIGFOOT_LOG_ERROR_IF(!cyclicUuids.empty(),
|
||||
ENGINE_LOGGER,
|
||||
"Cyclic HardReference dependency detected while saving asset {}, involved UUIDs: {}",
|
||||
p_uuid,
|
||||
cyclicUuids);
|
||||
if (!cyclicUuids.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
g_dataAccessor->SaveAssetFlatbuffer(p_uuid, flatbuffer, hardReferences);
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool SaveAssets(const eastl::span<const UUID> p_uuids)
|
||||
{
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
|
||||
flatbuffers.reserve(p_uuids.size());
|
||||
hardReferences.reserve(p_uuids.size());
|
||||
for (const UUID& uuid: p_uuids)
|
||||
{
|
||||
AssetContainerBase* container = g_activeAssetLibrary->GetAssetContainer(uuid);
|
||||
CRITICAL_ASSERT(EngineAssertHandler, container != nullptr, "No AssetContainer registered for asset {}", uuid);
|
||||
const AssetBase* asset = container->AcquireContainerHandle(uuid)->GetAsset();
|
||||
|
||||
const eastl::vector<std::byte> flatbuffer = asset->Pack();
|
||||
|
||||
flatbuffers.push_back(flatbuffer);
|
||||
hardReferences.push_back(asset->GetHardReferences(flatbuffer));
|
||||
}
|
||||
|
||||
const eastl::vector<UUID> cyclicUuids = FindCyclicDependencies(p_uuids, hardReferences);
|
||||
BIGFOOT_LOG_ERROR_IF(!cyclicUuids.empty(),
|
||||
ENGINE_LOGGER,
|
||||
"Cyclic HardReference dependency detected while saving assets, involved UUIDs: {}",
|
||||
cyclicUuids);
|
||||
if (!cyclicUuids.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
g_dataAccessor->SaveAssetFlatbuffers(p_uuids, flatbuffers, hardReferences);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
} // namespace Bigfoot::SerializationManager
|
||||
Reference in New Issue
Block a user