BigFile/Sasquatch #1
+14
-10
@@ -127,22 +127,26 @@ IncludeCategories:
|
||||
Priority: 2
|
||||
- Regex: '^<Utils/'
|
||||
Priority: 3
|
||||
- Regex: '^<EngineTests/'
|
||||
- Regex: '^<SasquatchBaker/'
|
||||
Priority: 4
|
||||
- Regex: '^<SystemTests/'
|
||||
- Regex: '^<EngineTests/'
|
||||
Priority: 5
|
||||
- Regex: '^<UtilsTests/'
|
||||
- Regex: '^<SystemTests/'
|
||||
Priority: 6
|
||||
- Regex: '^<AssetsForTesting/'
|
||||
- Regex: '^<UtilsTests/'
|
||||
Priority: 7
|
||||
- Regex: '^<EngineBenchmarks/'
|
||||
- Regex: '^<AssetsForTesting/'
|
||||
Priority: 8
|
||||
- Regex: '^<AssetsForBenchmarking/'
|
||||
- Regex: '^<SasquatchBakerTests/'
|
||||
Priority: 9
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.h>'
|
||||
- Regex: '^<EngineBenchmarks/'
|
||||
Priority: 10
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.hpp>'
|
||||
Priority: 10
|
||||
- Regex: '^<[a-zA-Z0-9_/]+>'
|
||||
- Regex: '^<AssetsForBenchmarking/'
|
||||
Priority: 11
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.h>'
|
||||
Priority: 12
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.hpp>'
|
||||
Priority: 12
|
||||
- Regex: '^<[a-zA-Z0-9_/]+>'
|
||||
Priority: 13
|
||||
MainIncludeChar: AngleBracket
|
||||
@@ -23,16 +23,14 @@
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
namespace
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
constexpr std::uint32_t ASSET_COUNT = 20'000;
|
||||
constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
class Fixture: public benchmark::Fixture
|
||||
class BigFileFixture: public benchmark::Fixture
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
static constexpr std::uint32_t ASSET_COUNT = 20'000;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
BigFile::Request {m_bigFile, "DELETE FROM Asset;"}.Execute();
|
||||
@@ -63,7 +61,6 @@ class Fixture: public benchmark::Fixture
|
||||
m_bigFile.SaveAssetFlatbuffers(metadatas, flatbuffers);
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> RandomBatch(const std::size_t p_batchSize) const
|
||||
{
|
||||
@@ -94,11 +91,10 @@ class Fixture: public benchmark::Fixture
|
||||
BigFile m_bigFile {File {BIGFILE_ENGINEBENCHMARKS_LOCATION}};
|
||||
eastl::vector<UUID> m_uuids;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
@@ -118,11 +114,11 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer");
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffers)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffers)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
|
||||
@@ -149,7 +145,7 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffers)(benchmark::State& state)
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, GetAssetFlatbuffers)
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffers)
|
||||
->Name("BigFile/GetAssetFlatbuffers")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
@@ -159,7 +155,7 @@ BENCHMARK_REGISTER_F(Fixture, GetAssetFlatbuffers)
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, GetAssetMetadata)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetMetadata)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
@@ -169,11 +165,11 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetMetadata)(benchmark::State& state)
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, GetAssetMetadata)->Name("BigFile/GetAssetMetadata");
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetMetadata)->Name("BigFile/GetAssetMetadata");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, GetAssetMetadatas)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetMetadatas)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
|
||||
@@ -190,7 +186,7 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetMetadatas)(benchmark::State& state)
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, GetAssetMetadatas)
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetMetadatas)
|
||||
->Name("BigFile/GetAssetMetadatas")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
@@ -200,7 +196,7 @@ BENCHMARK_REGISTER_F(Fixture, GetAssetMetadatas)
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffer)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
@@ -226,11 +222,11 @@ BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffer)(benchmark::State& state)
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer");
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffers)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffers)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
std::int64_t bytesProcessed = 0;
|
||||
@@ -265,7 +261,7 @@ BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffers)(benchmark::State& state)
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(Fixture, SaveAssetFlatbuffers)
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffers)
|
||||
->Name("BigFile/SaveAssetFlatbuffers")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
|
||||
@@ -10,3 +10,5 @@ target_link_libraries(${PROJECT_NAME}
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,AssetsForBenchmarking>)
|
||||
|
||||
bigfoot_create_bigfile("Benchmarks/Bigfoot")
|
||||
|
||||
bigfoot_create_sasquatch_info()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// AUTO-GENERATED DO NOT TOUCH
|
||||
|
||||
/*********************************************************************
|
||||
* \file SasquatchInfo.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINEBENCHMARKS_SASQUATCHINFO_GENERATED_HPP
|
||||
#define BIGFOOT_ENGINEBENCHMARKS_SASQUATCHINFO_GENERATED_HPP
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_GAME
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* Sasquatch location
|
||||
*/
|
||||
constexpr eastl::string_view SASQUATCH_ENGINEBENCHMARKS_LOCATION {"./EngineBenchmarks-sasquatch.sasquatch"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
/*********************************************************************
|
||||
* \file Sasquatch.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EngineBenchmarks/SasquatchInfo_generated.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_GAME
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
class SasquatchFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
static constexpr std::uint32_t ASSET_COUNT = 20'000;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}.Delete();
|
||||
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
flatbuffers.reserve(ASSET_COUNT);
|
||||
|
||||
m_uuids.clear();
|
||||
m_uuids.reserve(ASSET_COUNT);
|
||||
|
||||
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
|
||||
{
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString().c_str());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
|
||||
flatbuffers.push_back(asset.Pack());
|
||||
m_uuids.push_back(asset.GetHeader().uuid);
|
||||
}
|
||||
|
||||
eastl::vector<SasquatchData> assets;
|
||||
assets.reserve(ASSET_COUNT);
|
||||
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
|
||||
{
|
||||
assets.push_back(SasquatchData {.m_uuid = m_uuids[i], .m_flatbuffer = flatbuffers[i]});
|
||||
}
|
||||
|
||||
std::uint64_t totalFlatbufferBytes = 0;
|
||||
for (const SasquatchData& asset: assets)
|
||||
{
|
||||
totalFlatbufferBytes += asset.m_flatbuffer.size();
|
||||
}
|
||||
|
||||
{
|
||||
SasquatchWriter writer {File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}, assets.size(), totalFlatbufferBytes};
|
||||
for (const SasquatchData& asset: assets)
|
||||
{
|
||||
writer.WriteAsset(asset.m_uuid, asset.m_flatbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
m_sasquatch.emplace(File {SASQUATCH_ENGINEBENCHMARKS_LOCATION});
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
m_sasquatch.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> RandomBatch(const std::size_t p_batchSize) const
|
||||
{
|
||||
eastl::vector<UUID> batch;
|
||||
batch.reserve(p_batchSize);
|
||||
for (std::size_t i = 0; i < p_batchSize; ++i)
|
||||
{
|
||||
batch.push_back(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
}
|
||||
|
||||
return batch;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::vector<UUID>> RandomBatchPool(const std::size_t p_batchSize,
|
||||
const std::size_t p_poolSize) const
|
||||
{
|
||||
eastl::vector<eastl::vector<UUID>> pool;
|
||||
pool.reserve(p_poolSize);
|
||||
for (std::size_t i = 0; i < p_poolSize; ++i)
|
||||
{
|
||||
pool.push_back(RandomBatch(p_batchSize));
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
eastl::optional<Sasquatch> m_sasquatch;
|
||||
eastl::vector<UUID> m_uuids;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
eastl::optional<eastl::vector<std::byte>> flatbuffer = m_sasquatch->GetAssetFlatbuffer(
|
||||
m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
benchmark::DoNotOptimize(flatbuffer);
|
||||
|
||||
if (flatbuffer.has_value())
|
||||
{
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer->size());
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffer)->Name("Sasquatch/GetAssetFlatbuffer");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetFlatbuffers)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
|
||||
constexpr std::size_t BATCH_POOL_SIZE = 64;
|
||||
const eastl::vector<eastl::vector<UUID>> batchPool = RandomBatchPool(batchSize, BATCH_POOL_SIZE);
|
||||
|
||||
std::int64_t bytesProcessed = 0;
|
||||
std::size_t batchIndex = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers = m_sasquatch->GetAssetFlatbuffers(batchPool[batchIndex]);
|
||||
benchmark::DoNotOptimize(flatbuffers);
|
||||
|
||||
for (const eastl::vector<std::byte>& flatbuffer: flatbuffers)
|
||||
{
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
|
||||
}
|
||||
|
||||
batchIndex = (batchIndex + 1) % BATCH_POOL_SIZE;
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffers)
|
||||
->Name("Sasquatch/GetAssetFlatbuffers")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096);
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -11,10 +11,7 @@
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
} // namespace
|
||||
|
||||
struct MyComplexStruct
|
||||
{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/System)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
|
||||
if(${TOOLMODE})
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Tools)
|
||||
endif()
|
||||
@@ -0,0 +1,214 @@
|
||||
/*********************************************************************
|
||||
* \file Sasquatch.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_SASQUATCH_SASQUATCH_HPP
|
||||
#define BIGFOOT_ENGINE_SASQUATCH_SASQUATCH_HPP
|
||||
#include <Engine/DataAccessor/DataAccessor.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
#include <System/File/MemoryMappedFile.hpp>
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
struct SasquatchHeader
|
||||
{
|
||||
static constexpr std::uint32_t MAGIC = 0x53515443; // 'SQTC'
|
||||
static constexpr std::uint32_t VERSION = 1;
|
||||
|
||||
std::uint32_t m_magic = MAGIC;
|
||||
std::uint32_t m_version = VERSION;
|
||||
std::uint64_t m_assetCount = 0;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SasquatchHeader) == 16);
|
||||
static_assert(std::is_trivially_copyable_v<SasquatchHeader>);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
struct SasquatchEntry
|
||||
{
|
||||
std::array<std::byte, UUID::UUID_BYTE_SIZE> m_uuid {};
|
||||
std::size_t m_offset = 0;
|
||||
std::size_t m_size = 0;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SasquatchEntry) == 32);
|
||||
static_assert(std::is_trivially_copyable_v<SasquatchEntry>);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#if defined(BIGFOOT_TOOLMODE) || defined(BIGFOOT_BUILD_TESTS) || defined(BIGFOOT_BUILD_BENCHMARKS)
|
||||
struct SasquatchData
|
||||
{
|
||||
UUID m_uuid;
|
||||
eastl::span<const std::byte> m_flatbuffer;
|
||||
};
|
||||
|
||||
class SasquatchWriter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* Writes the header
|
||||
*
|
||||
* \param p_file The file to write
|
||||
* \param p_assetCount The number of assets that will be written
|
||||
* \param p_totalFlatbufferBytes The sum of the sizes of every flatbuffer that will be written
|
||||
*/
|
||||
SasquatchWriter(const File& p_file, std::uint64_t p_assetCount, std::size_t p_totalFlatbufferBytes);
|
||||
|
||||
SasquatchWriter(const SasquatchWriter& p_writer) = delete;
|
||||
SasquatchWriter(SasquatchWriter&& p_writer) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* Writes the entries
|
||||
*
|
||||
*/
|
||||
~SasquatchWriter();
|
||||
|
||||
/**
|
||||
* Writes an asset to the file.
|
||||
*
|
||||
* \param p_uuid The UUID of the asset
|
||||
* \param p_flatbuffer The flatbuffer of the asset
|
||||
*/
|
||||
void WriteAsset(const UUID& p_uuid, eastl::span<const std::byte> p_flatbuffer);
|
||||
|
||||
SasquatchWriter& operator=(const SasquatchWriter& p_writer) = delete;
|
||||
SasquatchWriter& operator=(SasquatchWriter&& p_writer) = delete;
|
||||
|
||||
private:
|
||||
static constexpr std::uint64_t PREFETCH_BYTES = 4 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* The number of assets expected
|
||||
*/
|
||||
std::uint64_t m_assetCount;
|
||||
|
||||
/**
|
||||
* The offset the next WriteAsset call will write to
|
||||
*/
|
||||
std::size_t m_nextOffset;
|
||||
|
||||
/**
|
||||
* How far into m_file has already been prefetched
|
||||
*/
|
||||
std::size_t m_prefetchedUpTo;
|
||||
|
||||
/**
|
||||
* The Sasquatch file being written, mapped at its final size
|
||||
*/
|
||||
WritableMemoryMappedFile m_file;
|
||||
|
||||
/**
|
||||
* The entries
|
||||
*/
|
||||
eastl::vector<SasquatchEntry> m_entries;
|
||||
};
|
||||
#endif
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#ifdef BIGFOOT_GAME
|
||||
class Sasquatch: public DataAccessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param p_file The sasquatch file
|
||||
*/
|
||||
Sasquatch(const File& p_file);
|
||||
|
||||
Sasquatch(const Sasquatch& p_sasquatch) = delete;
|
||||
Sasquatch(Sasquatch&& p_sasquatch) = delete;
|
||||
|
||||
~Sasquatch() override = default;
|
||||
|
||||
/**
|
||||
* Get an Asset flatbuffer
|
||||
*
|
||||
* \param p_uuid The asset to get the flatbuffer of
|
||||
* \return The flatbuffer, or nullopt if the asset was not found
|
||||
*/
|
||||
eastl::optional<eastl::vector<std::byte>> GetAssetFlatbuffer(const UUID& p_uuid) const override;
|
||||
|
||||
/**
|
||||
* Get several Asset flatbuffers
|
||||
*
|
||||
* \param p_uuids The assets to get the flatbuffers of
|
||||
* \return The flatbuffers that were found
|
||||
*/
|
||||
eastl::vector<eastl::vector<std::byte>> GetAssetFlatbuffers(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
/**
|
||||
* Sasquatch only serves flatbuffers: this will always fail
|
||||
*
|
||||
*/
|
||||
eastl::optional<AssetMetadata> GetAssetMetadata(const UUID& p_uuid) const override;
|
||||
|
||||
/**
|
||||
* Sasquatch only serves flatbuffers: this will always fail
|
||||
*
|
||||
*/
|
||||
eastl::vector<AssetMetadata> GetAssetMetadatas(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
/**
|
||||
* Sasquatch is read-only: this will always fail
|
||||
*
|
||||
*/
|
||||
void SaveAssetFlatbuffer(const AssetMetadata& p_metadata, eastl::span<const std::byte> p_flatbuffer) override;
|
||||
|
||||
/**
|
||||
* Sasquatch is read-only: this will always fail
|
||||
*
|
||||
*/
|
||||
void SaveAssetFlatbuffers(eastl::span<const AssetMetadata> p_metadatas,
|
||||
eastl::span<const eastl::vector<std::byte>> p_flatbuffers) override;
|
||||
|
||||
/**
|
||||
* Check if the Asset exists
|
||||
*
|
||||
* \param p_uuid The asset to check
|
||||
* \param True if it exists, false otherwise
|
||||
*/
|
||||
bool Has(const UUID& p_uuid) const override;
|
||||
|
||||
Sasquatch& operator=(const Sasquatch& p_sasquatch) = delete;
|
||||
Sasquatch& operator=(Sasquatch&& p_sasquatch) = delete;
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
std::size_t m_offset;
|
||||
std::size_t m_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Sasquatch file
|
||||
*/
|
||||
MemoryMappedFile m_file;
|
||||
|
||||
/**
|
||||
* UUID to entry
|
||||
*/
|
||||
ankerl::unordered_dense::map<UUID, Entry> m_entries;
|
||||
};
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
@AUTO_GENERATED_COMMENT@
|
||||
|
||||
/*********************************************************************
|
||||
* \file SasquatchInfo.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_@SASQUATCH_NAME@_SASQUATCHINFO_GENERATED_HPP
|
||||
#define BIGFOOT_@SASQUATCH_NAME@_SASQUATCHINFO_GENERATED_HPP
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_GAME
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* Sasquatch location
|
||||
*/
|
||||
constexpr eastl::string_view SASQUATCH_@SASQUATCH_NAME@_LOCATION {"@SASQUATCH_LOCATION@"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,232 @@
|
||||
/*********************************************************************
|
||||
* \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):
|
||||
m_assetCount(p_assetCount),
|
||||
m_nextOffset(sizeof(SasquatchHeader) + p_assetCount * sizeof(SasquatchEntry)),
|
||||
m_prefetchedUpTo(0),
|
||||
m_file(p_file, m_nextOffset + p_totalFlatbufferBytes)
|
||||
{
|
||||
m_entries.reserve(p_assetCount);
|
||||
|
||||
SasquatchHeader header;
|
||||
header.m_assetCount = p_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));
|
||||
|
||||
std::memcpy(m_file.GetData().data() + sizeof(SasquatchHeader),
|
||||
m_entries.data(),
|
||||
m_entries.size() * sizeof(SasquatchEntry));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void SasquatchWriter::WriteAsset(const UUID& p_uuid, const eastl::span<const std::byte> p_flatbuffer)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
m_entries.size() < m_assetCount,
|
||||
"SasquatchWriter can only hold {} assets",
|
||||
m_assetCount);
|
||||
|
||||
const eastl::span<std::byte> data = m_file.GetData();
|
||||
ASSERT(EngineAssertHandler,
|
||||
m_nextOffset + p_flatbuffer.size() <= data.size(),
|
||||
"SasquatchWriter can only write {} bytes",
|
||||
data.size());
|
||||
|
||||
const std::size_t writeEnd = m_nextOffset + p_flatbuffer.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_offset = m_nextOffset;
|
||||
entry.m_size = p_flatbuffer.size();
|
||||
m_entries.push_back(entry);
|
||||
|
||||
std::memcpy(data.data() + m_nextOffset, p_flatbuffer.data(), p_flatbuffer.size());
|
||||
m_nextOffset += entry.m_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#ifdef BIGFOOT_GAME
|
||||
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());
|
||||
|
||||
SasquatchHeader header;
|
||||
std::memcpy(&header, data.data(), sizeof(SasquatchHeader));
|
||||
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
header.m_magic == SasquatchHeader::MAGIC,
|
||||
"Sasquatch file {} has an invalid magic number (got {} / expected {})",
|
||||
p_file.GetPath(),
|
||||
header.m_magic,
|
||||
SasquatchHeader::MAGIC);
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
header.m_version == SasquatchHeader::VERSION,
|
||||
"Sasquatch file {} has an unsupported version (got {} / expected {})",
|
||||
p_file.GetPath(),
|
||||
header.m_version,
|
||||
SasquatchHeader::VERSION);
|
||||
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
data.size() >= sizeof(SasquatchHeader) + header.m_assetCount * sizeof(SasquatchEntry),
|
||||
"Sasquatch file {} index is truncated (got {} / expected at least {})",
|
||||
p_file.GetPath(),
|
||||
data.size(),
|
||||
sizeof(SasquatchHeader) + header.m_assetCount * sizeof(SasquatchEntry));
|
||||
|
||||
eastl::vector<SasquatchEntry> entries(header.m_assetCount);
|
||||
std::memcpy(entries.data(), data.data() + sizeof(SasquatchHeader), entries.size() * sizeof(SasquatchEntry));
|
||||
|
||||
m_entries.reserve(header.m_assetCount);
|
||||
for (const SasquatchEntry& entry: entries)
|
||||
{
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
entry.m_offset + entry.m_size <= data.size(),
|
||||
"Sasquatch file {} has an out-of-bounds asset entry",
|
||||
p_file.GetPath());
|
||||
|
||||
m_entries.emplace(UUID {entry.m_uuid}, Entry {.m_offset = entry.m_offset, .m_size = entry.m_size});
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::optional<eastl::vector<std::byte>> Sasquatch::GetAssetFlatbuffer(const UUID& p_uuid) const
|
||||
{
|
||||
const auto it = m_entries.find(p_uuid);
|
||||
if (it == m_entries.end())
|
||||
{
|
||||
return eastl::nullopt;
|
||||
}
|
||||
|
||||
const eastl::span<const std::byte> data = m_file.GetData();
|
||||
const eastl::span<const std::byte> bytes {data.data() + it->second.m_offset,
|
||||
static_cast<std::size_t>(it->second.m_size)};
|
||||
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;
|
||||
if (p_uuids.empty())
|
||||
{
|
||||
return flatbuffers;
|
||||
}
|
||||
|
||||
flatbuffers.reserve(p_uuids.size());
|
||||
|
||||
if (p_uuids.size() == 1)
|
||||
{
|
||||
if (eastl::optional<eastl::vector<std::byte>> flatbuffer = GetAssetFlatbuffer(p_uuids[0]);
|
||||
flatbuffer.has_value())
|
||||
{
|
||||
flatbuffers.push_back(std::move(*flatbuffer));
|
||||
}
|
||||
|
||||
return flatbuffers;
|
||||
}
|
||||
|
||||
ankerl::unordered_dense::set<UUID> seen;
|
||||
seen.reserve(p_uuids.size());
|
||||
for (const UUID& uuid: p_uuids)
|
||||
{
|
||||
if (!seen.insert(uuid).second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto it = m_entries.find(uuid); it != m_entries.end())
|
||||
{
|
||||
const eastl::span<const std::byte> data = m_file.GetData();
|
||||
const eastl::span<const std::byte> bytes {data.data() + it->second.m_offset,
|
||||
static_cast<std::size_t>(it->second.m_size)};
|
||||
flatbuffers.emplace_back(bytes.begin(), bytes.end());
|
||||
}
|
||||
}
|
||||
|
||||
return flatbuffers;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::optional<AssetMetadata> Sasquatch::GetAssetMetadata(const UUID&) const
|
||||
{
|
||||
ASSERT(EngineAssertHandler, false, "Sasquatch does not support metadata queries, only flatbuffers");
|
||||
return eastl::nullopt;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<AssetMetadata> Sasquatch::GetAssetMetadatas(const eastl::span<const UUID>) const
|
||||
{
|
||||
ASSERT(EngineAssertHandler, false, "Sasquatch does not support metadata queries, only flatbuffers");
|
||||
return {};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void Sasquatch::SaveAssetFlatbuffer(const AssetMetadata&, eastl::span<const std::byte>)
|
||||
{
|
||||
ASSERT(EngineAssertHandler, false, "Sasquatch is read-only, it cannot save assets");
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void Sasquatch::SaveAssetFlatbuffers(eastl::span<const AssetMetadata>, eastl::span<const eastl::vector<std::byte>>)
|
||||
{
|
||||
ASSERT(EngineAssertHandler, false, "Sasquatch is read-only, it cannot save assets");
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool Sasquatch::Has(const UUID& p_uuid) const
|
||||
{
|
||||
return m_entries.find(p_uuid) != m_entries.end();
|
||||
}
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/SasquatchBaker)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Executables/SasquatchBaker)
|
||||
@@ -0,0 +1,33 @@
|
||||
project(SasquatchBakerCLI)
|
||||
|
||||
add_executable(${PROJECT_NAME})
|
||||
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
BigfootCompileAndLinkFlags
|
||||
bfg::lyra
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,SasquatchBaker>)
|
||||
|
||||
file(GLOB_RECURSE _SOURCES
|
||||
CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE _HEADERS
|
||||
CONFIGURE_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
|
||||
)
|
||||
|
||||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${_SOURCES}
|
||||
${_HEADERS}
|
||||
)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src FILES ${_SOURCES} ${_HEADERS})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Bigfoot/Tools/Executables)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME SasquatchBaker)
|
||||
@@ -0,0 +1,49 @@
|
||||
/*********************************************************************
|
||||
* \file main.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
#include <Utils/TargetMacros.h>
|
||||
|
||||
#include <SasquatchBaker/SasquatchBaker.hpp>
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
#include <lyra/lyra.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
BIGFOOT_NOT_OPTIMIZED_ONLY(::Bigfoot::Singleton<::Bigfoot::Log>::Lifetime logLifetime);
|
||||
|
||||
bool showHelp = false;
|
||||
std::string bigFilePath;
|
||||
std::string outputPath;
|
||||
|
||||
auto cli = lyra::help(showHelp) |
|
||||
lyra::opt(bigFilePath, "bigfile")["-b"]["--bigfile"]("The BigFile to bake (must exist)").required() |
|
||||
lyra::opt(outputPath, "output")["-o"]["--output"]("The Sasquatch file to write").required();
|
||||
|
||||
const lyra::parse_result result = cli.parse({argc, argv});
|
||||
if (!result)
|
||||
{
|
||||
std::cerr << result.message() << "\n\n" << cli << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
std::cout << cli << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Bigfoot::BigFile bigFile {Bigfoot::File {eastl::string_view {bigFilePath.c_str()}}};
|
||||
Bigfoot::Bake(bigFile, Bigfoot::File {eastl::string_view {outputPath.c_str()}});
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName})
|
||||
|
||||
set(PublicDependencies)
|
||||
set(PrivateDependencies)
|
||||
set(BigfootPublicDependencies
|
||||
Engine)
|
||||
set(BigfootPrivateDependencies
|
||||
Utils)
|
||||
|
||||
bigfoot_create_package_lib(
|
||||
"${PublicDependencies}"
|
||||
"${PrivateDependencies}"
|
||||
"${BigfootPublicDependencies}"
|
||||
"${BigfootPrivateDependencies}"
|
||||
"Tools")
|
||||
|
||||
bigfoot_create_logger()
|
||||
@@ -0,0 +1,24 @@
|
||||
/*********************************************************************
|
||||
* \file SasquatchBaker.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_TOOLS_SASQUATCHBAKER_SASQUATCHBAKER_HPP
|
||||
#define BIGFOOT_TOOLS_SASQUATCHBAKER_SASQUATCHBAKER_HPP
|
||||
#include <Engine/BigFile/BigFile.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/**
|
||||
* Bake every Asset of a BigFile into a Sasquatch file
|
||||
*
|
||||
* \param p_bigFile The BigFile to bake
|
||||
* \param p_outputFile The file to write to
|
||||
*/
|
||||
void Bake(const BigFile& p_bigFile, const File& p_outputFile);
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*********************************************************************
|
||||
* \file SasquatchBakerAssertHandler.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_TOOLS_SASQUATCHBAKER_SASQUATCHBAKERASSERTHANDLER_HPP
|
||||
#define BIGFOOT_TOOLS_SASQUATCHBAKER_SASQUATCHBAKERASSERTHANDLER_HPP
|
||||
#include <Utils/Assert/AssertHandler.hpp>
|
||||
|
||||
#include <SasquatchBaker/SasquatchBakerLogger_generated.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
using SasquatchBakerAssertHandler = AssertHandler<SASQUATCHBAKER_LOGGER>;
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// AUTO-GENERATED DO NOT TOUCH
|
||||
|
||||
/*********************************************************************
|
||||
* \file SasquatchBakerLogger.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SASQUATCHBAKERLOGGER_GENERATED_HPP
|
||||
#define BIGFOOT_SASQUATCHBAKERLOGGER_GENERATED_HPP
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* Logger
|
||||
*/
|
||||
inline Log::LoggerInfo SASQUATCHBAKER_LOGGER {"SASQUATCHBAKER_LOGGER", Flat::LogLevel::Trace};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*********************************************************************
|
||||
* \file SasquatchBaker.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <SasquatchBaker/SasquatchBaker.hpp>
|
||||
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <SasquatchBaker/SasquatchBakerAssertHandler.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
void Bake(const BigFile& p_bigFile, const File& p_outputFile)
|
||||
{
|
||||
std::uint64_t assetCount = 0;
|
||||
{
|
||||
BigFile::Request countRequest {p_bigFile, "SELECT COUNT(*) FROM Asset;"};
|
||||
[[maybe_unused]]
|
||||
const bool hasCount = countRequest.Step();
|
||||
ASSERT(SasquatchBakerAssertHandler, hasCount, "Failed to count the assets of the BigFile");
|
||||
assetCount = countRequest.Get(0);
|
||||
}
|
||||
|
||||
std::uint64_t totalFlatbufferBytes = 0;
|
||||
{
|
||||
BigFile::Request sizeRequest {p_bigFile, "SELECT COALESCE(SUM(LENGTH(Asset)), 0) FROM Asset;"};
|
||||
[[maybe_unused]]
|
||||
const bool hasSize = sizeRequest.Step();
|
||||
ASSERT(SasquatchBakerAssertHandler, hasSize, "Failed to sum the asset sizes of the BigFile");
|
||||
totalFlatbufferBytes = sizeRequest.Get(0);
|
||||
}
|
||||
|
||||
SasquatchWriter writer {p_outputFile, assetCount, totalFlatbufferBytes};
|
||||
|
||||
BigFile::Request request {p_bigFile, "SELECT UUID, Asset FROM Asset;"};
|
||||
while (request.Step())
|
||||
{
|
||||
const eastl::span<const std::byte> uuidBytes = request.Get(0);
|
||||
const eastl::span<const std::byte> flatbuffer = request.Get(1);
|
||||
|
||||
ASSERT(SasquatchBakerAssertHandler,
|
||||
uuidBytes.size() == UUID::UUID_BYTE_SIZE,
|
||||
"Unexpected UUID blob size in BigFile (got {} / expected {})",
|
||||
uuidBytes.size(),
|
||||
UUID::UUID_BYTE_SIZE);
|
||||
|
||||
writer.WriteAsset(
|
||||
UUID {{std::span<const std::byte, UUID::UUID_BYTE_SIZE> {uuidBytes.data(), UUID::UUID_BYTE_SIZE}}},
|
||||
flatbuffer);
|
||||
}
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -2,4 +2,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Misc)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/System)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
|
||||
if(${TOOLMODE})
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Tools)
|
||||
endif()
|
||||
@@ -10,4 +10,6 @@ bigfoot_create_package_tests(
|
||||
"${BigfootDependencies}"
|
||||
"")
|
||||
|
||||
bigfoot_create_bigfile("Tests/Bigfoot")
|
||||
bigfoot_create_bigfile("Tests/Bigfoot")
|
||||
|
||||
bigfoot_create_sasquatch_info()
|
||||
@@ -0,0 +1,22 @@
|
||||
// AUTO-GENERATED DO NOT TOUCH
|
||||
|
||||
/*********************************************************************
|
||||
* \file SasquatchInfo.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINETESTS_SASQUATCHINFO_GENERATED_HPP
|
||||
#define BIGFOOT_ENGINETESTS_SASQUATCHINFO_GENERATED_HPP
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_GAME
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* Sasquatch location
|
||||
*/
|
||||
constexpr eastl::string_view SASQUATCH_ENGINETESTS_LOCATION {"./EngineTests-sasquatch.sasquatch"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*********************************************************************
|
||||
* \file Sasquatch.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <EngineTests/SasquatchInfo_generated.hpp>
|
||||
|
||||
#include <AssetsForTesting/Asset/AssetA.hpp>
|
||||
#include <AssetsForTesting/Asset/AssetB.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_GAME
|
||||
class SasquatchFixture: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
File {SASQUATCH_ENGINETESTS_LOCATION}.Delete();
|
||||
|
||||
m_assetA.SetName("hello");
|
||||
m_flatbufferA = m_assetA.Pack();
|
||||
|
||||
m_assetB.SetName("world");
|
||||
m_flatbufferB = m_assetB.Pack();
|
||||
|
||||
const eastl::vector<SasquatchData> assets {
|
||||
SasquatchData {.m_uuid = m_assetA.GetHeader().uuid, .m_flatbuffer = m_flatbufferA},
|
||||
SasquatchData {.m_uuid = m_assetB.GetHeader().uuid, .m_flatbuffer = m_flatbufferB}};
|
||||
|
||||
std::uint64_t totalFlatbufferBytes = 0;
|
||||
for (const SasquatchData& asset: assets)
|
||||
{
|
||||
totalFlatbufferBytes += asset.m_flatbuffer.size();
|
||||
}
|
||||
|
||||
{
|
||||
SasquatchWriter writer {File {SASQUATCH_ENGINETESTS_LOCATION}, assets.size(), totalFlatbufferBytes};
|
||||
for (const SasquatchData& asset: assets)
|
||||
{
|
||||
writer.WriteAsset(asset.m_uuid, asset.m_flatbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
m_sasquatch.emplace(File {SASQUATCH_ENGINETESTS_LOCATION});
|
||||
}
|
||||
|
||||
AssetA m_assetA;
|
||||
AssetB m_assetB;
|
||||
|
||||
eastl::vector<std::byte> m_flatbufferA;
|
||||
eastl::vector<std::byte> m_flatbufferB;
|
||||
|
||||
eastl::optional<Sasquatch> m_sasquatch;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, GetAssetFlatbuffer_ShouldReturnNulloptForNonExistentUUID)
|
||||
{
|
||||
EXPECT_FALSE(m_sasquatch->GetAssetFlatbuffer(UUID {}).has_value());
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, GetAssetFlatbuffer_ShouldGetTheAssetFlatbuffer)
|
||||
{
|
||||
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer = m_sasquatch->GetAssetFlatbuffer(
|
||||
m_assetA.GetHeader().uuid);
|
||||
ASSERT_TRUE(savedFlatbuffer.has_value());
|
||||
EXPECT_EQ(*savedFlatbuffer, m_flatbufferA);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, GetAssetFlatbuffers_ShouldOmitUUIDsThatDoNotExist)
|
||||
{
|
||||
const eastl::vector<UUID> uuids {m_assetA.GetHeader().uuid, UUID {}, m_assetB.GetHeader().uuid};
|
||||
const eastl::vector<eastl::vector<std::byte>> flatbuffers = m_sasquatch->GetAssetFlatbuffers(uuids);
|
||||
|
||||
ASSERT_EQ(flatbuffers.size(), 2);
|
||||
EXPECT_TRUE((flatbuffers[0] == m_flatbufferA && flatbuffers[1] == m_flatbufferB) ||
|
||||
(flatbuffers[0] == m_flatbufferB && flatbuffers[1] == m_flatbufferA));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, GetAssetFlatbuffers_ShouldNotDuplicateResultsForADuplicateUUID)
|
||||
{
|
||||
const eastl::vector<UUID> uuids {m_assetA.GetHeader().uuid, m_assetA.GetHeader().uuid};
|
||||
const eastl::vector<eastl::vector<std::byte>> flatbuffers = m_sasquatch->GetAssetFlatbuffers(uuids);
|
||||
|
||||
ASSERT_EQ(flatbuffers.size(), 1);
|
||||
EXPECT_EQ(flatbuffers[0], m_flatbufferA);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, Has_ShouldReturnTrueIfTheAssetExists)
|
||||
{
|
||||
EXPECT_TRUE(m_sasquatch->Has(m_assetA.GetHeader().uuid));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchFixture, Has_ShouldReturnFalseIfTheAssetDoesNotExist)
|
||||
{
|
||||
EXPECT_FALSE(m_sasquatch->Has(UUID {}));
|
||||
}
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/SasquatchBaker)
|
||||
@@ -0,0 +1,13 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName}Tests)
|
||||
|
||||
set(Dependencies)
|
||||
set(BigfootDependencies
|
||||
AssetsForTesting)
|
||||
|
||||
bigfoot_create_package_tests(
|
||||
"${Dependencies}"
|
||||
"${BigfootDependencies}"
|
||||
"Tools")
|
||||
|
||||
bigfoot_create_bigfile("Tests/Tools")
|
||||
@@ -0,0 +1,113 @@
|
||||
/*********************************************************************
|
||||
* \file SasquatchBaker.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <SasquatchBaker/SasquatchBaker.hpp>
|
||||
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <AssetsForTesting/Asset/AssetA.hpp>
|
||||
#include <AssetsForTesting/Asset/AssetB.hpp>
|
||||
|
||||
#include <SasquatchBakerTests/BigFileInfo_generated.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class SasquatchBakerFixture: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
BigFile::Request {m_bigFile, "DELETE FROM Asset;"}.Execute();
|
||||
BigFile::Request {m_bigFile, "DELETE FROM AssetMetadata;"}.Execute();
|
||||
m_sasquatch.Delete();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
SasquatchHeader ReadHeader(const eastl::vector<std::byte>& p_data)
|
||||
{
|
||||
SasquatchHeader header;
|
||||
std::memcpy(&header, p_data.data(), sizeof(SasquatchHeader));
|
||||
return header;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
eastl::optional<SasquatchEntry> FindEntry(const eastl::vector<std::byte>& p_data, const UUID& p_uuid)
|
||||
{
|
||||
const SasquatchHeader header = ReadHeader(p_data);
|
||||
|
||||
for (std::uint64_t i = 0; i < header.m_assetCount; ++i)
|
||||
{
|
||||
SasquatchEntry entry;
|
||||
std::memcpy(&entry,
|
||||
p_data.data() + sizeof(SasquatchHeader) + i * sizeof(SasquatchEntry),
|
||||
sizeof(SasquatchEntry));
|
||||
|
||||
if (UUID {entry.m_uuid} == p_uuid)
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return eastl::nullopt;
|
||||
}
|
||||
|
||||
static constexpr eastl::string_view SASQUATCH_SASQUATCHBAKERTESTS_LOCATION {
|
||||
"./SasquatchBakerTests-sasquatch.sasquatch"};
|
||||
BigFile m_bigFile {File {BIGFILE_SASQUATCHBAKERTESTS_LOCATION}};
|
||||
File m_sasquatch {SASQUATCH_SASQUATCHBAKERTESTS_LOCATION};
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchBakerFixture, Bake_ShouldWriteAnEmptyFileWhenTheBigFileIsEmpty)
|
||||
{
|
||||
Bake(m_bigFile, m_sasquatch);
|
||||
|
||||
const eastl::vector<std::byte> data = m_sasquatch.Read(0, m_sasquatch.GetSize());
|
||||
ASSERT_EQ(data.size(), sizeof(SasquatchHeader));
|
||||
|
||||
const SasquatchHeader header = ReadHeader(data);
|
||||
EXPECT_EQ(header.m_magic, SasquatchHeader::MAGIC);
|
||||
EXPECT_EQ(header.m_version, SasquatchHeader::VERSION);
|
||||
EXPECT_EQ(header.m_assetCount, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
TEST_F(SasquatchBakerFixture, Bake_ShouldWriteEveryAssetOfTheBigFile)
|
||||
{
|
||||
AssetA assetA;
|
||||
assetA.SetName("hello");
|
||||
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetA.GetHeader().uuid,
|
||||
.m_name = assetA.GetHeader().name,
|
||||
.m_typeID = assetA.GetHeader().type_id,
|
||||
.m_typeName = assetA.GetHeader().type_name},
|
||||
assetA.Pack());
|
||||
|
||||
AssetA assetB;
|
||||
assetB.SetName("world");
|
||||
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetB.GetHeader().uuid,
|
||||
.m_name = assetB.GetHeader().name,
|
||||
.m_typeID = assetB.GetHeader().type_id,
|
||||
.m_typeName = assetB.GetHeader().type_name},
|
||||
assetB.Pack());
|
||||
|
||||
Bake(m_bigFile, m_sasquatch);
|
||||
|
||||
const eastl::vector<std::byte> data = m_sasquatch.Read(0, m_sasquatch.GetSize());
|
||||
const SasquatchHeader header = ReadHeader(data);
|
||||
EXPECT_EQ(header.m_assetCount, 2);
|
||||
|
||||
EXPECT_TRUE(FindEntry(data, assetA.GetHeader().uuid).has_value());
|
||||
EXPECT_TRUE(FindEntry(data, assetB.GetHeader().uuid).has_value());
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,19 @@
|
||||
/*********************************************************************
|
||||
* \file main.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
#include <Utils/TargetMacros.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
BIGFOOT_NOT_OPTIMIZED_ONLY(::Bigfoot::Singleton<::Bigfoot::Log>::Lifetime m_lifetime);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+3
-1
@@ -97,7 +97,9 @@ function(bigfoot_create_package_tests PackageDependencies PackageBigfootDependen
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(${PROJECT_NAME} XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/TestResults/)
|
||||
gtest_discover_tests(${PROJECT_NAME}
|
||||
WORKING_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||
XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/TestResults/)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src/ FILES ${_SOURCES} ${_HEADERS} ${_OTHERS})
|
||||
|
||||
|
||||
@@ -38,6 +38,16 @@ function(bigfoot_create_bigfile ParentFolder)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(bigfoot_create_sasquatch_info)
|
||||
string(TOUPPER ${PROJECT_NAME} SASQUATCH_NAME)
|
||||
set(SASQUATCH_LOCATION "./${PROJECT_NAME}-sasquatch.sasquatch")
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/Bigfoot/Sources/Engine/Include/Engine/Sasquatch/SasquatchInfo_generated.hpp.in
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Include/${PROJECT_NAME}/SasquatchInfo_generated.hpp
|
||||
@ONLY
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(_bigfoot_collect_includes_recursive Target UseAllLinks)
|
||||
get_property(_Visited GLOBAL PROPERTY _BIGFOOT_VISITED_TARGETS)
|
||||
if("${Target}" IN_LIST _Visited)
|
||||
|
||||
Reference in New Issue
Block a user