Files
Bigfoot/Bigfoot/Sources/Engine/Sasquatch/Sasquatch.cpp
T
rboullard b7f4677c68
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 44s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 46s
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 1m21s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m3s
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 1m8s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m9s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m34s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m13s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 37s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 28s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 57s
Bigfoot / Sonarqube (push) Successful in 6m0s
sonarqube ERROR
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 30s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 36s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m23s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 59s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 51s
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 13s
Bigfoot / Sonarqube (pull_request) Failing after 5m14s
BigFile/Sasquatch (#1)
Implementing an asset management system

BigFile is a sqlite database, for toolmode
Sasquatch is a custom binary file, for gamemode

Assets are built from flatbuffers
References allow to get an Asset, from an UUID, from anywhere in the code
HardReference refcount the asset and allow automatic removal

Reviewed-on: #1
Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
2026-09-01 16:32:01 +00:00

267 lines
10 KiB
C++

/*********************************************************************
* \file Sasquatch.cpp
*
* \author Romain BOULLARD
* \date August 2026
*********************************************************************/
#include <Engine/Sasquatch/Sasquatch.hpp>
#include <Engine/EngineAssertHandler.hpp>
#include <algorithm>
#include <cstring>
namespace Bigfoot
{
#if defined(BIGFOOT_TOOLMODE) || defined(BIGFOOT_BUILD_TESTS) || defined(BIGFOOT_BUILD_BENCHMARKS)
SasquatchWriter::SasquatchWriter(const File& p_file,
const std::uint64_t p_assetCount,
const std::size_t p_totalFlatbufferBytes,
const std::size_t p_totalHardReferencesBytes):
m_assetCount(p_assetCount),
m_currentDataOffset(sizeof(SasquatchHeader) + (m_assetCount * sizeof(SasquatchEntry))),
m_file(p_file,
sizeof(SasquatchHeader) + (m_assetCount * sizeof(SasquatchEntry)) + p_totalFlatbufferBytes +
p_totalHardReferencesBytes)
{
m_entries.reserve(m_assetCount);
SasquatchHeader header;
header.m_assetCount = m_assetCount;
std::memcpy(m_file.GetData().data(), &header, sizeof(SasquatchHeader));
}
/****************************************************************************************/
SasquatchWriter::~SasquatchWriter()
{
ASSERT(EngineAssertHandler,
m_entries.size() == m_assetCount,
"SasquatchWriter expected {} assets, only {} were added",
m_assetCount,
m_entries.size());
m_file.Prefetch(sizeof(SasquatchHeader), m_entries.size() * sizeof(SasquatchEntry));
if (!m_entries.empty())
{
std::memcpy(m_file.GetData().data() + sizeof(SasquatchHeader),
m_entries.data(),
m_entries.size() * sizeof(SasquatchEntry));
}
}
/****************************************************************************************/
void SasquatchWriter::WriteAsset(const UUID& p_uuid,
eastl::span<const std::byte> p_flatbuffer,
eastl::span<const UUID> p_hardReferences)
{
ASSERT(EngineAssertHandler,
m_entries.size() < m_assetCount,
"SasquatchWriter can only hold {} assets",
m_assetCount);
const std::size_t offset = m_currentDataOffset;
const eastl::span<std::byte> data = m_file.GetData();
ASSERT(EngineAssertHandler,
offset + p_flatbuffer.size() + (p_hardReferences.size() * UUID::UUID_BYTE_SIZE) <= data.size(),
"SasquatchWriter can only write {} bytes",
data.size());
const std::size_t writeEnd = offset + p_flatbuffer.size() + (p_hardReferences.size() * UUID::UUID_BYTE_SIZE);
if (writeEnd > m_prefetchedUpTo)
{
const std::size_t prefetchEnd = std::min(writeEnd + PREFETCH_BYTES, data.size());
m_file.Prefetch(m_prefetchedUpTo, prefetchEnd - m_prefetchedUpTo);
m_prefetchedUpTo = prefetchEnd;
}
SasquatchEntry entry;
std::memcpy(entry.m_uuid.data(), p_uuid.ToBytes().data(), UUID::UUID_BYTE_SIZE);
entry.m_assetOffset = offset;
entry.m_assetSize = p_flatbuffer.size();
entry.m_hardReferencesOffset = entry.m_assetOffset + entry.m_assetSize;
entry.m_hardReferencesSize = p_hardReferences.size() * UUID::UUID_BYTE_SIZE;
std::memcpy(data.data() + entry.m_assetOffset, p_flatbuffer.data(), entry.m_assetSize);
for (std::uint32_t i = 0; i < p_hardReferences.size(); ++i)
{
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawUUID = p_hardReferences[i].ToBytes();
std::memcpy(data.data() + entry.m_hardReferencesOffset + (static_cast<std::size_t>(i) * UUID::UUID_BYTE_SIZE),
rawUUID.data(),
UUID::UUID_BYTE_SIZE);
}
m_currentDataOffset += entry.m_assetSize + entry.m_hardReferencesSize;
m_entries.push_back(entry);
}
/****************************************************************************************/
Sasquatch::Sasquatch(const File& p_file):
m_file(p_file)
{
const eastl::span<const std::byte> data = m_file.GetData();
CRITICAL_ASSERT(EngineAssertHandler,
data.size() >= sizeof(SasquatchHeader),
"Sasquatch file {} is smaller than its header",
p_file.GetPath());
std::memcpy(&m_header, data.data(), sizeof(SasquatchHeader));
CRITICAL_ASSERT(EngineAssertHandler,
m_header.m_magic == SasquatchHeader::MAGIC,
"Sasquatch file {} has an invalid magic number (got {} / expected {})",
p_file.GetPath(),
m_header.m_magic,
SasquatchHeader::MAGIC);
CRITICAL_ASSERT(EngineAssertHandler,
m_header.m_version == SasquatchHeader::VERSION,
"Sasquatch file {} has an unsupported version (got {} / expected {})",
p_file.GetPath(),
m_header.m_version,
SasquatchHeader::VERSION);
CRITICAL_ASSERT(EngineAssertHandler,
data.size() >= sizeof(SasquatchHeader) + (m_header.m_assetCount * sizeof(SasquatchEntry)),
"Sasquatch file {} index is truncated (got {} / expected at least {})",
p_file.GetPath(),
data.size(),
sizeof(SasquatchHeader) + (m_header.m_assetCount * sizeof(SasquatchEntry)));
eastl::vector<SasquatchEntry> entries(m_header.m_assetCount);
if (!entries.empty())
{
std::memcpy(entries.data(), data.data() + sizeof(SasquatchHeader), entries.size() * sizeof(SasquatchEntry));
}
m_entries.reserve(m_header.m_assetCount);
for (const SasquatchEntry& entry: entries)
{
CRITICAL_ASSERT(EngineAssertHandler,
entry.m_assetOffset + entry.m_assetSize + entry.m_hardReferencesSize <= data.size(),
"Sasquatch file {} has an out-of-bounds asset entry",
p_file.GetPath());
m_entries.emplace(UUID {entry.m_uuid},
Entry {.m_assetOffset = entry.m_assetOffset,
.m_assetSize = entry.m_assetSize,
.m_hardReferencesOffset = entry.m_hardReferencesOffset,
.m_hardReferencesSize = entry.m_hardReferencesSize});
}
}
/****************************************************************************************/
const SasquatchHeader& Sasquatch::GetHeader() const
{
return m_header;
}
/****************************************************************************************/
eastl::vector<std::byte> Sasquatch::GetAssetFlatbuffer(const UUID& p_uuid) const
{
const auto entryIt = m_entries.find(p_uuid);
if (entryIt == m_entries.end())
{
return {};
}
const eastl::span<const std::byte> data = m_file.GetData();
const eastl::span<const std::byte> bytes {data.data() + entryIt->second.m_assetOffset,
static_cast<std::size_t>(entryIt->second.m_assetSize)};
return eastl::vector<std::byte> {bytes.begin(), bytes.end()};
}
/****************************************************************************************/
eastl::vector<eastl::vector<std::byte>> Sasquatch::GetAssetFlatbuffers(const eastl::span<const UUID> p_uuids) const
{
eastl::vector<eastl::vector<std::byte>> flatbuffers;
flatbuffers.reserve(p_uuids.size());
for (const UUID& uuid: p_uuids)
{
flatbuffers.push_back(GetAssetFlatbuffer(uuid));
}
return flatbuffers;
}
/****************************************************************************************/
eastl::vector<UUID> Sasquatch::GetAssetHardReferences(const UUID& p_uuid) const
{
eastl::vector<UUID> hardReferences;
const auto entryIt = m_entries.find(p_uuid);
if (entryIt == m_entries.end())
{
return hardReferences;
}
const eastl::span<const std::byte> data = m_file.GetData();
const eastl::span<const std::byte> bytes {data.data() + entryIt->second.m_hardReferencesOffset,
static_cast<std::size_t>(entryIt->second.m_hardReferencesSize)};
const std::size_t hardReferenceCount = bytes.size() / UUID::UUID_BYTE_SIZE;
hardReferences.reserve(hardReferenceCount);
for (std::size_t i = 0; i < hardReferenceCount; ++i)
{
std::array<std::byte, UUID::UUID_BYTE_SIZE> rawUUID;
std::memcpy(rawUUID.data(), bytes.data() + (i * UUID::UUID_BYTE_SIZE), UUID::UUID_BYTE_SIZE);
hardReferences.emplace_back(rawUUID);
}
return hardReferences;
}
/****************************************************************************************/
eastl::vector<eastl::vector<UUID>> Sasquatch::GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const
{
eastl::vector<eastl::vector<UUID>> hardReferences;
hardReferences.reserve(p_uuids.size());
for (const UUID& uuid: p_uuids)
{
hardReferences.push_back(GetAssetHardReferences(uuid));
}
return hardReferences;
}
/****************************************************************************************/
void Sasquatch::SaveAssetFlatbuffer([[maybe_unused]] const UUID& p_uuid,
[[maybe_unused]] eastl::span<const std::byte> p_flatbuffer,
[[maybe_unused]] eastl::span<const UUID> p_hardReferences)
{
ASSERT(EngineAssertHandler, false, "Sasquatch is read-only, it cannot save assets");
}
/****************************************************************************************/
void Sasquatch::SaveAssetFlatbuffers([[maybe_unused]] const eastl::span<const UUID> p_uuids,
[[maybe_unused]] eastl::span<const eastl::vector<std::byte>> p_flatbuffers,
[[maybe_unused]] eastl::span<const eastl::vector<UUID>> p_hardReferences)
{
ASSERT(EngineAssertHandler, false, "Sasquatch is read-only, it cannot save assets");
}
/****************************************************************************************/
bool Sasquatch::Has(const UUID& p_uuid) const
{
return m_entries.contains(p_uuid);
}
#endif
} // namespace Bigfoot