Game analysis for SonarQube #5
+43
-2
@@ -51,7 +51,7 @@ CompactNamespaces: false
|
||||
Cpp11BracedListStyle: true
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
FixNamespaceComments: true
|
||||
IncludeBlocks: Preserve
|
||||
IncludeBlocks: Regroup
|
||||
IndentCaseBlocks: true
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
@@ -119,4 +119,45 @@ SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceInEmptyBlock: true
|
||||
PenaltyBreakAssignment: 200
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
IncludeCategories:
|
||||
- Regex: '^<Engine/'
|
||||
Priority: 1
|
||||
- Regex: '^<System/'
|
||||
Priority: 2
|
||||
- Regex: '^<Utils/'
|
||||
Priority: 3
|
||||
- Regex: '^<SasquatchBaker/'
|
||||
Priority: 4
|
||||
- Regex: '^<EngineTests/'
|
||||
Priority: 5
|
||||
- Regex: '^<SystemTests/'
|
||||
Priority: 6
|
||||
- Regex: '^<UtilsTests/'
|
||||
Priority: 7
|
||||
- Regex: '^<AssetsForTesting/'
|
||||
Priority: 8
|
||||
- Regex: '^<SasquatchBakerTests/'
|
||||
Priority: 9
|
||||
- Regex: '^<EngineBenchmarks/'
|
||||
Priority: 10
|
||||
- Regex: '^<AssetsForBenchmarking/'
|
||||
Priority: 11
|
||||
- Regex: '^<SasquatchBakerBenchmarks/'
|
||||
Priority: 12
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.h>'
|
||||
Priority: 13
|
||||
- Regex: '^<[a-zA-Z0-9_/]+\.hpp>'
|
||||
Priority: 13
|
||||
- Regex: '^<[a-zA-Z0-9_/]+>'
|
||||
Priority: 14
|
||||
MainIncludeChar: AngleBracket
|
||||
Macros:
|
||||
- 'BIGFOOT_OPTIMIZED_ONLY(x)=x'
|
||||
- 'BIGFOOT_OPTIMIZED_ONLY(x,y)=x,y'
|
||||
- 'BIGFOOT_NOT_OPTIMIZED_ONLY(x)=x'
|
||||
- 'BIGFOOT_NOT_OPTIMIZED_ONLY(x,y)=x,y'
|
||||
- 'BIGFOOT_TOOLMODE_ONLY(x)=x'
|
||||
- 'BIGFOOT_TOOLMODE_ONLY(x,y)=x,y'
|
||||
- 'BIGFOOT_GAME_ONLY(x)=x'
|
||||
- 'BIGFOOT_GAME_ONLY(x,y)=x,y'
|
||||
|
||||
+4
-1
@@ -11,9 +11,12 @@ Checks: >
|
||||
portability-*,
|
||||
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-auto,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-avoid-do-while
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
|
||||
ExcludeHeaderFilterRegex: '_generated.*'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Bigfoot",
|
||||
"image": "git.romainboullard.com/bigfootdev/linuxbigfootbuilder:main"
|
||||
"image": "git.romainboullard.com/bigfootdev/linuxcppbuilder:development"
|
||||
}
|
||||
|
||||
+16
-16
@@ -6,20 +6,22 @@ on:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CCACHE_BASEDIR: ${{ github.workspace }}
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
container:
|
||||
image: git.romainboullard.com/bigfootdev/linuxbigfootbuilder:main
|
||||
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:development
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: ["Debug", "RelWithDebInfo", "Release"]
|
||||
unity_build: ["ON", "OFF"]
|
||||
name: "Build & Test ${{ matrix.build_type }} (Unity Build: ${{ matrix.unity_build }})"
|
||||
conan_profile: ["./ConanProfiles/host/clang", "./ConanProfiles/host/clang_asan"]
|
||||
toolmode:
|
||||
- { value: "True", name: "toolmode" }
|
||||
- { value: "False", name: "game" }
|
||||
name: "Build & Test ${{ matrix.build_type }} with ${{ matrix.conan_profile }} (Unity Build: ${{ matrix.unity_build }}, Variant: ${{ matrix.toolmode.name }})"
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
@@ -29,28 +31,26 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Show ccache stats before
|
||||
run: ccache --zero-stats
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
conan install . --deployer=full_deploy --deployer-folder=build --remote=bigfootpackages -pr:h=clang -pr:b=clang --build=missing -s build_type=${{ matrix.build_type }} -o bigfoot/*:build_tests=True -o bigfoot/*:tracy=False -o bigfoot/*:build_tools=True -o bigfoot/*:vulkan=True -o bigfoot/*:build_benchmarks=True
|
||||
cmake -S . -B ./build/${{ matrix.build_type }} --toolchain ./build/${{ matrix.build_type }}/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_UNITY_BUILD=${{ matrix.unity_build }} -G "Ninja"
|
||||
cmake --build build/${{ matrix.build_type }} --parallel $(nproc)
|
||||
|
||||
- name: Show ccache stats after
|
||||
run: ccache --show-stats
|
||||
conan install . --remote=bigfootpackages -pr:h=${{ matrix.conan_profile }} -pr:b=./ConanProfiles/build/clang --build=missing -s build_type=${{ matrix.build_type }} -o bigfoot/*:build_tests=True -o bigfoot/*:build_benchmarks=True -o bigfoot/*:tracy=False -o bigfoot/*:vulkan=True -o bigfoot/*:toolmode=${{ matrix.toolmode.value }} -of build-${{ matrix.toolmode.name }}
|
||||
. ./build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/conanbuild.sh
|
||||
cmake -S . -B ./build-${{ matrix.toolmode.name }}/${{ matrix.build_type }} --toolchain ./build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_UNITY_BUILD=${{ matrix.unity_build }} -G "Ninja"
|
||||
cmake --build build-${{ matrix.toolmode.name }}/${{ matrix.build_type }} --parallel $(nproc)
|
||||
. ./build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/deactivate_conanbuild.sh
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
cd ./build/${{ matrix.build_type }}
|
||||
cd ./build-${{ matrix.toolmode.name }}/${{ matrix.build_type }}
|
||||
. ../../build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/conanbuild.sh
|
||||
xvfb-run ctest . --output-on-failure
|
||||
. ../../build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/deactivate_conanbuild.sh
|
||||
|
||||
clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
container:
|
||||
image: git.romainboullard.com/bigfootdev/linuxbigfootbuilder:main
|
||||
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:main
|
||||
name: "Clang Format Checks"
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
|
||||
@@ -5,13 +5,18 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- Development
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- Development
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
container:
|
||||
image: git.romainboullard.com/bigfootdev/linuxbigfootbuilder:main
|
||||
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:development
|
||||
|
||||
name: "Sonarqube"
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
@@ -22,26 +27,56 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Generate
|
||||
- name: Build
|
||||
run: |
|
||||
conan install . --deployer=full_deploy --deployer-folder=build --remote=bigfootpackages -pr:h=clang_coverage -pr:b=clang_coverage --build=missing -s build_type=Debug -o bigfoot/*:build_tests=True -o bigfoot/*:tracy=False -o bigfoot/*:build_tools=True -o bigfoot/*:vulkan=True -o bigfoot/*:build_benchmarks=True
|
||||
cmake -S . -B ./build/Debug --toolchain ./build/Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja"
|
||||
cmake --build build/Debug --parallel $(nproc)
|
||||
conan install . --remote=bigfootpackages -pr:h=./ConanProfiles/host/clang_coverage -pr:b=./ConanProfiles/build/clang --build=missing -s build_type=Debug -o bigfoot/*:build_tests=True -o bigfoot/*:build_benchmarks=True -o bigfoot/*:tracy=False -o bigfoot/*:toolmode=True -o bigfoot/*:vulkan=True -of build-toolmode
|
||||
. ./build-toolmode/build/Debug/generators/conanbuild.sh
|
||||
cmake -S . -B ./build-toolmode/Debug --toolchain ./build-toolmode/build/Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja"
|
||||
cmake --build build-toolmode/Debug --parallel $(nproc)
|
||||
. ./build-toolmode/build/Debug/generators/deactivate_conanbuild.sh
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ./build-toolmode/Debug
|
||||
. ../../build-toolmode/build/Debug/generators/conanbuild.sh
|
||||
LLVM_PROFILE_FILE="%p.profraw%c" xvfb-run ctest --output-on-failure
|
||||
. ../../build-toolmode/build/Debug/generators/deactivate_conanbuild.sh
|
||||
|
||||
- name: Clang-Tidy
|
||||
run: run-clang-tidy -p ./build/Debug/ >> tidy_result.txt
|
||||
run: run-clang-tidy -p ./build-toolmode/Debug/ >> tidy_result.txt
|
||||
|
||||
- name: Infer
|
||||
run: infer run --compilation-database build/Debug/compile_commands.json
|
||||
run: infer run --compilation-database build-toolmode/Debug/compile_commands.json
|
||||
|
||||
- name: Generate coverage report
|
||||
shell: bash
|
||||
run: |
|
||||
# gcovr's own LLVM-JSON reader only understands coverage-mapping format
|
||||
# 2.x; this image's clang 22 llvm-cov emits format 3.x, which gcovr
|
||||
# rejects outright ("Wrong major version"). Bypassing gcovr's JSON
|
||||
# path entirely: llvm-cov's lcov exporter is a separate, unaffected
|
||||
# code path, so merge -> export lcov -> convert lcov to Cobertura.
|
||||
llvm-profdata merge -sparse $(find build-toolmode/Debug -name "*.profraw") -o merged.profdata
|
||||
BINARIES=($(find build-toolmode/Debug -maxdepth 8 -type f -executable -iname "*Tests"))
|
||||
OBJECT_ARGS=$(printf -- '-object %s ' "${BINARIES[@]:1}")
|
||||
llvm-cov export -format=lcov -instr-profile=merged.profdata "${BINARIES[0]}" $OBJECT_ARGS > coverage.lcov
|
||||
# lcov_cobertura isn't preinstalled on the image; inject it into
|
||||
# gcovr's existing pipx venv and expose its CLI on PATH.
|
||||
pipx inject gcovr lcov_cobertura --include-apps
|
||||
lcov_cobertura coverage.lcov -b . -o coverage.xml
|
||||
|
||||
- name: SonarQube Scan
|
||||
if: github.head_ref == 'main' || github.ref_name == 'main'
|
||||
uses: SonarSource/sonarqube-scan-action@v7.0.0
|
||||
with:
|
||||
args: >
|
||||
-Dsonar.cxx.jsonCompilationDatabase=./build/Debug/compile_commands.json
|
||||
-Dsonar.verbose=true
|
||||
-Dsonar.cxx.jsonCompilationDatabase=./build-toolmode/Debug/compile_commands.json
|
||||
-Dsonar.qualitygate.wait=true
|
||||
${{ github.event_name == 'pull_request' &&
|
||||
format('-Dsonar.pullrequest.key={0} -Dsonar.pullrequest.branch={1} -Dsonar.pullrequest.base={2} -Dsonar.analysis.sqbot={3}',
|
||||
github.event.pull_request.number, github.head_ref, github.base_ref, github.event.pull_request.head.sha)
|
||||
|| format('-Dsonar.branch.name={0}', github.ref_name) }}
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST }}
|
||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST }}
|
||||
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
[Bb]uild*
|
||||
!ConanProfiles/build/
|
||||
!ConanProfiles/build/**
|
||||
Environment/output
|
||||
*.generated.h
|
||||
|
||||
@@ -18,4 +20,4 @@ graph_info.json
|
||||
|
||||
test_package/Vendor
|
||||
|
||||
graphviz
|
||||
graphviz*
|
||||
@@ -0,0 +1,11 @@
|
||||
/*********************************************************************
|
||||
* \file BenchmarkAsset.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetHelper.hpp>
|
||||
|
||||
BIGFOOT_ASSET(BenchmarkAsset, ::Bigfoot::BenchmarkAsset)
|
||||
@@ -0,0 +1,11 @@
|
||||
/*********************************************************************
|
||||
* \file BenchmarkAssetReferred.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAssetReferred.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetHelper.hpp>
|
||||
|
||||
BIGFOOT_ASSET(BenchmarkAssetReferred, ::Bigfoot::BenchmarkAssetReferred)
|
||||
@@ -0,0 +1,17 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName})
|
||||
|
||||
set(PublicDependencies)
|
||||
set(PrivateDependencies)
|
||||
set(BigfootPublicDependencies)
|
||||
set(BigfootPrivateDependencies
|
||||
Engine)
|
||||
|
||||
bigfoot_create_package_lib(
|
||||
"${PublicDependencies}"
|
||||
"${PrivateDependencies}"
|
||||
"${BigfootPublicDependencies}"
|
||||
"${BigfootPrivateDependencies}"
|
||||
"")
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Benchmarks/Bigfoot)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
include "Engine/Asset/Reference.fbs";
|
||||
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
table BenchmarkAsset
|
||||
{
|
||||
payload: [ubyte];
|
||||
refs: [HardReference] (native_inline, bigfoot_ref: "::Bigfoot::BenchmarkAssetReferred");
|
||||
}
|
||||
|
||||
root_type BenchmarkAsset;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*********************************************************************
|
||||
* \file BenchmarkAsset.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ASSETSFORBENCHMARKING_ASSET_BENCHMARKASSET_HPP
|
||||
#define BIGFOOT_ASSETSFORBENCHMARKING_ASSET_BENCHMARKASSET_HPP
|
||||
#include <Engine/Asset/Asset.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset_generated.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
struct BenchmarkAssetTraits
|
||||
{
|
||||
using FLAT = ::Flat::Bigfoot::BenchmarkAsset;
|
||||
};
|
||||
|
||||
class BenchmarkAsset: public Asset<BenchmarkAssetTraits>
|
||||
{
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
table BenchmarkAssetReferred
|
||||
{
|
||||
}
|
||||
|
||||
root_type BenchmarkAssetReferred;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*********************************************************************
|
||||
* \file BenchmarkAssetReferred.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ASSETSFORBENCHMARKING_ASSET_BENCHMARKASSETREFERRED_HPP
|
||||
#define BIGFOOT_ASSETSFORBENCHMARKING_ASSET_BENCHMARKASSETREFERRED_HPP
|
||||
#include <Engine/Asset/Asset.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAssetReferred_generated.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
struct BenchmarkAssetReferredTraits
|
||||
{
|
||||
using FLAT = ::Flat::Bigfoot::BenchmarkAssetReferred;
|
||||
};
|
||||
|
||||
class BenchmarkAssetReferred: public Asset<BenchmarkAssetReferredTraits>
|
||||
{
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_BENCHMARKASSETREFERRED_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_BENCHMARKASSETREFERRED_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct BenchmarkAssetReferred;
|
||||
struct BenchmarkAssetReferredBuilder;
|
||||
struct BenchmarkAssetReferredT;
|
||||
|
||||
inline const ::flatbuffers::TypeTable *BenchmarkAssetReferredTypeTable();
|
||||
|
||||
struct BenchmarkAssetReferredT : public ::flatbuffers::NativeTable {
|
||||
typedef BenchmarkAssetReferred TableType;
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.BenchmarkAssetReferredT";
|
||||
}
|
||||
};
|
||||
|
||||
struct BenchmarkAssetReferred FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef BenchmarkAssetReferredT NativeTableType;
|
||||
typedef BenchmarkAssetReferredBuilder Builder;
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return BenchmarkAssetReferredTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.BenchmarkAssetReferred";
|
||||
}
|
||||
template <bool B = false>
|
||||
bool Verify(::flatbuffers::VerifierTemplate<B> &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
BenchmarkAssetReferredT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
void UnPackTo(BenchmarkAssetReferredT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
static ::flatbuffers::Offset<BenchmarkAssetReferred> Pack(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetReferredT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
};
|
||||
|
||||
struct BenchmarkAssetReferredBuilder {
|
||||
typedef BenchmarkAssetReferred Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
explicit BenchmarkAssetReferredBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<BenchmarkAssetReferred> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<BenchmarkAssetReferred>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAssetReferred> CreateBenchmarkAssetReferred(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
BenchmarkAssetReferredBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct BenchmarkAssetReferred::Traits {
|
||||
using type = BenchmarkAssetReferred;
|
||||
static auto constexpr Create = CreateBenchmarkAssetReferred;
|
||||
};
|
||||
|
||||
::flatbuffers::Offset<BenchmarkAssetReferred> CreateBenchmarkAssetReferred(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetReferredT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
|
||||
inline BenchmarkAssetReferredT *BenchmarkAssetReferred::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
auto _o = std::make_unique<BenchmarkAssetReferredT>();
|
||||
UnPackTo(_o.get(), _resolver);
|
||||
return _o.release();
|
||||
}
|
||||
|
||||
inline void BenchmarkAssetReferred::UnPackTo(BenchmarkAssetReferredT *_o, const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
(void)_o;
|
||||
(void)_resolver;
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAssetReferred> CreateBenchmarkAssetReferred(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetReferredT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
return BenchmarkAssetReferred::Pack(_fbb, _o, _rehasher);
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAssetReferred> BenchmarkAssetReferred::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetReferredT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
(void)_rehasher;
|
||||
(void)_o;
|
||||
struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const BenchmarkAssetReferredT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
|
||||
return Flat::Bigfoot::CreateBenchmarkAssetReferred(
|
||||
_fbb);
|
||||
}
|
||||
|
||||
inline const ::flatbuffers::TypeTable *BenchmarkAssetReferredTypeTable() {
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_TABLE, 0, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::BenchmarkAssetReferred *GetBenchmarkAssetReferred(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Flat::Bigfoot::BenchmarkAssetReferred>(buf);
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::BenchmarkAssetReferred *GetSizePrefixedBenchmarkAssetReferred(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Flat::Bigfoot::BenchmarkAssetReferred>(buf);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifyBenchmarkAssetReferredBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifyBuffer<Flat::Bigfoot::BenchmarkAssetReferred>(nullptr);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifySizePrefixedBenchmarkAssetReferredBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifySizePrefixedBuffer<Flat::Bigfoot::BenchmarkAssetReferred>(nullptr);
|
||||
}
|
||||
|
||||
inline const char *BenchmarkAssetReferredExtension() {
|
||||
return "bfbs";
|
||||
}
|
||||
|
||||
inline void FinishBenchmarkAssetReferredBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::BenchmarkAssetReferred> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedBenchmarkAssetReferredBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::BenchmarkAssetReferred> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetReferredT> UnPackBenchmarkAssetReferred(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetReferredT>(GetBenchmarkAssetReferred(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetReferredT> UnPackSizePrefixedBenchmarkAssetReferred(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetReferredT>(GetSizePrefixedBenchmarkAssetReferred(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_BENCHMARKASSETREFERRED_FLAT_BIGFOOT_H_
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_BENCHMARKASSET_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_BENCHMARKASSET_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "System/UUID/UUID.hpp"
|
||||
#include "Engine/Asset/Reference.hpp"
|
||||
#include "Engine/Asset/Reference_generated.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct BenchmarkAsset;
|
||||
struct BenchmarkAssetBuilder;
|
||||
struct BenchmarkAssetT;
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
namespace Bigfoot {
|
||||
class BenchmarkAssetReferred;
|
||||
} // namespace Bigfoot
|
||||
|
||||
namespace flatbuffers {
|
||||
Flat::Bigfoot::HardReference PackHardReferenceBenchmarkAssetReferred(const ::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred>& p_asset);
|
||||
::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred> UnPackHardReferenceBenchmarkAssetReferred(const Flat::Bigfoot::HardReference& p_asset);
|
||||
} // namespace flatbuffers
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
inline const ::flatbuffers::TypeTable *BenchmarkAssetTypeTable();
|
||||
|
||||
struct BenchmarkAssetT : public ::flatbuffers::NativeTable {
|
||||
typedef BenchmarkAsset TableType;
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.BenchmarkAssetT";
|
||||
}
|
||||
eastl::vector<uint8_t> payload{};
|
||||
eastl::vector<::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred>> refs{};
|
||||
};
|
||||
|
||||
struct BenchmarkAsset FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef BenchmarkAssetT NativeTableType;
|
||||
typedef BenchmarkAssetBuilder Builder;
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return BenchmarkAssetTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.BenchmarkAsset";
|
||||
}
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PAYLOAD = 4,
|
||||
VT_REFS = 6
|
||||
};
|
||||
const ::flatbuffers::Vector<uint8_t> *payload() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_PAYLOAD);
|
||||
}
|
||||
const ::flatbuffers::Vector<const Flat::Bigfoot::HardReference *> *refs() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<const Flat::Bigfoot::HardReference *> *>(VT_REFS);
|
||||
}
|
||||
template <bool B = false>
|
||||
bool Verify(::flatbuffers::VerifierTemplate<B> &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PAYLOAD) &&
|
||||
verifier.VerifyVector(payload()) &&
|
||||
VerifyOffset(verifier, VT_REFS) &&
|
||||
verifier.VerifyVector(refs()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
BenchmarkAssetT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
void UnPackTo(BenchmarkAssetT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
static ::flatbuffers::Offset<BenchmarkAsset> Pack(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
};
|
||||
|
||||
struct BenchmarkAssetBuilder {
|
||||
typedef BenchmarkAsset Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_payload(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> payload) {
|
||||
fbb_.AddOffset(BenchmarkAsset::VT_PAYLOAD, payload);
|
||||
}
|
||||
void add_refs(::flatbuffers::Offset<::flatbuffers::Vector<const Flat::Bigfoot::HardReference *>> refs) {
|
||||
fbb_.AddOffset(BenchmarkAsset::VT_REFS, refs);
|
||||
}
|
||||
explicit BenchmarkAssetBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<BenchmarkAsset> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<BenchmarkAsset>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAsset> CreateBenchmarkAsset(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> payload = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<const Flat::Bigfoot::HardReference *>> refs = 0) {
|
||||
BenchmarkAssetBuilder builder_(_fbb);
|
||||
builder_.add_refs(refs);
|
||||
builder_.add_payload(payload);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct BenchmarkAsset::Traits {
|
||||
using type = BenchmarkAsset;
|
||||
static auto constexpr Create = CreateBenchmarkAsset;
|
||||
};
|
||||
|
||||
::flatbuffers::Offset<BenchmarkAsset> CreateBenchmarkAsset(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
|
||||
inline BenchmarkAssetT *BenchmarkAsset::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
auto _o = std::make_unique<BenchmarkAssetT>();
|
||||
UnPackTo(_o.get(), _resolver);
|
||||
return _o.release();
|
||||
}
|
||||
|
||||
inline void BenchmarkAsset::UnPackTo(BenchmarkAssetT *_o, const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
(void)_o;
|
||||
(void)_resolver;
|
||||
{ auto _e = payload(); if (_e) { _o->payload.resize(_e->size()); std::copy(_e->begin(), _e->end(), _o->payload.begin()); } }
|
||||
{ auto _e = refs(); if (_e) { _o->refs.resize(_e->size()); for (::flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->refs[_i] = ::flatbuffers::UnPackHardReferenceBenchmarkAssetReferred(*_e->Get(_i)); } } else { _o->refs.resize(0); } }
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAsset> CreateBenchmarkAsset(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
return BenchmarkAsset::Pack(_fbb, _o, _rehasher);
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<BenchmarkAsset> BenchmarkAsset::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const BenchmarkAssetT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
(void)_rehasher;
|
||||
(void)_o;
|
||||
struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const BenchmarkAssetT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
|
||||
auto _payload = _fbb.CreateVector(_o->payload.data(), _o->payload.size());
|
||||
auto _refs = _fbb.CreateVectorOfNativeStructs<Flat::Bigfoot::HardReference, ::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred>>(_o->refs.data(), _o->refs.size(), ::flatbuffers::PackHardReferenceBenchmarkAssetReferred);
|
||||
return Flat::Bigfoot::CreateBenchmarkAsset(
|
||||
_fbb,
|
||||
_payload,
|
||||
_refs);
|
||||
}
|
||||
|
||||
inline const ::flatbuffers::TypeTable *BenchmarkAssetTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_UCHAR, 1, -1 },
|
||||
{ ::flatbuffers::ET_SEQUENCE, 1, 0 }
|
||||
};
|
||||
static const ::flatbuffers::TypeFunction type_refs[] = {
|
||||
Flat::Bigfoot::HardReferenceTypeTable
|
||||
};
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::BenchmarkAsset *GetBenchmarkAsset(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Flat::Bigfoot::BenchmarkAsset>(buf);
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::BenchmarkAsset *GetSizePrefixedBenchmarkAsset(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Flat::Bigfoot::BenchmarkAsset>(buf);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifyBenchmarkAssetBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifyBuffer<Flat::Bigfoot::BenchmarkAsset>(nullptr);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifySizePrefixedBenchmarkAssetBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifySizePrefixedBuffer<Flat::Bigfoot::BenchmarkAsset>(nullptr);
|
||||
}
|
||||
|
||||
inline const char *BenchmarkAssetExtension() {
|
||||
return "bfbs";
|
||||
}
|
||||
|
||||
inline void FinishBenchmarkAssetBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::BenchmarkAsset> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedBenchmarkAssetBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::BenchmarkAsset> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetT> UnPackBenchmarkAsset(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetT>(GetBenchmarkAsset(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetT> UnPackSizePrefixedBenchmarkAsset(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetT>(GetSizePrefixedBenchmarkAsset(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
namespace flatbuffers {
|
||||
#ifndef FLATBUFFERS_BIGFOOT_REF_HARDREFERENCEBENCHMARKASSETREFERRED
|
||||
#define FLATBUFFERS_BIGFOOT_REF_HARDREFERENCEBENCHMARKASSETREFERRED
|
||||
inline Flat::Bigfoot::HardReference PackHardReferenceBenchmarkAssetReferred(const ::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred>& p_asset) { return {Pack(p_asset.GetUUID())}; }
|
||||
inline ::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred> UnPackHardReferenceBenchmarkAssetReferred(const Flat::Bigfoot::HardReference& p_asset) { return {UnPack(p_asset.uuid())}; }
|
||||
#endif // FLATBUFFERS_BIGFOOT_REF_HARDREFERENCEBENCHMARKASSETREFERRED
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_BENCHMARKASSET_FLAT_BIGFOOT_H_
|
||||
@@ -0,0 +1,7 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/AssetsForBenchmarking)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils)
|
||||
if(${TOOLMODE})
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Tools)
|
||||
endif()
|
||||
@@ -0,0 +1,175 @@
|
||||
/*********************************************************************
|
||||
* \file AssetContainer.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Asset/AssetContainer.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
} // namespace
|
||||
|
||||
class AssetContainerFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
[[nodiscard]]
|
||||
static eastl::vector<UUID> InsertRandomAssets(AssetContainer<BenchmarkAsset>& p_container,
|
||||
const std::uint32_t p_count)
|
||||
{
|
||||
eastl::vector<UUID> uuids;
|
||||
uuids.reserve(p_count);
|
||||
for (std::uint32_t i = 0; i < p_count; ++i)
|
||||
{
|
||||
uuids.push_back(p_container.Insert()->GetAsset()->GetHeader().uuid);
|
||||
}
|
||||
Random::shuffle(uuids);
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
AssetContainer<BenchmarkAsset> m_container;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(AssetContainerFixture, Insert)(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
AssetContainer<BenchmarkAsset> container;
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
benchmark::DoNotOptimize(container.Insert());
|
||||
}
|
||||
benchmark::DoNotOptimize(container);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(BenchmarkAsset)));
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AssetContainerFixture, Insert)->Name("AssetContainer/Insert")->Arg(512)->Arg(8192)->Arg(131'072);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(AssetContainerFixture, Get)(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
|
||||
|
||||
eastl::vector<ContainerHandle*> handles;
|
||||
handles.reserve(count);
|
||||
for (const UUID& uuid: uuids)
|
||||
{
|
||||
handles.push_back(m_container.AcquireContainerHandle(uuid));
|
||||
}
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
std::uint64_t sum = 0;
|
||||
for (ContainerHandle* const handle: handles)
|
||||
{
|
||||
if (const auto* const asset = static_cast<const BenchmarkAsset*>(handle->GetAsset()))
|
||||
{
|
||||
sum += asset->GetHeader().type_id;
|
||||
}
|
||||
}
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AssetContainerFixture, Get)->Name("AssetContainer/Get")->Arg(512)->Arg(8192)->Arg(131'072);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(AssetContainerFixture, Has)(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
bool result = true;
|
||||
for (const UUID& uuid: uuids)
|
||||
{
|
||||
result = result && m_container.Has(uuid);
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AssetContainerFixture, Has)->Name("AssetContainer/Has")->Arg(512)->Arg(8192)->Arg(131'072);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(AssetContainerFixture, AcquireContainerHandle)(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
std::uint64_t sum = 0;
|
||||
for (const UUID& uuid: uuids)
|
||||
{
|
||||
sum += m_container.AcquireContainerHandle(uuid)->GetHardRefCount();
|
||||
}
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AssetContainerFixture, AcquireContainerHandle)
|
||||
->Name("AssetContainer/AcquireContainerHandle")
|
||||
->Arg(512)
|
||||
->Arg(8192)
|
||||
->Arg(131'072);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(AssetContainerFixture, Remove)(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
eastl::vector<UUID> uuids;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
uuids = InsertRandomAssets(m_container, count);
|
||||
state.ResumeTiming();
|
||||
|
||||
for (const UUID& uuid: uuids)
|
||||
{
|
||||
m_container.Remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(BenchmarkAsset)));
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AssetContainerFixture, Remove)->Name("AssetContainer/Remove")->Arg(512)->Arg(8192)->Arg(131'072);
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,187 @@
|
||||
/*********************************************************************
|
||||
* \file Reference.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Asset/Reference.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
template<template<class> class REFERENCE>
|
||||
class ReferenceFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
using ReferenceType = REFERENCE<BenchmarkAsset>;
|
||||
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
SetActiveAssetLibrary(&m_library);
|
||||
std::ignore = m_library.Insert(&m_container);
|
||||
|
||||
m_uuid = CreateAsset<BenchmarkAsset>()->GetHeader().uuid;
|
||||
m_reference.emplace(m_uuid);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
SetActiveAssetLibrary(&g_assetLibrary);
|
||||
}
|
||||
|
||||
AssetLibrary m_library;
|
||||
AssetContainer<BenchmarkAsset> m_container;
|
||||
UUID m_uuid;
|
||||
eastl::optional<ReferenceType> m_reference;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceConstruct, HardReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
ReferenceType reference {m_uuid};
|
||||
benchmark::DoNotOptimize(reference);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, HardReferenceConstruct)->Name("Reference/HardReference/Construct");
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, SoftReferenceConstruct, SoftReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
ReferenceType reference {m_uuid};
|
||||
benchmark::DoNotOptimize(reference);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, SoftReferenceConstruct)->Name("Reference/SoftReference/Construct");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceGet, HardReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(m_reference->Get());
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, HardReferenceGet)->Name("Reference/HardReference/Get");
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, SoftReferenceGet, SoftReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(m_reference->Get());
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, SoftReferenceGet)->Name("Reference/SoftReference/Get");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceCopy, HardReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
ReferenceType copy {*m_reference};
|
||||
benchmark::DoNotOptimize(copy);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, HardReferenceCopy)->Name("Reference/HardReference/Copy");
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, SoftReferenceCopy, SoftReference)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
ReferenceType copy {*m_reference};
|
||||
benchmark::DoNotOptimize(copy);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, SoftReferenceCopy)->Name("Reference/SoftReference/Copy");
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceMove, HardReference)(benchmark::State& state)
|
||||
{
|
||||
constexpr std::size_t BATCH_SIZE = 128;
|
||||
eastl::vector<ReferenceType> batch;
|
||||
batch.reserve(BATCH_SIZE);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
batch.clear();
|
||||
for (std::size_t i = 0; i < BATCH_SIZE; ++i)
|
||||
{
|
||||
batch.emplace_back(m_uuid);
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
for (ReferenceType& reference: batch)
|
||||
{
|
||||
ReferenceType moved {std::move(reference)};
|
||||
benchmark::DoNotOptimize(moved);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * BATCH_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, HardReferenceMove)->Name("Reference/HardReference/Move");
|
||||
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, SoftReferenceMove, SoftReference)(benchmark::State& state)
|
||||
{
|
||||
constexpr std::size_t BATCH_SIZE = 128;
|
||||
eastl::vector<ReferenceType> batch;
|
||||
batch.reserve(BATCH_SIZE);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
batch.clear();
|
||||
for (std::size_t i = 0; i < BATCH_SIZE; ++i)
|
||||
{
|
||||
batch.emplace_back(m_uuid);
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
for (ReferenceType& reference: batch)
|
||||
{
|
||||
ReferenceType moved {std::move(reference)};
|
||||
benchmark::DoNotOptimize(moved);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * BATCH_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ReferenceFixture, SoftReferenceMove)->Name("Reference/SoftReference/Move");
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,312 @@
|
||||
/*********************************************************************
|
||||
* \file BigFile.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/BigFile/BigFile.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EngineBenchmarks/BigFileInfo_generated.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAssetReferred.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
class BigFileFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
static constexpr std::uint32_t ASSET_COUNT = 20'000;
|
||||
static constexpr std::uint32_t REFS_PER_ASSET = 4;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
// The DB is populated exactly once for the entire benchmark process.
|
||||
// SetUp is called before every calibration round (Google Benchmark doubles
|
||||
// iteration counts until it reaches a stable measurement), so rebuilding
|
||||
// the DB each time would dominate the total benchmark time.
|
||||
m_uuids = BuildData();
|
||||
m_bigFile.emplace(File {BIGFILE_ENGINEBENCHMARKS_LOCATION});
|
||||
}
|
||||
|
||||
void TearDown([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
m_bigFile.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<BigFile> m_bigFile;
|
||||
eastl::span<const UUID> m_uuids;
|
||||
|
||||
private:
|
||||
static const eastl::vector<UUID>& BuildData()
|
||||
{
|
||||
static const eastl::vector<UUID> s_uuids = []()
|
||||
{
|
||||
eastl::vector<UUID> uuids;
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
uuids.reserve(ASSET_COUNT);
|
||||
flatbuffers.reserve(ASSET_COUNT);
|
||||
hardReferences.reserve(ASSET_COUNT);
|
||||
|
||||
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
|
||||
{
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ASSET; ++j)
|
||||
{
|
||||
BenchmarkAssetReferred assetReferred;
|
||||
assetReferred.SetName(assetReferred.GetHeader().uuid.ToString());
|
||||
|
||||
asset.GetAsset().refs.emplace_back(assetReferred.GetHeader().uuid);
|
||||
}
|
||||
|
||||
eastl::vector<std::byte> flatbuffer = asset.Pack();
|
||||
hardReferences.push_back(asset.GetHardReferences(flatbuffer));
|
||||
flatbuffers.push_back(std::move(flatbuffer));
|
||||
uuids.push_back(asset.GetHeader().uuid);
|
||||
}
|
||||
|
||||
BigFile bigFile {File {BIGFILE_ENGINEBENCHMARKS_LOCATION}};
|
||||
bigFile.Clear();
|
||||
bigFile.SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
|
||||
|
||||
return uuids;
|
||||
}();
|
||||
|
||||
return s_uuids;
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
eastl::vector<std::byte> flatbuffer = m_bigFile->GetAssetFlatbuffer(
|
||||
m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
benchmark::DoNotOptimize(flatbuffer);
|
||||
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, 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_bigFile->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(BigFileFixture, GetAssetFlatbuffers)
|
||||
->Name("BigFile/GetAssetFlatbuffers")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096)
|
||||
->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetHardReferences)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(
|
||||
m_bigFile->GetAssetHardReferences(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]));
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations()) * REFS_PER_ASSET * UUID::UUID_BYTE_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetHardReferences)->Name("BigFile/GetAssetHardReferences")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetsHardReferences)(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::size_t batchIndex = 0;
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(m_bigFile->GetAssetsHardReferences(batchPool[batchIndex]));
|
||||
batchIndex = (batchIndex + 1) % BATCH_POOL_SIZE;
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations()) * batchSize * REFS_PER_ASSET *
|
||||
UUID::UUID_BYTE_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetsHardReferences)
|
||||
->Name("BigFile/GetAssetsHardReferences")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096)
|
||||
->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ASSET; ++j)
|
||||
{
|
||||
asset.GetAsset().refs.emplace_back(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
}
|
||||
|
||||
const eastl::vector<std::byte> flatbuffer = asset.Pack();
|
||||
const eastl::vector<UUID> hardReferences = asset.GetHardReferences(flatbuffer);
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
|
||||
state.ResumeTiming();
|
||||
|
||||
m_bigFile->SaveAssetFlatbuffer(asset.GetHeader().uuid, flatbuffer, hardReferences);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffers)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
eastl::vector<UUID> uuids;
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
uuids.reserve(batchSize);
|
||||
flatbuffers.reserve(batchSize);
|
||||
hardReferences.reserve(batchSize);
|
||||
|
||||
for (std::size_t i = 0; i < batchSize; ++i)
|
||||
{
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ASSET; ++j)
|
||||
{
|
||||
asset.GetAsset().refs.emplace_back(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
}
|
||||
|
||||
eastl::vector<std::byte> flatbuffer = asset.Pack();
|
||||
uuids.push_back(asset.GetHeader().uuid);
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
|
||||
hardReferences.push_back(asset.GetHardReferences(flatbuffer));
|
||||
flatbuffers.push_back(std::move(flatbuffer));
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
m_bigFile->SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffers)
|
||||
->Name("BigFile/SaveAssetFlatbuffers")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096)
|
||||
->UseRealTime();
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,14 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName}Benchmarks)
|
||||
|
||||
bigfoot_create_package_benchmarks(
|
||||
"")
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
effolkronium_random
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,AssetsForBenchmarking>)
|
||||
|
||||
bigfoot_create_bigfile("Benchmarks/Bigfoot")
|
||||
|
||||
bigfoot_create_sasquatch_info()
|
||||
@@ -0,0 +1,22 @@
|
||||
// AUTO-GENERATED DO NOT TOUCH
|
||||
|
||||
/*********************************************************************
|
||||
* \file BigFileInfo.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINEBENCHMARKS_BIGFILEINFO_GENERATED_HPP
|
||||
#define BIGFOOT_ENGINEBENCHMARKS_BIGFILEINFO_GENERATED_HPP
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* BigFile location
|
||||
*/
|
||||
constexpr eastl::string_view BIGFILE_ENGINEBENCHMARKS_LOCATION {"./EngineBenchmarks.db"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -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"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,245 @@
|
||||
/*********************************************************************
|
||||
* \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 <AssetsForBenchmarking/Asset/BenchmarkAssetReferred.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/span.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::uint32_t REFS_PER_ASSET = 4;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
// The Sasquatch file is built exactly once for the entire benchmark process.
|
||||
// SetUp is called before every calibration round (Google Benchmark doubles
|
||||
// iteration counts until it reaches a stable measurement), so rebuilding
|
||||
// the file each time would dominate the total benchmark time.
|
||||
m_uuids = BuildData();
|
||||
m_sasquatch.emplace(File {SASQUATCH_ENGINEBENCHMARKS_LOCATION});
|
||||
}
|
||||
|
||||
void TearDown([[maybe_unused]] benchmark::State& p_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::span<const UUID> m_uuids;
|
||||
|
||||
private:
|
||||
static const eastl::vector<UUID>& BuildData()
|
||||
{
|
||||
static const eastl::vector<UUID> s_uuids = []()
|
||||
{
|
||||
File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}.Delete();
|
||||
|
||||
eastl::vector<UUID> uuids;
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
uuids.reserve(ASSET_COUNT);
|
||||
flatbuffers.reserve(ASSET_COUNT);
|
||||
hardReferences.reserve(ASSET_COUNT);
|
||||
|
||||
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
|
||||
{
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ASSET; ++j)
|
||||
{
|
||||
BenchmarkAssetReferred assetReferred;
|
||||
assetReferred.SetName(assetReferred.GetHeader().uuid.ToString());
|
||||
|
||||
asset.GetAsset().refs.emplace_back(assetReferred.GetHeader().uuid);
|
||||
}
|
||||
|
||||
eastl::vector<std::byte> flatbuffer = asset.Pack();
|
||||
hardReferences.push_back(asset.GetHardReferences(flatbuffer));
|
||||
flatbuffers.push_back(std::move(flatbuffer));
|
||||
uuids.push_back(asset.GetHeader().uuid);
|
||||
}
|
||||
|
||||
std::size_t totalFlatbufferBytes = 0;
|
||||
std::size_t totalHardReferencesBytes = 0;
|
||||
for (std::size_t i = 0; i < uuids.size(); ++i)
|
||||
{
|
||||
totalFlatbufferBytes += flatbuffers[i].size();
|
||||
totalHardReferencesBytes += hardReferences[i].size() * UUID::UUID_BYTE_SIZE;
|
||||
}
|
||||
|
||||
SasquatchWriter writer {File {SASQUATCH_ENGINEBENCHMARKS_LOCATION},
|
||||
uuids.size(),
|
||||
totalFlatbufferBytes,
|
||||
totalHardReferencesBytes};
|
||||
for (std::size_t i = 0; i < uuids.size(); ++i)
|
||||
{
|
||||
writer.WriteAsset(uuids[i], flatbuffers[i], hardReferences[i]);
|
||||
}
|
||||
|
||||
return uuids;
|
||||
}();
|
||||
|
||||
return s_uuids;
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetHardReferences)(benchmark::State& state)
|
||||
{
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(
|
||||
m_sasquatch->GetAssetHardReferences(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]));
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations()) * REFS_PER_ASSET * UUID::UUID_BYTE_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetHardReferences)->Name("Sasquatch/GetAssetHardReferences")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetsHardReferences)(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::size_t batchIndex = 0;
|
||||
for (auto _: state)
|
||||
{
|
||||
benchmark::DoNotOptimize(m_sasquatch->GetAssetsHardReferences(batchPool[batchIndex]));
|
||||
batchIndex = (batchIndex + 1) % BATCH_POOL_SIZE;
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations()) * batchSize * REFS_PER_ASSET *
|
||||
UUID::UUID_BYTE_SIZE);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetsHardReferences)
|
||||
->Name("Sasquatch/GetAssetsHardReferences")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096)
|
||||
->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetFlatbuffer)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
eastl::vector<std::byte> flatbuffer = m_sasquatch->GetAssetFlatbuffer(
|
||||
m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
|
||||
benchmark::DoNotOptimize(flatbuffer);
|
||||
|
||||
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffer)->Name("Sasquatch/GetAssetFlatbuffer")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
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)
|
||||
->UseRealTime();
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,391 @@
|
||||
/*********************************************************************
|
||||
* \file SerializationManager.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/SerializationManager/SerializationManager.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
#include <Engine/DataAccessor/DataAccessor.hpp>
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
#include <Engine/BigFile/BigFile.hpp>
|
||||
|
||||
#include <EngineBenchmarks/BigFileInfo_generated.hpp>
|
||||
#elif defined BIGFOOT_GAME
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <EngineBenchmarks/SasquatchInfo_generated.hpp>
|
||||
#endif
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAssetReferred.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
class SerializationManagerFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
static constexpr std::uint32_t ROOT_ASSET_COUNT = 20'000;
|
||||
static constexpr std::uint32_t REFS_PER_ROOT = 4;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
m_previousDataAccessor = g_dataAccessor;
|
||||
m_previousAssetLibrary = g_activeAssetLibrary;
|
||||
|
||||
// The data store is populated exactly once for the entire benchmark process.
|
||||
// SetUp is called before every calibration round (Google Benchmark doubles
|
||||
// iteration counts until it reaches a stable measurement), so rebuilding
|
||||
// the data store each time would dominate the total benchmark time.
|
||||
static const SharedData& s_data = BuildData();
|
||||
m_rootUuids = s_data.rootUuids;
|
||||
m_leafUuids = s_data.leafUuids;
|
||||
m_rootTotalBytes = &s_data.rootTotalBytes;
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
m_bigFile.emplace(File {BIGFILE_ENGINEBENCHMARKS_LOCATION});
|
||||
SetDataAccessor(&*m_bigFile);
|
||||
#elif defined BIGFOOT_GAME
|
||||
m_sasquatch.emplace(File {SASQUATCH_ENGINEBENCHMARKS_LOCATION});
|
||||
SetDataAccessor(&*m_sasquatch);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TearDown([[maybe_unused]] benchmark::State& p_state) override
|
||||
{
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
m_bigFile.reset();
|
||||
#elif defined BIGFOOT_GAME
|
||||
m_sasquatch.reset();
|
||||
#endif
|
||||
SetDataAccessor(m_previousDataAccessor);
|
||||
SetActiveAssetLibrary(m_previousAssetLibrary);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers a fresh, empty AssetLibrary as the active one, so that every root and leaf asset appears unloaded
|
||||
*
|
||||
* \param p_library The library to register
|
||||
* \param p_leafContainer The container to register leaves into
|
||||
* \param p_rootContainer The container to register roots into
|
||||
*/
|
||||
static void ResetActiveLibrary(AssetLibrary& p_library,
|
||||
AssetContainer<BenchmarkAssetReferred>& p_leafContainer,
|
||||
AssetContainer<BenchmarkAsset>& p_rootContainer)
|
||||
{
|
||||
std::ignore = p_library.Insert(&p_leafContainer);
|
||||
std::ignore = p_library.Insert(&p_rootContainer);
|
||||
SetActiveAssetLibrary(&p_library);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> RandomRootBatch(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_rootUuids[Random::get<std::size_t>(0, m_rootUuids.size() - 1)]);
|
||||
}
|
||||
|
||||
return batch;
|
||||
}
|
||||
|
||||
/*
|
||||
* Total flatbuffer bytes (root + its leaves) that loading p_roots will actually read, deduplicated the same way
|
||||
* SerializationManager::LoadAssets deduplicates its own transitive discovery
|
||||
*
|
||||
* \param p_roots The root assets that would be loaded
|
||||
* \return The total flatbuffer bytes across the unique roots
|
||||
*/
|
||||
[[nodiscard]]
|
||||
std::int64_t TotalBytes(const eastl::vector<UUID>& p_roots) const
|
||||
{
|
||||
ankerl::unordered_dense::set<UUID> unique;
|
||||
unique.reserve(p_roots.size());
|
||||
for (const UUID& uuid: p_roots)
|
||||
{
|
||||
unique.insert(uuid);
|
||||
}
|
||||
|
||||
std::int64_t bytes = 0;
|
||||
for (const UUID& uuid: unique)
|
||||
{
|
||||
bytes += static_cast<std::int64_t>(m_rootTotalBytes->at(uuid));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
DataAccessor* m_previousDataAccessor = nullptr;
|
||||
AssetLibrary* m_previousAssetLibrary = nullptr;
|
||||
|
||||
eastl::span<const UUID> m_rootUuids;
|
||||
eastl::span<const UUID> m_leafUuids;
|
||||
const ankerl::unordered_dense::map<UUID, std::size_t>* m_rootTotalBytes = nullptr;
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
eastl::optional<BigFile> m_bigFile;
|
||||
#elif defined BIGFOOT_GAME
|
||||
eastl::optional<Sasquatch> m_sasquatch;
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct SharedData
|
||||
{
|
||||
eastl::vector<UUID> rootUuids;
|
||||
eastl::vector<UUID> leafUuids;
|
||||
ankerl::unordered_dense::map<UUID, std::size_t> rootTotalBytes;
|
||||
};
|
||||
|
||||
static const SharedData& BuildData()
|
||||
{
|
||||
static const SharedData s_data = []()
|
||||
{
|
||||
SharedData data;
|
||||
data.rootUuids.reserve(ROOT_ASSET_COUNT);
|
||||
data.leafUuids.reserve(ROOT_ASSET_COUNT * REFS_PER_ROOT);
|
||||
data.rootTotalBytes.reserve(ROOT_ASSET_COUNT);
|
||||
|
||||
eastl::vector<UUID> uuids;
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
uuids.reserve(ROOT_ASSET_COUNT * (1 + REFS_PER_ROOT));
|
||||
flatbuffers.reserve(uuids.capacity());
|
||||
hardReferences.reserve(uuids.capacity());
|
||||
|
||||
for (std::uint32_t i = 0; i < ROOT_ASSET_COUNT; ++i)
|
||||
{
|
||||
BenchmarkAsset root;
|
||||
root.SetName(root.GetHeader().uuid.ToString());
|
||||
root.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
|
||||
std::size_t totalBytes = 0;
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ROOT; ++j)
|
||||
{
|
||||
BenchmarkAssetReferred leaf;
|
||||
leaf.SetName(leaf.GetHeader().uuid.ToString());
|
||||
|
||||
root.GetAsset().refs.emplace_back(leaf.GetHeader().uuid);
|
||||
|
||||
eastl::vector<std::byte> leafFlatbuffer = leaf.Pack();
|
||||
totalBytes += leafFlatbuffer.size();
|
||||
data.leafUuids.push_back(leaf.GetHeader().uuid);
|
||||
uuids.push_back(leaf.GetHeader().uuid);
|
||||
hardReferences.push_back(leaf.GetHardReferences(leafFlatbuffer));
|
||||
flatbuffers.push_back(std::move(leafFlatbuffer));
|
||||
}
|
||||
|
||||
eastl::vector<std::byte> rootFlatbuffer = root.Pack();
|
||||
totalBytes += rootFlatbuffer.size();
|
||||
data.rootTotalBytes.emplace(root.GetHeader().uuid, totalBytes);
|
||||
data.rootUuids.push_back(root.GetHeader().uuid);
|
||||
uuids.push_back(root.GetHeader().uuid);
|
||||
hardReferences.push_back(root.GetHardReferences(rootFlatbuffer));
|
||||
flatbuffers.push_back(std::move(rootFlatbuffer));
|
||||
}
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
BigFile bigFile {File { BIGFILE_ENGINEBENCHMARKS_LOCATION }};
|
||||
bigFile.Clear();
|
||||
bigFile.SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
|
||||
#elif defined BIGFOOT_GAME
|
||||
std::size_t totalFlatbufferBytes = 0;
|
||||
std::size_t totalHardReferencesBytes = 0;
|
||||
for (std::size_t i = 0; i < uuids.size(); ++i)
|
||||
{
|
||||
totalFlatbufferBytes += flatbuffers[i].size();
|
||||
totalHardReferencesBytes += hardReferences[i].size() * UUID::UUID_BYTE_SIZE;
|
||||
}
|
||||
|
||||
File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}.Delete();
|
||||
SasquatchWriter writer {File {SASQUATCH_ENGINEBENCHMARKS_LOCATION},
|
||||
uuids.size(),
|
||||
totalFlatbufferBytes,
|
||||
totalHardReferencesBytes};
|
||||
for (std::size_t i = 0; i < uuids.size(); ++i)
|
||||
{
|
||||
writer.WriteAsset(uuids[i], flatbuffers[i], hardReferences[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}();
|
||||
|
||||
return s_data;
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SerializationManagerFixture, LoadAsset)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
AssetLibrary library;
|
||||
// leafContainer must outlive rootContainer: the loaded root's HardReferences release into leafContainer's
|
||||
// handles on destruction, so declaring it second would tear it down first and use-after-free those handles.
|
||||
AssetContainer<BenchmarkAssetReferred> leafContainer;
|
||||
AssetContainer<BenchmarkAsset> rootContainer;
|
||||
ResetActiveLibrary(library, leafContainer, rootContainer);
|
||||
|
||||
const UUID uuid = m_rootUuids[Random::get<std::size_t>(0, m_rootUuids.size() - 1)];
|
||||
const std::size_t uuidBytes = m_rootTotalBytes->at(uuid);
|
||||
state.ResumeTiming();
|
||||
|
||||
SerializationManager::LoadAsset(uuid);
|
||||
bytesProcessed += static_cast<std::int64_t>(uuidBytes);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * (1 + REFS_PER_ROOT));
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SerializationManagerFixture, LoadAsset)->Name("SerializationManager/LoadAsset")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SerializationManagerFixture, LoadAssets)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
AssetLibrary library;
|
||||
// leafContainer must outlive rootContainer, see the same comment in the LoadAsset benchmark above.
|
||||
AssetContainer<BenchmarkAssetReferred> leafContainer;
|
||||
AssetContainer<BenchmarkAsset> rootContainer;
|
||||
ResetActiveLibrary(library, leafContainer, rootContainer);
|
||||
|
||||
const eastl::vector<UUID> batch = RandomRootBatch(batchSize);
|
||||
const std::int64_t batchBytes = TotalBytes(batch);
|
||||
state.ResumeTiming();
|
||||
|
||||
SerializationManager::LoadAssets(batch);
|
||||
bytesProcessed += batchBytes;
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize * (1 + REFS_PER_ROOT));
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SerializationManagerFixture, LoadAssets)
|
||||
->Name("SerializationManager/LoadAssets")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->Arg(4096)
|
||||
->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
BENCHMARK_DEFINE_F(SerializationManagerFixture, SaveAsset)(benchmark::State& state)
|
||||
{
|
||||
AssetLibrary library;
|
||||
AssetContainer<BenchmarkAsset> container;
|
||||
std::ignore = library.Insert(&container);
|
||||
SetActiveAssetLibrary(&library);
|
||||
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
auto* asset = CreateAsset<BenchmarkAsset>();
|
||||
asset->SetName(asset->GetHeader().uuid.ToString());
|
||||
asset->GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ROOT; ++j)
|
||||
{
|
||||
asset->GetAsset().refs.emplace_back(m_leafUuids[Random::get<std::size_t>(0, m_leafUuids.size() - 1)]);
|
||||
}
|
||||
const UUID uuid = asset->GetHeader().uuid;
|
||||
const std::size_t assetBytes = asset->Pack().size();
|
||||
bytesProcessed += static_cast<std::int64_t>(assetBytes);
|
||||
state.ResumeTiming();
|
||||
|
||||
benchmark::DoNotOptimize(SerializationManager::SaveAsset(uuid));
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SerializationManagerFixture, SaveAsset)->Name("SerializationManager/SaveAsset")->UseRealTime();
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SerializationManagerFixture, SaveAssets)(benchmark::State& state)
|
||||
{
|
||||
const auto batchSize = static_cast<std::size_t>(state.range(0));
|
||||
|
||||
AssetLibrary library;
|
||||
AssetContainer<BenchmarkAsset> container;
|
||||
std::ignore = library.Insert(&container);
|
||||
SetActiveAssetLibrary(&library);
|
||||
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
eastl::vector<UUID> uuids;
|
||||
uuids.reserve(batchSize);
|
||||
std::size_t batchBytes = 0;
|
||||
for (std::size_t i = 0; i < batchSize; ++i)
|
||||
{
|
||||
auto* asset = CreateAsset<BenchmarkAsset>();
|
||||
asset->SetName(asset->GetHeader().uuid.ToString());
|
||||
asset->GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
for (std::uint32_t j = 0; j < REFS_PER_ROOT; ++j)
|
||||
{
|
||||
asset->GetAsset().refs.emplace_back(m_leafUuids[Random::get<std::size_t>(0, m_leafUuids.size() - 1)]);
|
||||
}
|
||||
|
||||
batchBytes += asset->Pack().size();
|
||||
uuids.push_back(asset->GetHeader().uuid);
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
benchmark::DoNotOptimize(SerializationManager::SaveAssets(uuids));
|
||||
bytesProcessed += static_cast<std::int64_t>(batchBytes);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * batchSize);
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SerializationManagerFixture, SaveAssets)
|
||||
->Name("SerializationManager/SaveAssets")
|
||||
->Arg(1)
|
||||
->Arg(8)
|
||||
->Arg(64)
|
||||
->Arg(512)
|
||||
->UseRealTime();
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/SasquatchBaker)
|
||||
@@ -0,0 +1,11 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName}Benchmarks)
|
||||
|
||||
bigfoot_create_package_benchmarks(
|
||||
"Tools")
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,AssetsForBenchmarking>)
|
||||
|
||||
bigfoot_create_bigfile("Benchmarks/Tools")
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// AUTO-GENERATED DO NOT TOUCH
|
||||
|
||||
/*********************************************************************
|
||||
* \file BigFileInfo.generated.hpp
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SASQUATCHBAKERBENCHMARKS_BIGFILEINFO_GENERATED_HPP
|
||||
#define BIGFOOT_SASQUATCHBAKERBENCHMARKS_BIGFILEINFO_GENERATED_HPP
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* BigFile location
|
||||
*/
|
||||
constexpr eastl::string_view BIGFILE_SASQUATCHBAKERBENCHMARKS_LOCATION {"./SasquatchBakerBenchmarks.db"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/*********************************************************************
|
||||
* \file SasquatchBaker.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <SasquatchBaker/SasquatchBaker.hpp>
|
||||
|
||||
#include <Engine/BigFile/BigFile.hpp>
|
||||
#include <Engine/Sasquatch/Sasquatch.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
|
||||
|
||||
#include <SasquatchBakerBenchmarks/BigFileInfo_generated.hpp>
|
||||
|
||||
#include <EASTL/vector.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class SasquatchBakerFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
static constexpr std::uint32_t ASSET_COUNT = 20'000;
|
||||
static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
|
||||
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
m_bigFile.Clear();
|
||||
m_outputFile.Delete();
|
||||
|
||||
eastl::vector<UUID> uuids;
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers;
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences;
|
||||
uuids.reserve(ASSET_COUNT);
|
||||
flatbuffers.reserve(ASSET_COUNT);
|
||||
hardReferences.reserve(ASSET_COUNT);
|
||||
|
||||
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
|
||||
{
|
||||
BenchmarkAsset asset;
|
||||
asset.SetName(asset.GetHeader().uuid.ToString());
|
||||
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
|
||||
|
||||
const eastl::vector<std::byte> flatbuffer = asset.Pack();
|
||||
flatbuffers.push_back(flatbuffer);
|
||||
hardReferences.push_back(asset.GetHardReferences(flatbuffer));
|
||||
uuids.push_back(asset.GetHeader().uuid);
|
||||
}
|
||||
|
||||
m_bigFile.SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
m_outputFile.Delete();
|
||||
}
|
||||
|
||||
static constexpr eastl::string_view SASQUATCH_SASQUATCHBAKERBENCHMARKS_LOCATION {
|
||||
"./SasquatchBakerBenchmarks.sasquatch"};
|
||||
BigFile m_bigFile {File {BIGFILE_SASQUATCHBAKERBENCHMARKS_LOCATION}};
|
||||
File m_outputFile {SASQUATCH_SASQUATCHBAKERBENCHMARKS_LOCATION};
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(SasquatchBakerFixture, Bake)(benchmark::State& state)
|
||||
{
|
||||
std::int64_t bytesProcessed = 0;
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
Bake(m_bigFile, m_outputFile);
|
||||
bytesProcessed += static_cast<std::int64_t>(m_outputFile.GetSize());
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * ASSET_COUNT);
|
||||
state.SetBytesProcessed(bytesProcessed);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SasquatchBakerFixture, Bake)->Name("SasquatchBaker/Bake")->Unit(benchmark::kMillisecond);
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,92 @@
|
||||
/*********************************************************************
|
||||
* \file Graph.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Utils/Algorithm/Graph.hpp>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
class LayeredTopologicalSortFixture: public benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
/*
|
||||
* Nodes are grouped into layers, and every node in a layer depends on up to DEPENDENCY_MAX_COUNT
|
||||
* distinct nodes randomly picked from the previous layer.
|
||||
*/
|
||||
void SetUp(benchmark::State& state) override
|
||||
{
|
||||
Random::seed(0x5EEDU);
|
||||
|
||||
const auto nodeCount = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
m_graph.clear();
|
||||
m_nodes.clear();
|
||||
m_graph.reserve(nodeCount);
|
||||
m_nodes.reserve(nodeCount);
|
||||
|
||||
for (std::uint32_t node = 0; node < nodeCount; ++node)
|
||||
{
|
||||
m_nodes.insert(node);
|
||||
eastl::vector<std::uint32_t>& edges = m_graph[node];
|
||||
|
||||
const std::uint32_t layerStart = (node / NODE_COUNT_PER_LAYER) * NODE_COUNT_PER_LAYER;
|
||||
if (layerStart == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::uint32_t previousLayerStart = layerStart - NODE_COUNT_PER_LAYER;
|
||||
ankerl::unordered_dense::set<std::uint32_t> picked;
|
||||
for (std::uint32_t i = 0; i < DEPENDENCY_MAX_COUNT; ++i)
|
||||
{
|
||||
const std::uint32_t dependency = Random::get<std::uint32_t>(previousLayerStart, layerStart - 1);
|
||||
if (picked.insert(dependency).second)
|
||||
{
|
||||
edges.push_back(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::uint32_t NODE_COUNT_PER_LAYER = 64;
|
||||
static constexpr std::uint32_t DEPENDENCY_MAX_COUNT = 4;
|
||||
|
||||
ankerl::unordered_dense::map<std::uint32_t, eastl::vector<std::uint32_t>> m_graph;
|
||||
ankerl::unordered_dense::set<std::uint32_t> m_nodes;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BENCHMARK_DEFINE_F(LayeredTopologicalSortFixture, LayeredTopologicalSortBench)(benchmark::State& state)
|
||||
{
|
||||
const auto nodeCount = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
eastl::vector<eastl::vector<std::uint32_t>> waves;
|
||||
eastl::vector<std::uint32_t> cyclicNodes;
|
||||
benchmark::DoNotOptimize(LayeredTopologicalSort(m_graph, m_nodes, waves, cyclicNodes));
|
||||
benchmark::DoNotOptimize(waves);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * nodeCount);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(LayeredTopologicalSortFixture, LayeredTopologicalSortBench)
|
||||
->Name("Bigfoot::LayeredTopologicalSort")
|
||||
->Arg(512)
|
||||
->Arg(8192)
|
||||
->Arg(262'144)
|
||||
->Arg(4'194'304);
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,5 @@
|
||||
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName}Benchmarks)
|
||||
|
||||
bigfoot_create_package_benchmarks(
|
||||
"")
|
||||
@@ -0,0 +1,259 @@
|
||||
#include <Utils/Containers/SlotMap.hpp>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <effolkronium/random.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
using Random = effolkronium::random_static;
|
||||
|
||||
struct MyComplexStruct
|
||||
{
|
||||
std::uint32_t m_actualValueForBench = 0; // field summed by the Iterate benchmark
|
||||
|
||||
float m_position[3] = {0.0f, 0.0f, 0.0f};
|
||||
float m_rotation[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
float m_scale[3] = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
std::uint64_t m_entityId = 0;
|
||||
std::uint32_t m_parentSlot = 0;
|
||||
std::uint32_t m_flags = 0;
|
||||
|
||||
MyComplexStruct() = default;
|
||||
|
||||
explicit MyComplexStruct(const std::uint32_t p_value):
|
||||
m_actualValueForBench(p_value),
|
||||
m_entityId(p_value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(MyComplexStruct) == 64, "Payload should be exactly one cache line.");
|
||||
static_assert(std::is_trivially_copyable_v<MyComplexStruct>,
|
||||
"Container moves should be a plain memcpy, not a heap-touching move.");
|
||||
|
||||
template<class SLOTMAP>
|
||||
class SlotMapAdaptor
|
||||
{
|
||||
public:
|
||||
using Key = SLOTMAP::SlotMapKey;
|
||||
|
||||
Key Add(MyComplexStruct&& p_value)
|
||||
{
|
||||
return m_container.Insert(std::move(p_value));
|
||||
}
|
||||
|
||||
const MyComplexStruct* Find(const Key p_key) const
|
||||
{
|
||||
return m_container.Get(p_key);
|
||||
}
|
||||
|
||||
void Remove(const Key p_key)
|
||||
{
|
||||
m_container.Remove(p_key);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_container.Reset();
|
||||
}
|
||||
|
||||
template<class FUNC>
|
||||
void ForEachValue(FUNC&& p_func) const
|
||||
{
|
||||
for (const MyComplexStruct& value: m_container)
|
||||
{
|
||||
p_func(value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SLOTMAP m_container;
|
||||
};
|
||||
|
||||
template<class MAP>
|
||||
class HashMapHarnessAdaptor
|
||||
{
|
||||
public:
|
||||
using Key = typename MAP::key_type;
|
||||
|
||||
Key Add(MyComplexStruct&& p_value)
|
||||
{
|
||||
const Key key = m_nextKey++;
|
||||
m_container.emplace(key, std::move(p_value));
|
||||
return key;
|
||||
}
|
||||
|
||||
const MyComplexStruct* Find(const Key p_key) const
|
||||
{
|
||||
const auto it = m_container.find(p_key);
|
||||
return it != m_container.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void Remove(const Key p_key)
|
||||
{
|
||||
m_container.erase(p_key);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_container.clear();
|
||||
m_nextKey = 0;
|
||||
}
|
||||
|
||||
template<class FUNC>
|
||||
void ForEachValue(FUNC&& p_func) const
|
||||
{
|
||||
for (const auto& keyValue: m_container)
|
||||
{
|
||||
p_func(keyValue.second);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MAP m_container;
|
||||
Key m_nextKey = 0;
|
||||
};
|
||||
|
||||
template<class ADAPTOR>
|
||||
void Insert(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
ADAPTOR adaptor;
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
benchmark::DoNotOptimize(adaptor.Add(MyComplexStruct {i}));
|
||||
}
|
||||
benchmark::DoNotOptimize(adaptor);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(MyComplexStruct)));
|
||||
}
|
||||
|
||||
template<class ADAPTOR>
|
||||
void Remove(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
ADAPTOR adaptor;
|
||||
std::vector<typename ADAPTOR::Key> keys;
|
||||
keys.reserve(count);
|
||||
Random::seed(0x5EEDU);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
adaptor.Clear();
|
||||
keys.clear();
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
keys.push_back(adaptor.Add(MyComplexStruct {i}));
|
||||
}
|
||||
Random::shuffle(keys);
|
||||
state.ResumeTiming();
|
||||
|
||||
for (const typename ADAPTOR::Key key: keys)
|
||||
{
|
||||
adaptor.Remove(key);
|
||||
}
|
||||
benchmark::DoNotOptimize(adaptor);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(MyComplexStruct)));
|
||||
}
|
||||
|
||||
template<class ADAPTOR>
|
||||
void Get(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
ADAPTOR adaptor;
|
||||
std::vector<typename ADAPTOR::Key> keys;
|
||||
keys.reserve(count);
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
keys.push_back(adaptor.Add(MyComplexStruct {i}));
|
||||
}
|
||||
Random::seed(0x5EEDU);
|
||||
Random::shuffle(keys);
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
std::uint64_t sum = 0;
|
||||
for (const typename ADAPTOR::Key key: keys)
|
||||
{
|
||||
if (const MyComplexStruct* const value = adaptor.Find(key))
|
||||
{
|
||||
sum += value->m_actualValueForBench;
|
||||
}
|
||||
}
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(MyComplexStruct)));
|
||||
}
|
||||
|
||||
template<class ADAPTOR>
|
||||
void Iterate(benchmark::State& state)
|
||||
{
|
||||
const auto count = static_cast<std::uint32_t>(state.range(0));
|
||||
|
||||
ADAPTOR adaptor;
|
||||
for (std::uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
adaptor.Add(MyComplexStruct {i});
|
||||
}
|
||||
|
||||
for (auto _: state)
|
||||
{
|
||||
std::uint64_t sum = 0;
|
||||
adaptor.ForEachValue(
|
||||
[&sum](const MyComplexStruct& value)
|
||||
{
|
||||
sum += value.m_actualValueForBench;
|
||||
});
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * count);
|
||||
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(MyComplexStruct)));
|
||||
}
|
||||
|
||||
#define BIGFOOT_REGISTER_BENCHMARKS(ADAPTOR, NAME) \
|
||||
BENCHMARK_TEMPLATE(Insert, ADAPTOR)->Name(NAME "/Insert")->Arg(512)->Arg(8192)->Arg(262'144)->Arg(4'194'304); \
|
||||
BENCHMARK_TEMPLATE(Remove, ADAPTOR)->Name(NAME "/Remove")->Arg(512)->Arg(8192)->Arg(262'144)->Arg(4'194'304); \
|
||||
BENCHMARK_TEMPLATE(Get, ADAPTOR)->Name(NAME "/Get")->Arg(512)->Arg(8192)->Arg(262'144)->Arg(4'194'304); \
|
||||
BENCHMARK_TEMPLATE(Iterate, ADAPTOR)->Name(NAME "/Iterate")->Arg(512)->Arg(8192)->Arg(262'144)->Arg(4'194'304);
|
||||
|
||||
using DenseSlotMapHarness = SlotMapAdaptor<DenseSlotMap<MyComplexStruct>>;
|
||||
using StableSlotMapHarness = SlotMapAdaptor<StableSlotMap<MyComplexStruct>>;
|
||||
using UnorderedDenseMapHarness = HashMapHarnessAdaptor<ankerl::unordered_dense::map<std::uint64_t, MyComplexStruct>>;
|
||||
using UnorderedDenseSegementedMapHarness =
|
||||
HashMapHarnessAdaptor<ankerl::unordered_dense::segmented_map<std::uint64_t, MyComplexStruct>>;
|
||||
using UnorderedMapHarness = HashMapHarnessAdaptor<std::unordered_map<std::uint64_t, MyComplexStruct>>;
|
||||
using MapHarness = HashMapHarnessAdaptor<std::map<std::uint64_t, MyComplexStruct>>;
|
||||
|
||||
BIGFOOT_REGISTER_BENCHMARKS(DenseSlotMapHarness, "Bigfoot::DenseSlotMap");
|
||||
BIGFOOT_REGISTER_BENCHMARKS(StableSlotMapHarness, "Bigfoot::StableSlotMap");
|
||||
BIGFOOT_REGISTER_BENCHMARKS(UnorderedDenseMapHarness, "ankerl::unordered_dense::map");
|
||||
BIGFOOT_REGISTER_BENCHMARKS(UnorderedDenseSegementedMapHarness, "ankerl::unordered_dense::segmented_map");
|
||||
BIGFOOT_REGISTER_BENCHMARKS(UnorderedMapHarness, "std::unordered_map");
|
||||
BIGFOOT_REGISTER_BENCHMARKS(MapHarness, "std::map");
|
||||
|
||||
#undef BIGFOOT_REGISTER_BENCHMARKS
|
||||
#undef BIGFOOT_CACHE_TIER_ARGS
|
||||
} // namespace Bigfoot
|
||||
@@ -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,62 @@
|
||||
/*********************************************************************
|
||||
* \file AssetBase.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date April 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Asset/AssetBase.hpp>
|
||||
|
||||
#include <Engine/Asset/Reference_generated.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
void AssetBase::HardReferenceVisitor::Field([[maybe_unused]] size_t p_fieldIndex,
|
||||
[[maybe_unused]] size_t p_setIndex,
|
||||
const flatbuffers::ElementaryType p_type,
|
||||
const bool p_isVector,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
[[maybe_unused]] const char* p_name,
|
||||
const std::uint8_t* p_val)
|
||||
{
|
||||
if (!p_isVector)
|
||||
{
|
||||
Visit(p_type, p_typeTable, p_val);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void AssetBase::HardReferenceVisitor::Element([[maybe_unused]] size_t p_index,
|
||||
const flatbuffers::ElementaryType p_type,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
const std::uint8_t* p_val)
|
||||
{
|
||||
Visit(p_type, p_typeTable, p_val);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<UUID> AssetBase::HardReferenceVisitor::GetUUIDs() const
|
||||
{
|
||||
eastl::vector<UUID> uuids;
|
||||
uuids.reserve(m_uuids.size());
|
||||
std::copy(m_uuids.begin(), m_uuids.end(), eastl::back_inserter(uuids));
|
||||
return uuids;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void AssetBase::HardReferenceVisitor::Visit(const flatbuffers::ElementaryType p_type,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
const std::uint8_t* p_val)
|
||||
{
|
||||
if (p_val == nullptr || p_type != flatbuffers::ET_SEQUENCE ||
|
||||
p_typeTable != ::Flat::Bigfoot::HardReference::MiniReflectTypeTable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const ::Flat::Bigfoot::HardReference* hardReference = std::bit_cast<const ::Flat::Bigfoot::HardReference*>(p_val);
|
||||
m_uuids.emplace(flatbuffers::UnPack(hardReference->uuid()));
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -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)
|
||||
{
|
||||
auto* const pendingIt = eastl::find(m_pendingRemovalUuids.begin(), m_pendingRemovalUuids.end(), p_uuid);
|
||||
if (pendingIt != m_pendingRemovalUuids.end())
|
||||
{
|
||||
m_pendingRemovalUuids.erase_unsorted(pendingIt);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
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 entryIt = m_uuidToEntry.find(p_uuid);
|
||||
return entryIt != m_uuidToEntry.end() ? &entryIt->second : nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
const AssetContainerBase::Entry* AssetContainerBase::FindEntry(const UUID& p_uuid) const
|
||||
{
|
||||
const auto entryIt = m_uuidToEntry.find(p_uuid);
|
||||
return entryIt != m_uuidToEntry.end() ? &entryIt->second : nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
AssetContainerBase::Entry& AssetContainerBase::GetOrCreateEntry(const UUID& p_uuid)
|
||||
{
|
||||
auto [entryIt, inserted] = m_uuidToEntry.try_emplace(p_uuid);
|
||||
if (inserted)
|
||||
{
|
||||
entryIt->second.m_handleSlotKey = m_handles.Insert(this, p_uuid);
|
||||
entryIt->second.m_handle = m_handles.Get(entryIt->second.m_handleSlotKey);
|
||||
}
|
||||
|
||||
return entryIt->second;
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,86 @@
|
||||
/*********************************************************************
|
||||
* \file AssetLibrary.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
AssetContainerBase* AssetLibrary::GetAssetContainer(const AssetTypeID p_typeID)
|
||||
{
|
||||
if (const auto container = m_assetContainers.find(p_typeID); container != m_assetContainers.end())
|
||||
{
|
||||
return container->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
const AssetContainerBase* AssetLibrary::GetAssetContainer(const AssetTypeID p_typeID) const
|
||||
{
|
||||
if (const auto container = m_assetContainers.find(p_typeID); container != m_assetContainers.end())
|
||||
{
|
||||
return container->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
const AssetContainerBase* AssetLibrary::GetAssetContainer(const UUID& p_uuid) const
|
||||
{
|
||||
for (const auto [typeId, container]: m_assetContainers)
|
||||
{
|
||||
if (container->Has(p_uuid))
|
||||
{
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
AssetContainerBase* AssetLibrary::GetAssetContainer(const UUID& p_uuid)
|
||||
{
|
||||
for (const auto [typeId, container]: m_assetContainers)
|
||||
{
|
||||
if (container->Has(p_uuid))
|
||||
{
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool AssetLibrary::IsLoaded(const UUID& p_uuid) const
|
||||
{
|
||||
return GetAssetContainer(p_uuid) != nullptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void AssetLibrary::ProcessPendingRemovals()
|
||||
{
|
||||
for (const auto [typeId, container]: m_assetContainers)
|
||||
{
|
||||
container->ProcessPendingRemovals();
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void SetActiveAssetLibrary(AssetLibrary* p_assetLibrary)
|
||||
{
|
||||
g_activeAssetLibrary = p_assetLibrary;
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,24 @@
|
||||
/*********************************************************************
|
||||
* \file AssetTypeID.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date May 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/Asset/AssetTypeID.hpp>
|
||||
|
||||
#include <Engine/Asset/AssetTypeID_generated.hpp>
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::AssetTypeID Pack(const Bigfoot::AssetTypeID& p_assetTypeID)
|
||||
{
|
||||
return Flat::Bigfoot::AssetTypeID {p_assetTypeID};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
Bigfoot::AssetTypeID UnPack(const Flat::Bigfoot::AssetTypeID& p_flatAssetTypeID)
|
||||
{
|
||||
return Bigfoot::AssetTypeID {p_flatAssetTypeID.value()};
|
||||
}
|
||||
} // namespace flatbuffers
|
||||
@@ -0,0 +1,851 @@
|
||||
/*********************************************************************
|
||||
* \file BigFile.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#include <Engine/BigFile/BigFile.hpp>
|
||||
|
||||
#include <Engine/EngineAssertHandler.hpp>
|
||||
|
||||
#include <Utils/Format.hpp>
|
||||
|
||||
#include <EASTL/algorithm.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
BigFile::BigFile(const File& p_file)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_open_v2(p_file.ToAbsolute().GetPath().data(), &m_db, SQLITE_OPEN_READWRITE, nullptr);
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
result == SQLITE_OK,
|
||||
"Failed to open BigFile {} DB: {}",
|
||||
p_file.ToAbsolute().GetPath(),
|
||||
sqlite3_errmsg(m_db));
|
||||
|
||||
[[maybe_unused]]
|
||||
const int synchronousResult = sqlite3_exec(m_db, "PRAGMA synchronous = NORMAL;", nullptr, nullptr, nullptr);
|
||||
ASSERT(EngineAssertHandler,
|
||||
synchronousResult == SQLITE_OK,
|
||||
"Failed to set synchronous pragma: {}",
|
||||
sqlite3_errmsg(m_db));
|
||||
|
||||
[[maybe_unused]]
|
||||
const int foreignKeysResult = sqlite3_exec(m_db, "PRAGMA foreign_keys = ON;", nullptr, nullptr, nullptr);
|
||||
ASSERT(EngineAssertHandler,
|
||||
foreignKeysResult == SQLITE_OK,
|
||||
"Failed to set foreign_keys pragma: {}",
|
||||
sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::BeginTransaction()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_exec(m_db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to begin transaction: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::CommitTransaction()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_exec(m_db, "COMMIT TRANSACTION;", nullptr, nullptr, nullptr);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to commit: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::RollbackTransaction()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_exec(m_db, "ROLLBACK TRANSACTION;", nullptr, nullptr, nullptr);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to rollback: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Clear()
|
||||
{
|
||||
BeginTransaction();
|
||||
|
||||
BigFile::Request {*this, "DELETE FROM AssetHardReference;"}.Execute();
|
||||
BigFile::Request {*this, "DELETE FROM Asset;"}.Execute();
|
||||
BigFile::Request {*this, "DELETE FROM AssetMetadata;"}.Execute();
|
||||
|
||||
CommitTransaction();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::~BigFile()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_close_v2(m_db);
|
||||
CRITICAL_ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to close BigFile DB: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<std::byte> BigFile::GetAssetFlatbuffer(const UUID& p_uuid) const
|
||||
{
|
||||
BigFile::Request assetRequest {*this, "SELECT Asset FROM Asset WHERE UUID = ?1;"};
|
||||
assetRequest.Bind(1, p_uuid.ToBytes());
|
||||
|
||||
if (!assetRequest.Step())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const eastl::span<const std::byte> flatbuffer = assetRequest.Get(0);
|
||||
return eastl::vector<std::byte> {flatbuffer.begin(), flatbuffer.end()};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<eastl::vector<std::byte>> BigFile::GetAssetFlatbuffers(const eastl::span<const UUID> p_uuids) const
|
||||
{
|
||||
eastl::vector<eastl::vector<std::byte>> flatbuffers(p_uuids.size());
|
||||
|
||||
if (p_uuids.empty())
|
||||
{
|
||||
return flatbuffers;
|
||||
}
|
||||
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<std::size_t>> uuidToIndices;
|
||||
uuidToIndices.reserve(p_uuids.size());
|
||||
for (std::size_t i = 0; i < p_uuids.size(); ++i)
|
||||
{
|
||||
uuidToIndices[p_uuids[i]].push_back(i);
|
||||
}
|
||||
|
||||
eastl::vector<UUID> distinctUUIDs;
|
||||
distinctUUIDs.reserve(uuidToIndices.size());
|
||||
for (const auto& [uuid, indices]: uuidToIndices)
|
||||
{
|
||||
distinctUUIDs.push_back(uuid);
|
||||
}
|
||||
|
||||
constexpr std::size_t PARAMS_PER_ROW = 1;
|
||||
const std::size_t maxRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t chunkStart = 0; chunkStart < distinctUUIDs.size(); chunkStart += maxRowsPerStatement)
|
||||
{
|
||||
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, distinctUUIDs.size() - chunkStart);
|
||||
|
||||
eastl::string placeholders;
|
||||
placeholders.reserve(chunkSize * 2);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
placeholders += ", ";
|
||||
}
|
||||
placeholders += "?";
|
||||
}
|
||||
|
||||
const eastl::string sql = Format("SELECT UUID, Asset FROM Asset WHERE UUID IN ({});", placeholders);
|
||||
|
||||
BigFile::Request assetRequest {*this, sql};
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
assetRequest.Bind(static_cast<std::uint32_t>(i + 1), distinctUUIDs[chunkStart + i].ToBytes());
|
||||
}
|
||||
|
||||
while (assetRequest.Step())
|
||||
{
|
||||
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawUUID {
|
||||
static_cast<std::span<const std::byte>>(assetRequest.Get(0))};
|
||||
const UUID uuid {rawUUID};
|
||||
|
||||
const auto indicesIt = uuidToIndices.find(uuid);
|
||||
ASSERT(EngineAssertHandler,
|
||||
indicesIt != uuidToIndices.end(),
|
||||
"Returned UUID {} not found in requested UUIDs",
|
||||
uuid);
|
||||
|
||||
const eastl::span<const std::byte> flatbuffer = assetRequest.Get(1);
|
||||
|
||||
for (const std::size_t index: indicesIt->second)
|
||||
{
|
||||
flatbuffers[index].assign(flatbuffer.begin(), flatbuffer.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flatbuffers;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<UUID> BigFile::GetAssetHardReferences(const UUID& p_uuid) const
|
||||
{
|
||||
BigFile::Request hardReferencesRequest {*this, "SELECT HardReference FROM AssetHardReference WHERE UUID = ?1;"};
|
||||
hardReferencesRequest.Bind(1, p_uuid.ToBytes());
|
||||
|
||||
eastl::vector<UUID> hardReferences;
|
||||
while (hardReferencesRequest.Step())
|
||||
{
|
||||
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawUUID {
|
||||
static_cast<std::span<const std::byte>>(hardReferencesRequest.Get(0))};
|
||||
hardReferences.emplace_back(rawUUID);
|
||||
}
|
||||
|
||||
return hardReferences;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<eastl::vector<UUID>> BigFile::GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const
|
||||
{
|
||||
eastl::vector<eastl::vector<UUID>> hardReferences(p_uuids.size());
|
||||
|
||||
if (p_uuids.empty())
|
||||
{
|
||||
return hardReferences;
|
||||
}
|
||||
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<std::size_t>> uuidToIndices;
|
||||
uuidToIndices.reserve(p_uuids.size());
|
||||
for (std::size_t i = 0; i < p_uuids.size(); ++i)
|
||||
{
|
||||
uuidToIndices[p_uuids[i]].push_back(i);
|
||||
}
|
||||
|
||||
eastl::vector<UUID> distinctUUIDs;
|
||||
distinctUUIDs.reserve(uuidToIndices.size());
|
||||
for (const auto& [uuid, indices]: uuidToIndices)
|
||||
{
|
||||
distinctUUIDs.push_back(uuid);
|
||||
}
|
||||
|
||||
constexpr std::size_t PARAMS_PER_ROW = 1;
|
||||
const std::size_t maxRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t chunkStart = 0; chunkStart < distinctUUIDs.size(); chunkStart += maxRowsPerStatement)
|
||||
{
|
||||
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, distinctUUIDs.size() - chunkStart);
|
||||
|
||||
eastl::string placeholders;
|
||||
placeholders.reserve(chunkSize * 2);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
placeholders += ", ";
|
||||
}
|
||||
placeholders += "?";
|
||||
}
|
||||
|
||||
const eastl::string sql = Format("SELECT UUID, HardReference FROM AssetHardReference WHERE UUID IN ({});",
|
||||
placeholders);
|
||||
|
||||
BigFile::Request hardReferencesRequest {*this, sql};
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
hardReferencesRequest.Bind(static_cast<std::uint32_t>(i + 1), distinctUUIDs[chunkStart + i].ToBytes());
|
||||
}
|
||||
|
||||
while (hardReferencesRequest.Step())
|
||||
{
|
||||
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawOwnerUUID {
|
||||
static_cast<std::span<const std::byte>>(hardReferencesRequest.Get(0))};
|
||||
const UUID ownerUUID {rawOwnerUUID};
|
||||
|
||||
const auto indicesIt = uuidToIndices.find(ownerUUID);
|
||||
ASSERT(EngineAssertHandler,
|
||||
indicesIt != uuidToIndices.end(),
|
||||
"Returned UUID {} not found in requested UUIDs",
|
||||
ownerUUID);
|
||||
|
||||
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawHardReference {
|
||||
static_cast<std::span<const std::byte>>(hardReferencesRequest.Get(1))};
|
||||
|
||||
for (const std::size_t index: indicesIt->second)
|
||||
{
|
||||
hardReferences[index].emplace_back(rawHardReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hardReferences;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::optional<BigFile::AssetTime> BigFile::GetAssetTime(const UUID& p_uuid) const
|
||||
{
|
||||
BigFile::Request timeRequest {*this, "SELECT CreateTime, ModificationTime FROM AssetMetadata WHERE UUID = ?1;"};
|
||||
timeRequest.Bind(1, p_uuid.ToBytes());
|
||||
|
||||
if (!timeRequest.Step())
|
||||
{
|
||||
return eastl::nullopt;
|
||||
}
|
||||
|
||||
return AssetTime {.m_creationTime = Time {static_cast<std::uint64_t>(timeRequest.Get(0))},
|
||||
.m_modificationTime = Time {static_cast<std::uint64_t>(timeRequest.Get(1))}};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<eastl::optional<BigFile::AssetTime>> BigFile::GetAssetsTime(const eastl::span<const UUID> p_uuids) const
|
||||
{
|
||||
eastl::vector<eastl::optional<AssetTime>> times(p_uuids.size());
|
||||
|
||||
if (p_uuids.empty())
|
||||
{
|
||||
return times;
|
||||
}
|
||||
|
||||
ankerl::unordered_dense::map<UUID, eastl::vector<std::size_t>> uuidToIndices;
|
||||
uuidToIndices.reserve(p_uuids.size());
|
||||
for (std::size_t i = 0; i < p_uuids.size(); ++i)
|
||||
{
|
||||
uuidToIndices[p_uuids[i]].push_back(i);
|
||||
}
|
||||
|
||||
eastl::vector<UUID> distinctUUIDs;
|
||||
distinctUUIDs.reserve(uuidToIndices.size());
|
||||
for (const auto& [uuid, indices]: uuidToIndices)
|
||||
{
|
||||
distinctUUIDs.push_back(uuid);
|
||||
}
|
||||
|
||||
constexpr std::size_t PARAMS_PER_ROW = 1;
|
||||
const std::size_t maxRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t chunkStart = 0; chunkStart < distinctUUIDs.size(); chunkStart += maxRowsPerStatement)
|
||||
{
|
||||
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, distinctUUIDs.size() - chunkStart);
|
||||
|
||||
eastl::string placeholders;
|
||||
placeholders.reserve(chunkSize * 2);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
placeholders += ", ";
|
||||
}
|
||||
placeholders += "?";
|
||||
}
|
||||
|
||||
const eastl::string sql = Format(
|
||||
"SELECT UUID, CreateTime, ModificationTime FROM AssetMetadata WHERE UUID IN ({});",
|
||||
placeholders);
|
||||
|
||||
BigFile::Request timeRequest {*this, sql};
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
timeRequest.Bind(static_cast<std::uint32_t>(i + 1), distinctUUIDs[chunkStart + i].ToBytes());
|
||||
}
|
||||
|
||||
while (timeRequest.Step())
|
||||
{
|
||||
const std::span<const std::byte, UUID::UUID_BYTE_SIZE> rawUUID {
|
||||
static_cast<std::span<const std::byte>>(timeRequest.Get(0))};
|
||||
const UUID uuid {rawUUID};
|
||||
|
||||
const auto indicesIt = uuidToIndices.find(uuid);
|
||||
ASSERT(EngineAssertHandler,
|
||||
indicesIt != uuidToIndices.end(),
|
||||
"Returned UUID {} not found in requested UUIDs",
|
||||
uuid);
|
||||
|
||||
const AssetTime assetTime {.m_creationTime = Time {static_cast<std::uint64_t>(timeRequest.Get(1))},
|
||||
.m_modificationTime = Time {static_cast<std::uint64_t>(timeRequest.Get(2))}};
|
||||
|
||||
for (const std::size_t index: indicesIt->second)
|
||||
{
|
||||
times[index] = assetTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return times;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::SaveAssetFlatbuffer(const UUID& p_uuid,
|
||||
const eastl::span<const std::byte> p_flatbuffer,
|
||||
const eastl::span<const UUID> p_hardReferences)
|
||||
{
|
||||
BeginTransaction();
|
||||
|
||||
BigFile::Request metadataRequest {*this,
|
||||
"INSERT INTO AssetMetadata (UUID) VALUES (?1) "
|
||||
"ON CONFLICT(UUID) DO NOTHING;"};
|
||||
metadataRequest.Bind(1, p_uuid.ToBytes());
|
||||
metadataRequest.Execute();
|
||||
|
||||
BigFile::Request assetRequest {*this,
|
||||
"INSERT INTO Asset (UUID, Asset) VALUES (?1, ?2) "
|
||||
"ON CONFLICT(UUID) DO UPDATE SET Asset = excluded.Asset;"};
|
||||
assetRequest.Bind(1, p_uuid.ToBytes());
|
||||
assetRequest.Bind(2, p_flatbuffer);
|
||||
assetRequest.Execute();
|
||||
|
||||
BigFile::Request deleteHardReferencesRequest {*this, "DELETE FROM AssetHardReference WHERE UUID = ?1;"};
|
||||
deleteHardReferencesRequest.Bind(1, p_uuid.ToBytes());
|
||||
deleteHardReferencesRequest.Execute();
|
||||
|
||||
constexpr std::size_t HARDREF_PARAMS_PER_ROW = 2;
|
||||
constexpr std::size_t HARDREF_PLACEHOLDER_RESERVE_CHARS_PER_ROW = 8;
|
||||
const std::size_t maxHardRefRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
HARDREF_PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t chunkStart = 0; chunkStart < p_hardReferences.size(); chunkStart += maxHardRefRowsPerStatement)
|
||||
{
|
||||
const std::size_t chunkSize = eastl::min(maxHardRefRowsPerStatement, p_hardReferences.size() - chunkStart);
|
||||
|
||||
eastl::string hardReferencePlaceholders;
|
||||
hardReferencePlaceholders.reserve(chunkSize * HARDREF_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
hardReferencePlaceholders += ", ";
|
||||
}
|
||||
hardReferencePlaceholders += "(?, ?)";
|
||||
}
|
||||
|
||||
const eastl::string hardReferenceSQL = Format("INSERT INTO AssetHardReference (UUID, HardReference) VALUES {} "
|
||||
"ON CONFLICT(UUID, HardReference) DO NOTHING;",
|
||||
hardReferencePlaceholders);
|
||||
|
||||
BigFile::Request hardReferenceRequest {*this, hardReferenceSQL};
|
||||
std::uint32_t bindIndex = 1;
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
hardReferenceRequest.Bind(bindIndex++, p_uuid.ToBytes());
|
||||
hardReferenceRequest.Bind(bindIndex++, p_hardReferences[chunkStart + i].ToBytes());
|
||||
}
|
||||
hardReferenceRequest.Execute();
|
||||
}
|
||||
|
||||
CommitTransaction();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::SaveAssetFlatbuffers(const eastl::span<const UUID> p_uuids,
|
||||
const eastl::span<const eastl::vector<std::byte>> p_flatbuffers,
|
||||
const eastl::span<const eastl::vector<UUID>> p_hardReferences)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
p_uuids.size() == p_flatbuffers.size() && p_uuids.size() == p_hardReferences.size(),
|
||||
"p_metadatas and p_flatbuffers and p_hardReferences must have the same size");
|
||||
|
||||
if (p_uuids.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeginTransaction();
|
||||
|
||||
constexpr std::size_t METADATA_PARAMS_PER_ROW = 4;
|
||||
constexpr std::size_t METADATA_PLACEHOLDER_RESERVE_CHARS_PER_ROW = 14;
|
||||
constexpr std::size_t ASSET_PLACEHOLDER_RESERVE_CHARS_PER_ROW = 8;
|
||||
const std::size_t maxRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
METADATA_PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t chunkStart = 0; chunkStart < p_uuids.size(); chunkStart += maxRowsPerStatement)
|
||||
{
|
||||
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, p_uuids.size() - chunkStart);
|
||||
|
||||
eastl::string metadataPlaceholders;
|
||||
metadataPlaceholders.reserve(chunkSize * METADATA_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
metadataPlaceholders += ", ";
|
||||
}
|
||||
metadataPlaceholders += "(?)";
|
||||
}
|
||||
|
||||
const eastl::string metadataSQL = Format(
|
||||
"INSERT INTO AssetMetadata (UUID) VALUES {} ON CONFLICT(UUID) DO NOTHING;",
|
||||
metadataPlaceholders);
|
||||
|
||||
BigFile::Request metadataRequest {*this, metadataSQL};
|
||||
std::uint32_t metadataBindIndex = 1;
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
metadataRequest.Bind(metadataBindIndex++, p_uuids[chunkStart + i].ToBytes());
|
||||
}
|
||||
metadataRequest.Execute();
|
||||
|
||||
eastl::string assetPlaceholders;
|
||||
assetPlaceholders.reserve(chunkSize * ASSET_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
assetPlaceholders += ", ";
|
||||
}
|
||||
assetPlaceholders += "(?, ?)";
|
||||
}
|
||||
|
||||
const eastl::string assetSQL = Format(
|
||||
"INSERT INTO Asset (UUID, Asset) VALUES {} ON CONFLICT(UUID) DO UPDATE SET Asset = excluded.Asset;",
|
||||
assetPlaceholders);
|
||||
|
||||
BigFile::Request assetRequest {*this, assetSQL};
|
||||
std::uint32_t assetBindIndex = 1;
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
assetRequest.Bind(assetBindIndex++, p_uuids[chunkStart + i].ToBytes());
|
||||
assetRequest.Bind(assetBindIndex++, p_flatbuffers[chunkStart + i]);
|
||||
}
|
||||
assetRequest.Execute();
|
||||
|
||||
// Replace hard references for every asset in this chunk with its current set.
|
||||
eastl::string deleteInPlaceholders;
|
||||
deleteInPlaceholders.reserve(chunkSize * 3);
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
deleteInPlaceholders += ", ";
|
||||
}
|
||||
deleteInPlaceholders += "?";
|
||||
}
|
||||
|
||||
const eastl::string deleteHardReferencesSQL = Format("DELETE FROM AssetHardReference WHERE UUID IN ({});",
|
||||
deleteInPlaceholders);
|
||||
|
||||
BigFile::Request deleteHardReferencesRequest {*this, deleteHardReferencesSQL};
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
deleteHardReferencesRequest.Bind(static_cast<std::uint32_t>(i + 1), p_uuids[chunkStart + i].ToBytes());
|
||||
}
|
||||
deleteHardReferencesRequest.Execute();
|
||||
|
||||
eastl::vector<eastl::pair<UUID, UUID>> hardReferencePairs;
|
||||
for (std::size_t i = 0; i < chunkSize; ++i)
|
||||
{
|
||||
for (const UUID& hardReference: p_hardReferences[chunkStart + i])
|
||||
{
|
||||
hardReferencePairs.emplace_back(p_uuids[chunkStart + i], hardReference);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::size_t HARDREF_PARAMS_PER_ROW = 2;
|
||||
constexpr std::size_t HARDREF_PLACEHOLDER_RESERVE_CHARS_PER_ROW = 8;
|
||||
const std::size_t maxHardRefRowsPerStatement = static_cast<std::size_t>(
|
||||
sqlite3_limit(m_db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)) /
|
||||
HARDREF_PARAMS_PER_ROW;
|
||||
|
||||
for (std::size_t hardRefChunkStart = 0; hardRefChunkStart < hardReferencePairs.size();
|
||||
hardRefChunkStart += maxHardRefRowsPerStatement)
|
||||
{
|
||||
const std::size_t hardRefChunkSize = eastl::min(maxHardRefRowsPerStatement,
|
||||
hardReferencePairs.size() - hardRefChunkStart);
|
||||
|
||||
eastl::string hardReferencePlaceholders;
|
||||
hardReferencePlaceholders.reserve(hardRefChunkSize * HARDREF_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
|
||||
for (std::size_t i = 0; i < hardRefChunkSize; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
hardReferencePlaceholders += ", ";
|
||||
}
|
||||
hardReferencePlaceholders += "(?, ?)";
|
||||
}
|
||||
|
||||
const eastl::string hardReferenceSQL = Format(
|
||||
"INSERT INTO AssetHardReference (UUID, HardReference) VALUES {} "
|
||||
"ON CONFLICT(UUID, HardReference) DO NOTHING;",
|
||||
hardReferencePlaceholders);
|
||||
|
||||
BigFile::Request hardReferenceRequest {*this, hardReferenceSQL};
|
||||
std::uint32_t bindIndex = 1;
|
||||
for (std::size_t i = 0; i < hardRefChunkSize; ++i)
|
||||
{
|
||||
const auto& [assetUUID, hardReference] = hardReferencePairs[hardRefChunkStart + i];
|
||||
hardReferenceRequest.Bind(bindIndex++, assetUUID.ToBytes());
|
||||
hardReferenceRequest.Bind(bindIndex++, hardReference.ToBytes());
|
||||
}
|
||||
hardReferenceRequest.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
CommitTransaction();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool BigFile::Has(const UUID& p_uuid) const
|
||||
{
|
||||
BigFile::Request request {*this, "SELECT 1 FROM AssetMetadata WHERE UUID = ?1;"};
|
||||
request.Bind(1, p_uuid.ToBytes());
|
||||
|
||||
return request.Step();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Request(const BigFile& p_bigFile, const eastl::string_view p_request):
|
||||
m_db(p_bigFile.m_db)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_prepare_v2(m_db,
|
||||
p_request.data(),
|
||||
static_cast<int>(p_request.size()),
|
||||
&m_statement,
|
||||
nullptr);
|
||||
CRITICAL_ASSERT(EngineAssertHandler,
|
||||
result == SQLITE_OK,
|
||||
"Failed to create statement from BigFile DB: {}",
|
||||
sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const std::int32_t p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_int(m_statement, static_cast<int>(p_index), p_value);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint32_t p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_int(m_statement, static_cast<int>(p_index), static_cast<int>(p_value));
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const std::int64_t p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_int64(m_statement, static_cast<int>(p_index), p_value);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint64_t p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_int64(m_statement, static_cast<int>(p_index), std::bit_cast<std::int64_t>(p_value));
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const float p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_double(m_statement, static_cast<int>(p_index), p_value);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const double p_value)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_double(m_statement, static_cast<int>(p_index), p_value);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index, const eastl::string_view p_value, const CopyValue p_copy)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_text(m_statement,
|
||||
static_cast<int>(p_index),
|
||||
p_value.data(),
|
||||
static_cast<int>(p_value.size()),
|
||||
p_copy ? SQLITE_TRANSIENT : SQLITE_STATIC);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void BigFile::Request::Bind(const std::uint32_t p_index,
|
||||
const eastl::span<const std::byte> p_value,
|
||||
const CopyValue p_copy)
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
(p_index >= 1U) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
|
||||
"Invalid index for statement");
|
||||
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_bind_blob(m_statement,
|
||||
static_cast<int>(p_index),
|
||||
p_value.data(),
|
||||
static_cast<int>(p_value.size()),
|
||||
p_copy ? SQLITE_TRANSIENT : SQLITE_STATIC);
|
||||
ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to bind value for statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool BigFile::Request::Step()
|
||||
{
|
||||
const int result = sqlite3_step(m_statement);
|
||||
ASSERT(EngineAssertHandler,
|
||||
(result == SQLITE_DONE) || (result == SQLITE_ROW),
|
||||
"Failed to step through the statement: {}",
|
||||
sqlite3_errmsg(m_db));
|
||||
|
||||
return result == SQLITE_ROW;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t BigFile::Request::Execute()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_step(m_statement);
|
||||
ASSERT(EngineAssertHandler, (result == SQLITE_DONE), "Failed to execute the statement: {}", sqlite3_errmsg(m_db));
|
||||
|
||||
return sqlite3_changes(m_db);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::Column(const Request& p_request, const std::uint32_t p_index):
|
||||
m_statement(p_request.m_statement),
|
||||
m_index(p_index)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator std::int32_t() const
|
||||
{
|
||||
return sqlite3_column_int(m_statement, static_cast<int>(m_index));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator std::uint32_t() const
|
||||
{
|
||||
return sqlite3_column_int(m_statement, static_cast<int>(m_index));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator std::int64_t() const
|
||||
{
|
||||
return sqlite3_column_int64(m_statement, static_cast<int>(m_index));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator std::uint64_t() const
|
||||
{
|
||||
return std::bit_cast<std::uint64_t>(sqlite3_column_int64(m_statement, static_cast<int>(m_index)));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator float() const
|
||||
{
|
||||
return static_cast<float>(sqlite3_column_double(m_statement, static_cast<int>(m_index)));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator double() const
|
||||
{
|
||||
return sqlite3_column_double(m_statement, static_cast<int>(m_index));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator eastl::string_view() const
|
||||
{
|
||||
return eastl::string_view {
|
||||
reinterpret_cast<const char*>(sqlite3_column_text(m_statement, static_cast<int>(m_index))),
|
||||
static_cast<std::size_t>(sqlite3_column_bytes(m_statement, static_cast<int>(m_index)))};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator eastl::span<const std::byte>() const
|
||||
{
|
||||
return eastl::span<const std::byte> {
|
||||
static_cast<const std::byte*>(sqlite3_column_blob(m_statement, static_cast<int>(m_index))),
|
||||
static_cast<std::size_t>(sqlite3_column_bytes(m_statement, static_cast<int>(m_index)))};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column::operator std::span<const std::byte>() const
|
||||
{
|
||||
return std::span<const std::byte> {
|
||||
static_cast<const std::byte*>(sqlite3_column_blob(m_statement, static_cast<int>(m_index))),
|
||||
static_cast<std::size_t>(sqlite3_column_bytes(m_statement, static_cast<int>(m_index)))};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::Column BigFile::Request::Get(const std::uint32_t p_index) const
|
||||
{
|
||||
ASSERT(EngineAssertHandler,
|
||||
p_index < static_cast<std::uint32_t>(sqlite3_column_count(m_statement)),
|
||||
"Invalid index for column!");
|
||||
return {*this, p_index};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
BigFile::Request::~Request()
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int result = sqlite3_finalize(m_statement);
|
||||
CRITICAL_ASSERT(EngineAssertHandler, result == SQLITE_OK, "Failed to finalize statement: {}", sqlite3_errmsg(m_db));
|
||||
}
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
@@ -1,41 +1,38 @@
|
||||
PRAGMA journal_mode=WAL;
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
DROP TABLE IF EXISTS AssetHeader;
|
||||
CREATE TABLE IF NOT EXISTS AssetHeader (
|
||||
UUID BLOB NOT NULL UNIQUE,
|
||||
Name TEXT NOT NULL UNIQUE,
|
||||
TypeID INTEGER NOT NULL,
|
||||
TypeName TEXT NOT NULL,
|
||||
CREATE TABLE IF NOT EXISTS AssetMetadata (
|
||||
UUID BLOB NOT NULL,
|
||||
|
||||
CreateTime INTEGER NOT NULL DEFAULT(CAST(unixepoch('subsec') AS INTEGER) * 1000000),
|
||||
ModificationTime INTEGER NOT NULL DEFAULT(CAST(unixepoch('subsec') AS INTEGER) * 1000000),
|
||||
CreateTime INTEGER NOT NULL DEFAULT(CAST(unixepoch('subsec') * 1000000 AS INTEGER)),
|
||||
ModificationTime INTEGER NOT NULL DEFAULT(CAST(unixepoch('subsec') * 1000000 AS INTEGER)),
|
||||
|
||||
PRIMARY KEY(UUID)
|
||||
);
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS AssetHeader_UpdateTime
|
||||
AFTER UPDATE OF Name, TypeID, TypeName ON AssetHeader FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE AssetHeader
|
||||
SET ModificationTime = CAST(unixepoch('subsec') AS INTEGER) * 1000000
|
||||
WHERE UUID = NEW.UUID;
|
||||
END;
|
||||
|
||||
DROP TABLE IF EXISTS Asset;
|
||||
CREATE TABLE IF NOT EXISTS Asset (
|
||||
UUID BLOB NOT NULL UNIQUE,
|
||||
UUID BLOB NOT NULL,
|
||||
|
||||
Asset BLOB NOT NULL,
|
||||
|
||||
PRIMARY KEY(UUID),
|
||||
FOREIGN KEY(UUID) REFERENCES AssetHeader(UUID) ON DELETE CASCADE
|
||||
);
|
||||
FOREIGN KEY(UUID) REFERENCES AssetMetadata(UUID) ON DELETE CASCADE
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS Asset_UpdateTime
|
||||
AFTER UPDATE OF Asset ON Asset FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE AssetHeader
|
||||
SET ModificationTime = CAST(unixepoch('subsec') AS INTEGER) * 1000000
|
||||
UPDATE AssetMetadata
|
||||
SET ModificationTime = CAST(unixepoch('subsec') * 1000000 AS INTEGER)
|
||||
WHERE UUID = NEW.UUID;
|
||||
END;
|
||||
END;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS AssetHardReference (
|
||||
UUID BLOB NOT NULL,
|
||||
HardReference BLOB NOT NULL,
|
||||
|
||||
PRIMARY KEY(UUID, HardReference),
|
||||
FOREIGN KEY(UUID) REFERENCES AssetMetadata(UUID)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS AssetHardReference_ReferencedUUID ON AssetHardReference(HardReference);
|
||||
@@ -5,8 +5,7 @@ set(PublicDependencies
|
||||
SQLite::SQLite3)
|
||||
set(PrivateDependencies)
|
||||
set(BigfootPublicDependencies
|
||||
System
|
||||
Utils)
|
||||
System)
|
||||
set(BigfootPrivateDependencies)
|
||||
|
||||
set(BIGFOOT_MAJOR 0)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*********************************************************************
|
||||
* \file DataAccessor.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <Engine/DataAccessor/DataAccessor.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
void SetDataAccessor(DataAccessor* p_dataAccessor)
|
||||
{
|
||||
g_dataAccessor = p_dataAccessor;
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,21 @@
|
||||
include "Engine/Asset/AssetTypeID.fbs";
|
||||
|
||||
include "System/UUID/UUID.fbs";
|
||||
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
table AssetHeader
|
||||
{
|
||||
uuid: UUID (required, native_inline, id: 0);
|
||||
name: string (required, id: 1);
|
||||
type_id: AssetTypeID (required, native_inline, id: 2);
|
||||
type_name: string (required, id: 3);
|
||||
}
|
||||
|
||||
table Asset
|
||||
{
|
||||
header: AssetHeader (required, id: 0);
|
||||
inner_asset: [ubyte] (required, id: 1);
|
||||
}
|
||||
|
||||
root_type Asset;
|
||||
@@ -0,0 +1,214 @@
|
||||
/*********************************************************************
|
||||
* \file Asset.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date April 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSET_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSET_HPP
|
||||
#include <Engine/Asset/AssetBase.hpp>
|
||||
#include <Engine/Asset/AssetConcepts.hpp>
|
||||
#include <Engine/Asset/AssetContainer.hpp>
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
#include <Engine/Asset/Asset_generated.hpp>
|
||||
#include <Engine/EngineAssertHandler.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <flatbuffers/minireflect.h>
|
||||
#include <rapidhash.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
template<class ASSET>
|
||||
requires AssetTraits<ASSET>
|
||||
class Asset: public AssetBase
|
||||
{
|
||||
public:
|
||||
using FLAT_ASSET = ASSET::FLAT;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Will default initialize header
|
||||
*
|
||||
*/
|
||||
Asset()
|
||||
{
|
||||
m_header.type_id = GetTypeID();
|
||||
m_header.type_name = eastl::string {GetTypeName()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header of the asset
|
||||
*
|
||||
* \return The header of the asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const ::Flat::Bigfoot::AssetHeaderT& GetHeader() const override
|
||||
{
|
||||
return m_header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the asset
|
||||
*
|
||||
* \param p_name The name of the asset
|
||||
*/
|
||||
void SetName(const eastl::string_view p_name)
|
||||
{
|
||||
m_header.name = p_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset
|
||||
*
|
||||
* \return The asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const FLAT_ASSET::NativeTableType& GetAsset() const
|
||||
{
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset
|
||||
*
|
||||
* \return The asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
FLAT_ASSET::NativeTableType& GetAsset()
|
||||
{
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a flatbuffer to the asset
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer to unpack
|
||||
*/
|
||||
void Unpack(const eastl::span<const std::byte> p_flatbuffer) override
|
||||
{
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
flatbuffers::Verifier verifier {std::bit_cast<std::uint8_t*>(p_flatbuffer.data()), p_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>(p_flatbuffer.data());
|
||||
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
flatbuffers::Verifier innerAssetVerifier {asset->inner_asset()->data(), asset->inner_asset()->size()};
|
||||
const bool innerAssetVerified = innerAssetVerifier.VerifyBuffer<FLAT_ASSET>();
|
||||
CRITICAL_ASSERT(EngineAssertHandler, innerAssetVerified, "Given asset could not be verified!");
|
||||
#endif
|
||||
|
||||
asset->header()->UnPackTo(&m_header);
|
||||
flatbuffers::GetRoot<FLAT_ASSET>(asset->inner_asset()->data())->UnPackTo(&m_asset, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack the asset to a flatbuffer
|
||||
*
|
||||
* \return The packed asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<std::byte> Pack() const override
|
||||
{
|
||||
flatbuffers::FlatBufferBuilder innerAssetFbb;
|
||||
innerAssetFbb.Finish(FLAT_ASSET::Pack(innerAssetFbb, &m_asset));
|
||||
|
||||
flatbuffers::FlatBufferBuilder assetFbb;
|
||||
const auto header = ::Flat::Bigfoot::AssetHeader::Pack(assetFbb, &m_header);
|
||||
const auto innerAsset = assetFbb.CreateVector(innerAssetFbb.GetBufferPointer(), innerAssetFbb.GetSize());
|
||||
assetFbb.Finish(::Flat::Bigfoot::CreateAsset(assetFbb, header, innerAsset));
|
||||
|
||||
return eastl::vector<std::byte> {std::bit_cast<std::byte*>(assetFbb.GetBufferPointer()),
|
||||
std::bit_cast<std::byte*>(assetFbb.GetBufferPointer() + assetFbb.GetSize())};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HardReferences the flatbuffer has
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer
|
||||
* \return The UUIDs of the HardReferences
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> GetHardReferences(const eastl::span<const std::byte> p_flatbuffer) const override
|
||||
{
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
flatbuffers::Verifier verifier {std::bit_cast<std::uint8_t*>(p_flatbuffer.data()), p_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>(p_flatbuffer.data());
|
||||
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
flatbuffers::Verifier innerAssetVerifier {asset->inner_asset()->data(), asset->inner_asset()->size()};
|
||||
const bool innerAssetVerified = innerAssetVerifier.VerifyBuffer<FLAT_ASSET>();
|
||||
CRITICAL_ASSERT(EngineAssertHandler, innerAssetVerified, "Given asset could not be verified!");
|
||||
#endif
|
||||
|
||||
HardReferenceVisitor visitor;
|
||||
|
||||
flatbuffers::IterateFlatBuffer(asset->inner_asset()->data(), FLAT_ASSET::MiniReflectTypeTable(), &visitor);
|
||||
return visitor.GetUUIDs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AssetTypeID
|
||||
*
|
||||
* \return The AssetTypeIPD
|
||||
*/
|
||||
[[nodiscard]]
|
||||
static AssetTypeID GetTypeID()
|
||||
{
|
||||
static const AssetTypeID typeID = rapidhash(FLAT_ASSET::GetFullyQualifiedName(),
|
||||
std::strlen(FLAT_ASSET::GetFullyQualifiedName()));
|
||||
return typeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type name
|
||||
*
|
||||
* \return The type name
|
||||
*/
|
||||
[[nodiscard]]
|
||||
static eastl::string_view GetTypeName()
|
||||
{
|
||||
return FLAT_ASSET::GetFullyQualifiedName();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The asset
|
||||
*/
|
||||
FLAT_ASSET::NativeTableType m_asset;
|
||||
/**
|
||||
* The asset header
|
||||
*/
|
||||
::Flat::Bigfoot::AssetHeaderT m_header;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an asset and insert it into its corresponding AssetContainer
|
||||
*
|
||||
* \tparam ASSET The Asset type
|
||||
* \return The created asset, nullptr if no AssetContainer is registered for ASSET
|
||||
*/
|
||||
template<class ASSET>
|
||||
requires AssetType<ASSET>
|
||||
[[nodiscard]]
|
||||
ASSET* CreateAsset()
|
||||
{
|
||||
AssetContainerBase* container = g_activeAssetLibrary->GetAssetContainer(ASSET::GetTypeID());
|
||||
CRITICAL_ASSERT(EngineAssertHandler, container != nullptr, "No AssetContainer registered for this asset type!");
|
||||
|
||||
ContainerHandle* handle = container->Insert();
|
||||
return static_cast<ASSET*>(handle->GetAsset());
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/*********************************************************************
|
||||
* \file AssetBase.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date April 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETBASE_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETBASE_HPP
|
||||
#include <Engine/Asset/Asset_generated.hpp>
|
||||
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <flatbuffers/minireflect.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class AssetBase
|
||||
{
|
||||
public:
|
||||
virtual ~AssetBase() = default;
|
||||
|
||||
/**
|
||||
* Get the header of the asset
|
||||
*
|
||||
* \return The header of the asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual const ::Flat::Bigfoot::AssetHeaderT& GetHeader() const = 0;
|
||||
|
||||
/**
|
||||
* Get the HardReferences the flatbuffer has
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer
|
||||
* \return The UUIDs of the HardReferences
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<UUID> GetHardReferences(const eastl::span<const std::byte> p_flatbuffer) const = 0;
|
||||
|
||||
/**
|
||||
* Unpack a flatbuffer to the asset
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer to unpack
|
||||
*/
|
||||
virtual void Unpack(const eastl::span<const std::byte> p_flatbuffer) = 0;
|
||||
|
||||
/**
|
||||
* Pack the asset to a flatbuffer
|
||||
*
|
||||
* \return The packed asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<std::byte> Pack() const = 0;
|
||||
|
||||
AssetBase(const AssetBase& p_asset) = delete;
|
||||
AssetBase& operator=(const AssetBase& p_asset) = delete;
|
||||
|
||||
protected:
|
||||
AssetBase() = default;
|
||||
|
||||
AssetBase(AssetBase&& p_asset) = default;
|
||||
|
||||
AssetBase& operator=(AssetBase&& p_asset) = default;
|
||||
|
||||
class HardReferenceVisitor: public flatbuffers::IterationVisitor
|
||||
{
|
||||
public:
|
||||
void Field(size_t p_fieldIndex,
|
||||
size_t p_setIndex,
|
||||
const flatbuffers::ElementaryType p_type,
|
||||
const bool p_isVector,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
const char* p_name,
|
||||
const std::uint8_t* p_val) override;
|
||||
|
||||
void Element(size_t p_index,
|
||||
const flatbuffers::ElementaryType p_type,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
const std::uint8_t* p_val) override;
|
||||
|
||||
/**
|
||||
* Get the visited HardReference
|
||||
*
|
||||
* \return The visited HardReferences
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> GetUUIDs() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Visit an element in the flatbuffer
|
||||
*
|
||||
* \param p_type The element type
|
||||
* \param p_typeTable The element typetable
|
||||
* \param p_val The element
|
||||
*/
|
||||
void Visit(const flatbuffers::ElementaryType p_type,
|
||||
const flatbuffers::TypeTable* p_typeTable,
|
||||
const std::uint8_t* p_val);
|
||||
|
||||
/**
|
||||
* The HardReferences visited so far
|
||||
*/
|
||||
ankerl::unordered_dense::set<UUID> m_uuids;
|
||||
};
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*********************************************************************
|
||||
* \file AssetConcepts.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETCONCEPTS_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETCONCEPTS_HPP
|
||||
#include <Engine/Asset/AssetTypeID.hpp>
|
||||
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <flatbuffers/minireflect.h>
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class AssetBase;
|
||||
|
||||
/**
|
||||
* The traits struct passed to Asset<TRAITS> (e.g. AssetATraits).
|
||||
* Must name the generated flatbuffers table type for the asset.
|
||||
*/
|
||||
template<class TRAITS>
|
||||
concept AssetTraits = requires {
|
||||
typename TRAITS::FLAT;
|
||||
typename TRAITS::FLAT::NativeTableType;
|
||||
{ TRAITS::FLAT::GetFullyQualifiedName() } -> std::convertible_to<const char*>;
|
||||
{ TRAITS::FLAT::MiniReflectTypeTable() } -> std::convertible_to<const flatbuffers::TypeTable*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A concrete Asset type (e.g. AssetA), usable with AssetContainer, AssetLibrary and References.
|
||||
*/
|
||||
template<class ASSET>
|
||||
concept AssetType = std::derived_from<ASSET, AssetBase> && !std::same_as<ASSET, AssetBase> &&
|
||||
requires(const ASSET& p_asset) {
|
||||
{ ASSET::GetTypeID() } -> std::convertible_to<AssetTypeID>;
|
||||
{ p_asset.GetHeader().uuid } -> std::convertible_to<UUID>;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
@@ -0,0 +1,364 @@
|
||||
/*********************************************************************
|
||||
* \file AssetContainer.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date May 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
|
||||
#include <Engine/Asset/AssetBase.hpp>
|
||||
#include <Engine/Asset/AssetConcepts.hpp>
|
||||
#include <Engine/EngineAssertHandler.hpp>
|
||||
|
||||
#include <Utils/Containers/SlotMap.hpp>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* Increment the HardRef count
|
||||
*
|
||||
*/
|
||||
void IncrementHardRefCount();
|
||||
|
||||
/*
|
||||
* Decrement the HardRef count
|
||||
*
|
||||
*/
|
||||
void DecrementHardRefCount();
|
||||
|
||||
/*
|
||||
* Get the SoftRef count
|
||||
*
|
||||
* \return The SoftRef count.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetSoftRefCount() const;
|
||||
|
||||
/*
|
||||
* Increment the SoftRef count
|
||||
*
|
||||
*/
|
||||
void IncrementSoftRefCount();
|
||||
|
||||
/*
|
||||
* Decrement the SoftRef count
|
||||
*
|
||||
*/
|
||||
void DecrementSoftRefCount();
|
||||
|
||||
/*
|
||||
* Get the Asset
|
||||
*
|
||||
* \return The asset, can be nullptr
|
||||
*/
|
||||
[[nodiscard]]
|
||||
AssetBase* GetAsset();
|
||||
|
||||
/*
|
||||
* Get the Asset
|
||||
*
|
||||
* \return The asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const AssetBase* GetAsset() const;
|
||||
|
||||
/*
|
||||
* Set the asset
|
||||
*
|
||||
* \param p_asset The asset
|
||||
*/
|
||||
void SetAsset(AssetBase* p_asset);
|
||||
|
||||
private:
|
||||
/*
|
||||
* The HardRef count
|
||||
*/
|
||||
std::uint32_t m_hardRefCount = 0;
|
||||
|
||||
/*
|
||||
* The SoftRef count
|
||||
*/
|
||||
std::uint32_t m_softRefCount = 0;
|
||||
|
||||
/*
|
||||
* The asset
|
||||
*/
|
||||
AssetBase* m_asset = nullptr;
|
||||
|
||||
/**
|
||||
* The asset container
|
||||
*/
|
||||
AssetContainerBase* m_assetContainer = nullptr;
|
||||
|
||||
/**
|
||||
* The UUID of the asset
|
||||
*/
|
||||
UUID m_uuid = UUID::NULL_UUID;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
class AssetContainerBase
|
||||
{
|
||||
using SlotMap = StableSlotMap<ContainerHandle>;
|
||||
using SlotMapKey = SlotMap::SlotMapKey;
|
||||
|
||||
public:
|
||||
virtual ~AssetContainerBase() = default;
|
||||
|
||||
/*
|
||||
* Inserts an Asset to the AssetContainer
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer to unpack to the asset
|
||||
* \return The handle of the inserted asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual ContainerHandle* Insert(const eastl::span<const std::byte> p_flatbuffer) = 0;
|
||||
|
||||
/*
|
||||
* Inserts an Asset to the AssetContainer
|
||||
*
|
||||
* \return The handle of the inserted asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual ContainerHandle* Insert() = 0;
|
||||
|
||||
/*
|
||||
* Check if an Asset is in the AssetContainer or not
|
||||
*
|
||||
* \param p_uuid The UUID to check
|
||||
* \return True if in AssetContainer, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool Has(const UUID& p_uuid) const;
|
||||
|
||||
/*
|
||||
* Acquires a ContainerHandle for a given UUID
|
||||
* Will create it if it does not exists
|
||||
*
|
||||
* \param p_uuid The UUID to get ContainerHandle for
|
||||
* \return A ContainerHandle for the UUID
|
||||
*/
|
||||
[[nodiscard]]
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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=(const AssetContainerBase& p_container) = delete;
|
||||
AssetContainerBase& operator=(AssetContainerBase&& p_container) = delete;
|
||||
|
||||
AssetContainerBase(const AssetContainerBase& p_container) = delete;
|
||||
AssetContainerBase(AssetContainerBase&& p_container) = delete;
|
||||
|
||||
protected:
|
||||
struct Entry
|
||||
{
|
||||
/*
|
||||
* SlotMapKey to the handle
|
||||
*/
|
||||
SlotMapKey m_handleSlotKey;
|
||||
|
||||
/*
|
||||
* SlotMapKey to the asset
|
||||
*/
|
||||
SlotMapKey m_assetSlotKey;
|
||||
|
||||
/*
|
||||
* The handle itself, cached to avoid a SlotMap lookup on every access
|
||||
*/
|
||||
ContainerHandle* m_handle = nullptr;
|
||||
};
|
||||
|
||||
AssetContainerBase() = default;
|
||||
|
||||
/*
|
||||
* Finds an entry matching the Asset
|
||||
*
|
||||
* \param p_uuid The UUID to find
|
||||
* \return Pointer to an Entry, nullptr otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
Entry* FindEntry(const UUID& p_uuid);
|
||||
|
||||
/*
|
||||
* Finds an entry matching the Asset
|
||||
*
|
||||
* \param p_uuid The UUID to find
|
||||
* \return Pointer to an Entry, nullptr otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const Entry* FindEntry(const UUID& p_uuid) const;
|
||||
|
||||
/*
|
||||
* Finds an entry matching the Asset
|
||||
* Creates it if it does not exist
|
||||
*
|
||||
* \param p_uuid The UUID to find
|
||||
* \return The entry matching the Asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
Entry& GetOrCreateEntry(const UUID& p_uuid);
|
||||
|
||||
/*
|
||||
* Remove an asset
|
||||
*
|
||||
* \param p_key The key to the asset
|
||||
*/
|
||||
virtual void RemoveAsset(SlotMapKey p_slotKey) = 0;
|
||||
|
||||
private:
|
||||
/*
|
||||
* HandleContainers
|
||||
*/
|
||||
SlotMap m_handles;
|
||||
|
||||
/*
|
||||
* Every Asset added to the map
|
||||
*/
|
||||
ankerl::unordered_dense::map<UUID, Entry> m_uuidToEntry;
|
||||
|
||||
/**
|
||||
* Assets queued for removal
|
||||
*/
|
||||
eastl::vector<UUID> m_pendingRemovalUuids;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
template<class ASSET>
|
||||
requires AssetType<ASSET>
|
||||
class AssetContainer: public AssetContainerBase
|
||||
{
|
||||
using SlotMap = StableSlotMap<ASSET>;
|
||||
using SlotMapKey = SlotMap::SlotMapKey;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Inserts an Asset to the AssetContainer
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer to unpack to the asset
|
||||
* \return The handle of the inserted asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ContainerHandle* Insert(const eastl::span<const std::byte> p_flatbuffer) override
|
||||
{
|
||||
return InsertInternal(&p_flatbuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts an Asset to the AssetContainer
|
||||
*
|
||||
* \return The handle of the inserted asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ContainerHandle* Insert() override
|
||||
{
|
||||
return InsertInternal(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* Inserts an Asset to the AssetContainer, unpacking it from a flatbuffer if given one
|
||||
*
|
||||
* \param p_flatbuffer The flatbuffer to unpack to the asset, nullptr to default-construct the asset
|
||||
* \return The handle of the inserted asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ContainerHandle* InsertInternal(const eastl::span<const std::byte>* p_flatbuffer)
|
||||
{
|
||||
SlotMapKey slotKey = m_assets.Insert();
|
||||
ASSET* asset = m_assets.Get(slotKey);
|
||||
if (p_flatbuffer != nullptr)
|
||||
{
|
||||
asset->Unpack(*p_flatbuffer);
|
||||
}
|
||||
|
||||
Entry& entry = GetOrCreateEntry(asset->GetHeader().uuid);
|
||||
if (entry.m_assetSlotKey.Valid())
|
||||
{
|
||||
m_assets.Remove(slotKey);
|
||||
slotKey = entry.m_assetSlotKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.m_assetSlotKey = slotKey;
|
||||
entry.m_handle->SetAsset(asset);
|
||||
}
|
||||
|
||||
return entry.m_handle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an asset
|
||||
*
|
||||
* \param p_key The key to the asset
|
||||
*/
|
||||
void RemoveAsset(SlotMapKey p_key) override
|
||||
{
|
||||
m_assets.Remove(p_key);
|
||||
}
|
||||
|
||||
/*
|
||||
* The assets
|
||||
*/
|
||||
SlotMap m_assets;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/*********************************************************************
|
||||
* \file AssetHelper.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date April 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETHELPER_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETHELPER_HPP
|
||||
#include <Engine/Asset/AssetContainer.hpp>
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
#include <Engine/Asset/Reference.hpp>
|
||||
|
||||
#define BIGFOOT_ASSET(AssetName, AssetType) \
|
||||
namespace Bigfoot \
|
||||
{ \
|
||||
template<> \
|
||||
AssetTypeID GetAssetTypeID<AssetType>() \
|
||||
{ \
|
||||
return AssetType::GetTypeID(); \
|
||||
} \
|
||||
AssetContainer<AssetType> g_containerFor##AssetName; \
|
||||
const bool g_insertedContainerFor##AssetName = []() \
|
||||
{ \
|
||||
const bool inserted = g_assetLibrary.Insert(&g_containerFor##AssetName); \
|
||||
CRITICAL_ASSERT(EngineAssertHandler, \
|
||||
inserted, \
|
||||
"AssetTypeID collision or duplicate registration for asset type '" #AssetName "'"); \
|
||||
return inserted; \
|
||||
}(); \
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,154 @@
|
||||
/*********************************************************************
|
||||
* \file AssetLibrary.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date July 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETLIBRARY_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETLIBRARY_HPP
|
||||
#include <Engine/Asset/AssetConcepts.hpp>
|
||||
#include <Engine/Asset/AssetContainer.hpp>
|
||||
#include <Engine/Asset/AssetTypeID.hpp>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class AssetLibrary
|
||||
{
|
||||
public:
|
||||
AssetLibrary() = default;
|
||||
|
||||
AssetLibrary(const AssetLibrary& p_library) = default;
|
||||
AssetLibrary(AssetLibrary&& p_library) = delete;
|
||||
|
||||
/**
|
||||
* Insert an AssetContainer pointer to the library
|
||||
*
|
||||
* \tparam ASSET The Asset type
|
||||
* \param p_assetContainer The asset container
|
||||
* \return The AssetContainer for the Asset type. Nullptr if not found
|
||||
*/
|
||||
template<class ASSET>
|
||||
requires AssetType<ASSET>
|
||||
[[nodiscard]]
|
||||
bool Insert(AssetContainer<ASSET>* p_assetContainer)
|
||||
{
|
||||
static const AssetTypeID typeID = ASSET::GetTypeID();
|
||||
if (m_assetContainers.contains(typeID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto inserted = m_assetContainers.emplace(typeID, p_assetContainer);
|
||||
|
||||
return inserted.second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AssetContainer for a given AssetType
|
||||
*
|
||||
* \param p_typeID The asset type ID
|
||||
* \return The AssetContainer for the AssetType. Nullptr if not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID);
|
||||
|
||||
/**
|
||||
* Get the AssetContainer for a given AssetType
|
||||
*
|
||||
* \param p_typeID The asset type ID
|
||||
* \return The AssetContainer for the AssetType. Nullptr if not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID) const;
|
||||
|
||||
/**
|
||||
* Get the AssetContainer for a given UUID
|
||||
*
|
||||
* \param p_uuid The asset
|
||||
* \return The AssetContainer for the UUID. Nullptr if not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const AssetContainerBase* GetAssetContainer(const UUID& p_uuid) const;
|
||||
|
||||
/**
|
||||
* Get the AssetContainer for a given UUID
|
||||
*
|
||||
* \param p_uuid The asset
|
||||
* \return The AssetContainer for the UUID. Nullptr if not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
AssetContainerBase* GetAssetContainer(const UUID& p_uuid);
|
||||
|
||||
/**
|
||||
* Check if an Asset is already loaded in the library
|
||||
*
|
||||
* \param p_uuid The asset to check
|
||||
* \return True if already loaded, false otherwise
|
||||
*/
|
||||
[[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;
|
||||
|
||||
~AssetLibrary() = default;
|
||||
|
||||
private:
|
||||
// All the asset containers
|
||||
ankerl::unordered_dense::map<AssetTypeID, AssetContainerBase*> m_assetContainers;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
// The global asset library for Bigfoot
|
||||
inline AssetLibrary g_assetLibrary;
|
||||
inline AssetLibrary* g_activeAssetLibrary = &g_assetLibrary;
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
/*
|
||||
* Set the active asset library
|
||||
*
|
||||
* \param p_assetLibrary The asset library to set g_activeAssetLibrary to.
|
||||
*/
|
||||
void SetActiveAssetLibrary(AssetLibrary* p_assetLibrary);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
class ScopedActiveAssetLibrary
|
||||
{
|
||||
public:
|
||||
ScopedActiveAssetLibrary(AssetLibrary* p_assetLibrary):
|
||||
m_previous(g_activeAssetLibrary)
|
||||
{
|
||||
SetActiveAssetLibrary(p_assetLibrary);
|
||||
}
|
||||
|
||||
ScopedActiveAssetLibrary(const ScopedActiveAssetLibrary& p_scopedActiveAssetLibrary) = delete;
|
||||
ScopedActiveAssetLibrary(ScopedActiveAssetLibrary&& p_scopedActiveAssetLibrary) = delete;
|
||||
|
||||
~ScopedActiveAssetLibrary()
|
||||
{
|
||||
SetActiveAssetLibrary(m_previous);
|
||||
}
|
||||
|
||||
ScopedActiveAssetLibrary& operator=(const ScopedActiveAssetLibrary& p_scopedActiveAssetLibrary) = delete;
|
||||
ScopedActiveAssetLibrary& operator=(ScopedActiveAssetLibrary&& p_scopedActiveAssetLibrary) = delete;
|
||||
|
||||
private:
|
||||
/*
|
||||
* The previously active asset library.
|
||||
*/
|
||||
AssetLibrary* m_previous = nullptr;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
native_include "Engine/Asset/AssetTypeID.hpp";
|
||||
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
struct AssetTypeID (native_type: "::Bigfoot::AssetTypeID")
|
||||
{
|
||||
value: ulong;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*********************************************************************
|
||||
* \file AssetTypeID.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date February 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_ASSETTYPEID_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_ASSETTYPEID_HPP
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
using AssetTypeID = std::uint64_t;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace Flat::Bigfoot
|
||||
{
|
||||
struct AssetTypeID;
|
||||
} // namespace Flat::Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::AssetTypeID Pack(const Bigfoot::AssetTypeID& p_assetTypeID);
|
||||
Bigfoot::AssetTypeID UnPack(const Flat::Bigfoot::AssetTypeID& p_flatAssetTypeID);
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_ASSETTYPEID_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_ASSETTYPEID_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "Engine/Asset/AssetTypeID.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct AssetTypeID;
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetTypeIDTypeTable();
|
||||
|
||||
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) AssetTypeID FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
uint64_t value_;
|
||||
|
||||
public:
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return AssetTypeIDTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.AssetTypeID";
|
||||
}
|
||||
AssetTypeID()
|
||||
: value_(0) {
|
||||
}
|
||||
AssetTypeID(uint64_t _value)
|
||||
: value_(::flatbuffers::EndianScalar(_value)) {
|
||||
}
|
||||
uint64_t value() const {
|
||||
return ::flatbuffers::EndianScalar(value_);
|
||||
}
|
||||
};
|
||||
FLATBUFFERS_STRUCT_END(AssetTypeID, 8);
|
||||
|
||||
struct AssetTypeID::Traits {
|
||||
using type = AssetTypeID;
|
||||
};
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetTypeIDTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_ULONG, 0, -1 }
|
||||
};
|
||||
static const int64_t values[] = { 0, 8 };
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_ASSETTYPEID_FLAT_BIGFOOT_H_
|
||||
@@ -0,0 +1,398 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_ASSET_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_ASSET_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "Engine/Asset/AssetTypeID.hpp"
|
||||
#include "System/UUID/UUID.hpp"
|
||||
#include "Engine/Asset/AssetTypeID_generated.hpp"
|
||||
#include "System/UUID/UUID_generated.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct AssetHeader;
|
||||
struct AssetHeaderBuilder;
|
||||
struct AssetHeaderT;
|
||||
|
||||
struct Asset;
|
||||
struct AssetBuilder;
|
||||
struct AssetT;
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetHeaderTypeTable();
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetTypeTable();
|
||||
|
||||
struct AssetHeaderT : public ::flatbuffers::NativeTable {
|
||||
typedef AssetHeader TableType;
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.AssetHeaderT";
|
||||
}
|
||||
::Bigfoot::UUID uuid{};
|
||||
eastl::string name{};
|
||||
::Bigfoot::AssetTypeID type_id{};
|
||||
eastl::string type_name{};
|
||||
};
|
||||
|
||||
struct AssetHeader FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef AssetHeaderT NativeTableType;
|
||||
typedef AssetHeaderBuilder Builder;
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return AssetHeaderTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.AssetHeader";
|
||||
}
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_UUID = 4,
|
||||
VT_NAME = 6,
|
||||
VT_TYPE_ID = 8,
|
||||
VT_TYPE_NAME = 10
|
||||
};
|
||||
const Flat::Bigfoot::UUID *uuid() const {
|
||||
return GetStruct<const Flat::Bigfoot::UUID *>(VT_UUID);
|
||||
}
|
||||
const ::flatbuffers::String *name() const {
|
||||
return GetPointer<const ::flatbuffers::String *>(VT_NAME);
|
||||
}
|
||||
const Flat::Bigfoot::AssetTypeID *type_id() const {
|
||||
return GetStruct<const Flat::Bigfoot::AssetTypeID *>(VT_TYPE_ID);
|
||||
}
|
||||
const ::flatbuffers::String *type_name() const {
|
||||
return GetPointer<const ::flatbuffers::String *>(VT_TYPE_NAME);
|
||||
}
|
||||
template <bool B = false>
|
||||
bool Verify(::flatbuffers::VerifierTemplate<B> &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyFieldRequired<Flat::Bigfoot::UUID>(verifier, VT_UUID, 1) &&
|
||||
VerifyOffsetRequired(verifier, VT_NAME) &&
|
||||
verifier.VerifyString(name()) &&
|
||||
VerifyFieldRequired<Flat::Bigfoot::AssetTypeID>(verifier, VT_TYPE_ID, 8) &&
|
||||
VerifyOffsetRequired(verifier, VT_TYPE_NAME) &&
|
||||
verifier.VerifyString(type_name()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
AssetHeaderT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
void UnPackTo(AssetHeaderT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
static ::flatbuffers::Offset<AssetHeader> Pack(::flatbuffers::FlatBufferBuilder &_fbb, const AssetHeaderT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
};
|
||||
|
||||
struct AssetHeaderBuilder {
|
||||
typedef AssetHeader Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_uuid(const Flat::Bigfoot::UUID *uuid) {
|
||||
fbb_.AddStruct(AssetHeader::VT_UUID, uuid);
|
||||
}
|
||||
void add_name(::flatbuffers::Offset<::flatbuffers::String> name) {
|
||||
fbb_.AddOffset(AssetHeader::VT_NAME, name);
|
||||
}
|
||||
void add_type_id(const Flat::Bigfoot::AssetTypeID *type_id) {
|
||||
fbb_.AddStruct(AssetHeader::VT_TYPE_ID, type_id);
|
||||
}
|
||||
void add_type_name(::flatbuffers::Offset<::flatbuffers::String> type_name) {
|
||||
fbb_.AddOffset(AssetHeader::VT_TYPE_NAME, type_name);
|
||||
}
|
||||
explicit AssetHeaderBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<AssetHeader> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<AssetHeader>(end);
|
||||
fbb_.Required(o, AssetHeader::VT_UUID);
|
||||
fbb_.Required(o, AssetHeader::VT_NAME);
|
||||
fbb_.Required(o, AssetHeader::VT_TYPE_ID);
|
||||
fbb_.Required(o, AssetHeader::VT_TYPE_NAME);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<AssetHeader> CreateAssetHeader(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const Flat::Bigfoot::UUID *uuid = nullptr,
|
||||
::flatbuffers::Offset<::flatbuffers::String> name = 0,
|
||||
const Flat::Bigfoot::AssetTypeID *type_id = nullptr,
|
||||
::flatbuffers::Offset<::flatbuffers::String> type_name = 0) {
|
||||
AssetHeaderBuilder builder_(_fbb);
|
||||
builder_.add_type_name(type_name);
|
||||
builder_.add_type_id(type_id);
|
||||
builder_.add_name(name);
|
||||
builder_.add_uuid(uuid);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AssetHeader::Traits {
|
||||
using type = AssetHeader;
|
||||
static auto constexpr Create = CreateAssetHeader;
|
||||
};
|
||||
|
||||
::flatbuffers::Offset<AssetHeader> CreateAssetHeader(::flatbuffers::FlatBufferBuilder &_fbb, const AssetHeaderT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
|
||||
struct AssetT : public ::flatbuffers::NativeTable {
|
||||
typedef Asset TableType;
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.AssetT";
|
||||
}
|
||||
eastl::unique_ptr<Flat::Bigfoot::AssetHeaderT> header{new Flat::Bigfoot::AssetHeaderT()};
|
||||
eastl::vector<uint8_t> inner_asset{};
|
||||
AssetT() = default;
|
||||
AssetT(const AssetT &o);
|
||||
AssetT(AssetT&&) FLATBUFFERS_NOEXCEPT = default;
|
||||
AssetT &operator=(AssetT o) FLATBUFFERS_NOEXCEPT;
|
||||
};
|
||||
|
||||
struct Asset FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef AssetT NativeTableType;
|
||||
typedef AssetBuilder Builder;
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return AssetTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.Asset";
|
||||
}
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_HEADER = 4,
|
||||
VT_INNER_ASSET = 6
|
||||
};
|
||||
const Flat::Bigfoot::AssetHeader *header() const {
|
||||
return GetPointer<const Flat::Bigfoot::AssetHeader *>(VT_HEADER);
|
||||
}
|
||||
const ::flatbuffers::Vector<uint8_t> *inner_asset() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_INNER_ASSET);
|
||||
}
|
||||
template <bool B = false>
|
||||
bool Verify(::flatbuffers::VerifierTemplate<B> &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffsetRequired(verifier, VT_HEADER) &&
|
||||
verifier.VerifyTable(header()) &&
|
||||
VerifyOffsetRequired(verifier, VT_INNER_ASSET) &&
|
||||
verifier.VerifyVector(inner_asset()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
AssetT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
void UnPackTo(AssetT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
|
||||
static ::flatbuffers::Offset<Asset> Pack(::flatbuffers::FlatBufferBuilder &_fbb, const AssetT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
};
|
||||
|
||||
struct AssetBuilder {
|
||||
typedef Asset Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_header(::flatbuffers::Offset<Flat::Bigfoot::AssetHeader> header) {
|
||||
fbb_.AddOffset(Asset::VT_HEADER, header);
|
||||
}
|
||||
void add_inner_asset(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> inner_asset) {
|
||||
fbb_.AddOffset(Asset::VT_INNER_ASSET, inner_asset);
|
||||
}
|
||||
explicit AssetBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<Asset> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<Asset>(end);
|
||||
fbb_.Required(o, Asset::VT_HEADER);
|
||||
fbb_.Required(o, Asset::VT_INNER_ASSET);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<Asset> CreateAsset(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::AssetHeader> header = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> inner_asset = 0) {
|
||||
AssetBuilder builder_(_fbb);
|
||||
builder_.add_inner_asset(inner_asset);
|
||||
builder_.add_header(header);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Asset::Traits {
|
||||
using type = Asset;
|
||||
static auto constexpr Create = CreateAsset;
|
||||
};
|
||||
|
||||
::flatbuffers::Offset<Asset> CreateAsset(::flatbuffers::FlatBufferBuilder &_fbb, const AssetT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr);
|
||||
|
||||
inline AssetHeaderT *AssetHeader::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
auto _o = std::make_unique<AssetHeaderT>();
|
||||
UnPackTo(_o.get(), _resolver);
|
||||
return _o.release();
|
||||
}
|
||||
|
||||
inline void AssetHeader::UnPackTo(AssetHeaderT *_o, const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
(void)_o;
|
||||
(void)_resolver;
|
||||
{ auto _e = uuid(); if (_e) _o->uuid = ::flatbuffers::UnPack(*_e); }
|
||||
{ auto _e = name(); if (_e) _o->name = eastl::string(_e->c_str(), _e->size()); }
|
||||
{ auto _e = type_id(); if (_e) _o->type_id = ::flatbuffers::UnPack(*_e); }
|
||||
{ auto _e = type_name(); if (_e) _o->type_name = eastl::string(_e->c_str(), _e->size()); }
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<AssetHeader> CreateAssetHeader(::flatbuffers::FlatBufferBuilder &_fbb, const AssetHeaderT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
return AssetHeader::Pack(_fbb, _o, _rehasher);
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<AssetHeader> AssetHeader::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const AssetHeaderT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
(void)_rehasher;
|
||||
(void)_o;
|
||||
struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const AssetHeaderT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
|
||||
auto _uuid = ::flatbuffers::Pack(_o->uuid);
|
||||
auto _name = _fbb.CreateString(_o->name);
|
||||
auto _type_id = ::flatbuffers::Pack(_o->type_id);
|
||||
auto _type_name = _fbb.CreateString(_o->type_name);
|
||||
return Flat::Bigfoot::CreateAssetHeader(
|
||||
_fbb,
|
||||
&_uuid,
|
||||
_name,
|
||||
&_type_id,
|
||||
_type_name);
|
||||
}
|
||||
|
||||
inline AssetT::AssetT(const AssetT &o)
|
||||
: header((o.header) ? new Flat::Bigfoot::AssetHeaderT(*o.header) : nullptr),
|
||||
inner_asset(o.inner_asset) {
|
||||
}
|
||||
|
||||
inline AssetT &AssetT::operator=(AssetT o) FLATBUFFERS_NOEXCEPT {
|
||||
std::swap(header, o.header);
|
||||
std::swap(inner_asset, o.inner_asset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AssetT *Asset::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
auto _o = std::make_unique<AssetT>();
|
||||
UnPackTo(_o.get(), _resolver);
|
||||
return _o.release();
|
||||
}
|
||||
|
||||
inline void Asset::UnPackTo(AssetT *_o, const ::flatbuffers::resolver_function_t *_resolver) const {
|
||||
(void)_o;
|
||||
(void)_resolver;
|
||||
{ auto _e = header(); if (_e) { if(_o->header) { _e->UnPackTo(_o->header.get(), _resolver); } else { _o->header = eastl::unique_ptr<Flat::Bigfoot::AssetHeaderT>(_e->UnPack(_resolver)); } } else if (_o->header) { _o->header.reset(); } }
|
||||
{ auto _e = inner_asset(); if (_e) { _o->inner_asset.resize(_e->size()); std::copy(_e->begin(), _e->end(), _o->inner_asset.begin()); } }
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<Asset> CreateAsset(::flatbuffers::FlatBufferBuilder &_fbb, const AssetT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
return Asset::Pack(_fbb, _o, _rehasher);
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<Asset> Asset::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const AssetT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) {
|
||||
(void)_rehasher;
|
||||
(void)_o;
|
||||
struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const AssetT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
|
||||
auto _header = _o->header ? CreateAssetHeader(_fbb, _o->header.get(), _rehasher) : 0;
|
||||
auto _inner_asset = _fbb.CreateVector(_o->inner_asset.data(), _o->inner_asset.size());
|
||||
return Flat::Bigfoot::CreateAsset(
|
||||
_fbb,
|
||||
_header,
|
||||
_inner_asset);
|
||||
}
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetHeaderTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_SEQUENCE, 0, 0 },
|
||||
{ ::flatbuffers::ET_STRING, 0, -1 },
|
||||
{ ::flatbuffers::ET_SEQUENCE, 0, 1 },
|
||||
{ ::flatbuffers::ET_STRING, 0, -1 }
|
||||
};
|
||||
static const ::flatbuffers::TypeFunction type_refs[] = {
|
||||
Flat::Bigfoot::UUIDTypeTable,
|
||||
Flat::Bigfoot::AssetTypeIDTypeTable
|
||||
};
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_TABLE, 4, type_codes, type_refs, nullptr, nullptr, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
inline const ::flatbuffers::TypeTable *AssetTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_SEQUENCE, 0, 0 },
|
||||
{ ::flatbuffers::ET_UCHAR, 1, -1 }
|
||||
};
|
||||
static const ::flatbuffers::TypeFunction type_refs[] = {
|
||||
Flat::Bigfoot::AssetHeaderTypeTable
|
||||
};
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::Asset *GetAsset(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Flat::Bigfoot::Asset>(buf);
|
||||
}
|
||||
|
||||
inline const Flat::Bigfoot::Asset *GetSizePrefixedAsset(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Flat::Bigfoot::Asset>(buf);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifyAssetBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifyBuffer<Flat::Bigfoot::Asset>(nullptr);
|
||||
}
|
||||
|
||||
template <bool B = false>
|
||||
inline bool VerifySizePrefixedAssetBuffer(
|
||||
::flatbuffers::VerifierTemplate<B> &verifier) {
|
||||
return verifier.template VerifySizePrefixedBuffer<Flat::Bigfoot::Asset>(nullptr);
|
||||
}
|
||||
|
||||
inline const char *AssetExtension() {
|
||||
return "bfbs";
|
||||
}
|
||||
|
||||
inline void FinishAssetBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::Asset> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedAssetBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Flat::Bigfoot::Asset> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::AssetT> UnPackAsset(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::AssetT>(GetAsset(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
inline eastl::unique_ptr<Flat::Bigfoot::AssetT> UnPackSizePrefixedAsset(
|
||||
const void *buf,
|
||||
const ::flatbuffers::resolver_function_t *res = nullptr) {
|
||||
return eastl::unique_ptr<Flat::Bigfoot::AssetT>(GetSizePrefixedAsset(buf)->UnPack(res));
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_ASSET_FLAT_BIGFOOT_H_
|
||||
@@ -0,0 +1,15 @@
|
||||
include "System/UUID/UUID.fbs";
|
||||
|
||||
native_include "Engine/Asset/Reference.hpp";
|
||||
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
struct HardReference (bigfoot_ref_wrapper: "::Bigfoot::HardReference")
|
||||
{
|
||||
uuid: UUID;
|
||||
}
|
||||
|
||||
struct SoftReference (bigfoot_ref_wrapper: "::Bigfoot::SoftReference")
|
||||
{
|
||||
uuid: UUID;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*********************************************************************
|
||||
* \file Reference.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date April 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ASSET_REFERENCE_HPP
|
||||
#define BIGFOOT_ENGINE_ASSET_REFERENCE_HPP
|
||||
#include <Engine/Asset/AssetConcepts.hpp>
|
||||
#include <Engine/Asset/AssetLibrary.hpp>
|
||||
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
template<class ASSET>
|
||||
AssetTypeID GetAssetTypeID();
|
||||
|
||||
template<>
|
||||
AssetTypeID GetAssetTypeID<AssetBase>() = delete;
|
||||
|
||||
template<class DERIVED, class ASSET>
|
||||
class ReferenceBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Get the UUID of the reference
|
||||
*
|
||||
* \return The UUID of the reference
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const UUID& GetUUID() const
|
||||
{
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset
|
||||
*
|
||||
* \return The asset, nullptr if it is not loaded
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const ASSET* Get() const
|
||||
{
|
||||
static_assert(AssetType<ASSET>, "ASSET must derive from AssetBase (and not be AssetBase itself)");
|
||||
return m_handle ? static_cast<const ASSET*>(m_handle->GetAsset()) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset
|
||||
*
|
||||
* \return The asset, nullptr if it is not loaded
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ASSET* Get()
|
||||
{
|
||||
static_assert(AssetType<ASSET>, "ASSET must derive from AssetBase (and not be AssetBase itself)");
|
||||
return m_handle ? static_cast<ASSET*>(m_handle->GetAsset()) : nullptr;
|
||||
}
|
||||
|
||||
~ReferenceBase()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
ReferenceBase& operator=(const ReferenceBase& p_reference)
|
||||
{
|
||||
if (this != &p_reference)
|
||||
{
|
||||
Release();
|
||||
m_uuid = p_reference.m_uuid;
|
||||
m_handle = p_reference.m_handle;
|
||||
Acquire();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReferenceBase& operator=(ReferenceBase&& p_reference) noexcept
|
||||
{
|
||||
if (this != &p_reference)
|
||||
{
|
||||
Release();
|
||||
m_uuid = std::exchange(p_reference.m_uuid, UUID::NULL_UUID);
|
||||
m_handle = std::exchange(p_reference.m_handle, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool operator==(const ReferenceBase& p_reference) const
|
||||
{
|
||||
return m_uuid == p_reference.m_uuid;
|
||||
}
|
||||
|
||||
private:
|
||||
friend DERIVED;
|
||||
|
||||
ReferenceBase() = default;
|
||||
|
||||
ReferenceBase(const UUID& p_uuid):
|
||||
m_uuid(p_uuid)
|
||||
{
|
||||
Acquire();
|
||||
}
|
||||
|
||||
ReferenceBase(const ReferenceBase& p_reference):
|
||||
m_uuid(p_reference.m_uuid),
|
||||
m_handle(p_reference.m_handle)
|
||||
{
|
||||
Acquire();
|
||||
}
|
||||
|
||||
ReferenceBase(ReferenceBase&& p_reference) noexcept:
|
||||
m_uuid(std::exchange(p_reference.m_uuid, UUID::NULL_UUID)),
|
||||
m_handle(std::exchange(p_reference.m_handle, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated handle container for the asset
|
||||
* Register the reference inside the handle
|
||||
*
|
||||
*/
|
||||
void Acquire()
|
||||
{
|
||||
if (!m_uuid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AssetContainerBase* assetContainer = g_activeAssetLibrary->GetAssetContainer(GetAssetTypeID<ASSET>()))
|
||||
{
|
||||
if (!m_handle)
|
||||
{
|
||||
m_handle = assetContainer->AcquireContainerHandle(m_uuid);
|
||||
}
|
||||
DERIVED::IncrementCount(*m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the reference from the handle
|
||||
*
|
||||
*/
|
||||
void Release()
|
||||
{
|
||||
if (m_handle)
|
||||
{
|
||||
DERIVED::DecrementCount(*m_handle);
|
||||
m_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The UUID
|
||||
*/
|
||||
UUID m_uuid = UUID::NULL_UUID;
|
||||
|
||||
/**
|
||||
* Handle for the asset
|
||||
*/
|
||||
ContainerHandle* m_handle = nullptr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Owning reference
|
||||
* Keeps the asset alive
|
||||
*/
|
||||
template<class ASSET>
|
||||
class HardReference: public ReferenceBase<HardReference<ASSET>, ASSET>
|
||||
{
|
||||
using Base = ReferenceBase<HardReference<ASSET>, ASSET>;
|
||||
|
||||
public:
|
||||
HardReference() = default;
|
||||
|
||||
HardReference(const UUID& p_uuid):
|
||||
Base(p_uuid)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Increment the hard ref count of the handle
|
||||
*
|
||||
* \param p_handle The handle for the asset
|
||||
*/
|
||||
static void IncrementCount(ContainerHandle& p_handle)
|
||||
{
|
||||
p_handle.IncrementHardRefCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the hard ref count of the handle
|
||||
*
|
||||
* \param p_handle The handle for the asset
|
||||
*/
|
||||
static void DecrementCount(ContainerHandle& p_handle)
|
||||
{
|
||||
p_handle.DecrementHardRefCount();
|
||||
}
|
||||
|
||||
friend Base;
|
||||
};
|
||||
|
||||
template<class ASSET>
|
||||
class SoftReference: public ReferenceBase<SoftReference<ASSET>, ASSET>
|
||||
{
|
||||
using Base = ReferenceBase<SoftReference<ASSET>, ASSET>;
|
||||
|
||||
public:
|
||||
SoftReference() = default;
|
||||
|
||||
SoftReference(const UUID& p_uuid):
|
||||
Base(p_uuid)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Increment the soft ref count of the handle
|
||||
*
|
||||
* \param p_handle The handle for the asset
|
||||
*/
|
||||
static void IncrementCount(ContainerHandle& p_handle)
|
||||
{
|
||||
p_handle.IncrementSoftRefCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the hard ref count of the handle
|
||||
*
|
||||
* \param p_handle The handle for the asset
|
||||
*/
|
||||
static void DecrementCount(ContainerHandle& p_handle)
|
||||
{
|
||||
p_handle.DecrementSoftRefCount();
|
||||
}
|
||||
|
||||
friend Base;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_REFERENCE_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_REFERENCE_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "System/UUID/UUID.hpp"
|
||||
#include "Engine/Asset/Reference.hpp"
|
||||
#include "System/UUID/UUID_generated.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct HardReference;
|
||||
|
||||
struct SoftReference;
|
||||
|
||||
inline const ::flatbuffers::TypeTable *HardReferenceTypeTable();
|
||||
|
||||
inline const ::flatbuffers::TypeTable *SoftReferenceTypeTable();
|
||||
|
||||
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(1) HardReference FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
Flat::Bigfoot::UUID uuid_;
|
||||
|
||||
public:
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return HardReferenceTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.HardReference";
|
||||
}
|
||||
HardReference()
|
||||
: uuid_() {
|
||||
}
|
||||
HardReference(const Flat::Bigfoot::UUID &_uuid)
|
||||
: uuid_(_uuid) {
|
||||
}
|
||||
const Flat::Bigfoot::UUID &uuid() const {
|
||||
return uuid_;
|
||||
}
|
||||
};
|
||||
FLATBUFFERS_STRUCT_END(HardReference, 16);
|
||||
|
||||
struct HardReference::Traits {
|
||||
using type = HardReference;
|
||||
};
|
||||
|
||||
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(1) SoftReference FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
Flat::Bigfoot::UUID uuid_;
|
||||
|
||||
public:
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return SoftReferenceTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.SoftReference";
|
||||
}
|
||||
SoftReference()
|
||||
: uuid_() {
|
||||
}
|
||||
SoftReference(const Flat::Bigfoot::UUID &_uuid)
|
||||
: uuid_(_uuid) {
|
||||
}
|
||||
const Flat::Bigfoot::UUID &uuid() const {
|
||||
return uuid_;
|
||||
}
|
||||
};
|
||||
FLATBUFFERS_STRUCT_END(SoftReference, 16);
|
||||
|
||||
struct SoftReference::Traits {
|
||||
using type = SoftReference;
|
||||
};
|
||||
|
||||
inline const ::flatbuffers::TypeTable *HardReferenceTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_SEQUENCE, 0, 0 }
|
||||
};
|
||||
static const ::flatbuffers::TypeFunction type_refs[] = {
|
||||
Flat::Bigfoot::UUIDTypeTable
|
||||
};
|
||||
static const int64_t values[] = { 0, 16 };
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, type_refs, nullptr, values, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
inline const ::flatbuffers::TypeTable *SoftReferenceTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_SEQUENCE, 0, 0 }
|
||||
};
|
||||
static const ::flatbuffers::TypeFunction type_refs[] = {
|
||||
Flat::Bigfoot::UUIDTypeTable
|
||||
};
|
||||
static const int64_t values[] = { 0, 16 };
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, type_refs, nullptr, values, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_REFERENCE_FLAT_BIGFOOT_H_
|
||||
@@ -0,0 +1,384 @@
|
||||
/*********************************************************************
|
||||
* \file BigFile.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_BIGFILE_BIGFILE_HPP
|
||||
#define BIGFOOT_ENGINE_BIGFILE_BIGFILE_HPP
|
||||
#include <Engine/DataAccessor/DataAccessor.hpp>
|
||||
|
||||
#include <System/File/File.hpp>
|
||||
#include <System/Time/Time.hpp>
|
||||
|
||||
#include <Utils/TaggedType.hpp>
|
||||
|
||||
#include <EASTL/optional.h>
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/string_view.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <span>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
class BigFile: public DataAccessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param p_file The file targetting the BigFile DB
|
||||
*/
|
||||
BigFile(const File& p_file);
|
||||
|
||||
BigFile(const BigFile& p_bigFile) = delete;
|
||||
BigFile(BigFile&& p_bigFile) = delete;
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
*/
|
||||
void BeginTransaction();
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
*/
|
||||
void CommitTransaction();
|
||||
|
||||
/**
|
||||
* Rollback a transaction
|
||||
*
|
||||
*/
|
||||
void RollbackTransaction();
|
||||
|
||||
/**
|
||||
* Clear the BigFile
|
||||
*
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
~BigFile() override;
|
||||
|
||||
/**
|
||||
* Get an Asset flatbuffer
|
||||
*
|
||||
* \param p_uuid The asset to get the flatbuffer of
|
||||
* \return The flatbuffer, or an empty vector if the asset was not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
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
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::vector<std::byte>> GetAssetFlatbuffers(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of a given asset
|
||||
*
|
||||
* \param p_uuid The asset to get the HardReferences of
|
||||
* \return The HardReferences of the asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> GetAssetHardReferences(const UUID& p_uuid) const override;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of given assets
|
||||
*
|
||||
* \param p_uuids The assets to get the HardReferences of
|
||||
* \return The HardReferences of the assets
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::vector<UUID>> GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
struct AssetTime
|
||||
{
|
||||
Time m_creationTime;
|
||||
Time m_modificationTime;
|
||||
|
||||
[[nodiscard]]
|
||||
bool operator==(const AssetTime& p_other) const = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the creation and modification time of an asset
|
||||
*
|
||||
* \param p_uuid The asset to get the time of
|
||||
* \return The time of the asset, nullopt if the asset was not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::optional<AssetTime> GetAssetTime(const UUID& p_uuid) const;
|
||||
|
||||
/**
|
||||
* Get the creation and modification time of several assets
|
||||
*
|
||||
* \param p_uuids The assets to get the time of
|
||||
* \return The time of the assets. nullopt for assets not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::optional<AssetTime>> GetAssetsTime(eastl::span<const UUID> p_uuids) const;
|
||||
|
||||
/**
|
||||
* Save an asset
|
||||
*
|
||||
* \param p_uuid The UUID
|
||||
* \param p_flatbuffer The flatbuffer of the asset
|
||||
* \param p_hardReferences The HardReferences the asset points to
|
||||
*/
|
||||
void SaveAssetFlatbuffer(const UUID& p_uuid,
|
||||
const eastl::span<const std::byte> p_flatbuffer,
|
||||
const eastl::span<const UUID> p_hardReferences) override;
|
||||
|
||||
/**
|
||||
* Save several assets
|
||||
*
|
||||
* \param p_uuids The UUIDs
|
||||
* \param p_flatbuffers The flatbuffers of the assets, matching p_metadatas by index
|
||||
* \param p_hardReferences The HardReferences the asset points to
|
||||
*/
|
||||
void SaveAssetFlatbuffers(const eastl::span<const UUID> p_uuids,
|
||||
const eastl::span<const eastl::vector<std::byte>> p_flatbuffers,
|
||||
const eastl::span<const eastl::vector<UUID>> p_hardReferences) override;
|
||||
|
||||
/**
|
||||
* Check if the Asset exists
|
||||
*
|
||||
* \param p_uuid The asset to check
|
||||
* \param True if it exists, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool Has(const UUID& p_uuid) const override;
|
||||
|
||||
BigFile& operator=(const BigFile& p_bigFile) = delete;
|
||||
BigFile& operator=(BigFile&& p_bigFile) = delete;
|
||||
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Constructor
|
||||
*
|
||||
* \param p_bigFile The bigfile
|
||||
* \param p_request The SQL request
|
||||
*/
|
||||
Request(const BigFile& p_bigFile, const eastl::string_view p_request);
|
||||
|
||||
Request(const Request& p_request) = delete;
|
||||
Request(Request&& p_request) = delete;
|
||||
|
||||
using CopyValue = TaggedType<bool>;
|
||||
|
||||
/*
|
||||
* Bind a int32 value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const std::int32_t p_value);
|
||||
|
||||
/*
|
||||
* Bind a uint32 value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const std::uint32_t p_value);
|
||||
|
||||
/*
|
||||
* Bind a int64 value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const std::int64_t p_value);
|
||||
|
||||
/*
|
||||
* Bind a uint64 value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const std::uint64_t p_value);
|
||||
|
||||
/*
|
||||
* Bind a float value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const float p_value);
|
||||
|
||||
/*
|
||||
* Bind a double value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index, const double p_value);
|
||||
|
||||
/*
|
||||
* Bind a string value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
* \param p_copy Should the memory be copied so that caller does not have to retain it
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index,
|
||||
const eastl::string_view p_value,
|
||||
const CopyValue p_copy = CopyValue {false});
|
||||
|
||||
/*
|
||||
* Bind a blob value to the Request at index
|
||||
*
|
||||
* \param p_index The index to bind to
|
||||
* \param p_value The value
|
||||
* \param p_copy Should the memory be copied so that caller does not have to retain it
|
||||
*/
|
||||
void Bind(const std::uint32_t p_index,
|
||||
const eastl::span<const std::byte> p_value,
|
||||
const CopyValue p_copy = CopyValue {false});
|
||||
|
||||
/*
|
||||
* Step through the request
|
||||
*
|
||||
* \return True if a row is available to read via Get(), false once the statement is done
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool Step();
|
||||
|
||||
/*
|
||||
* Execute the request once
|
||||
*
|
||||
* \return The number of rows modified by the statement
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
std::uint32_t Execute();
|
||||
|
||||
class Column
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Constructor
|
||||
*
|
||||
* \param p_request The request to get the column of
|
||||
* \param p_index Column index
|
||||
*/
|
||||
Column(const Request& p_request, const std::uint32_t p_index);
|
||||
|
||||
/*
|
||||
* Get value as a int32
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator std::int32_t() const;
|
||||
|
||||
/*
|
||||
* Get value as a uint32
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator std::uint32_t() const;
|
||||
|
||||
/*
|
||||
* Get value as a int64
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator std::int64_t() const;
|
||||
|
||||
/*
|
||||
* Get value as a uint64
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator std::uint64_t() const;
|
||||
|
||||
/*
|
||||
* Get value as a float
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator float() const;
|
||||
|
||||
/*
|
||||
* Get value as a double
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator double() const;
|
||||
|
||||
/*
|
||||
* Get value as a string
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator eastl::string_view() const;
|
||||
|
||||
/*
|
||||
* Get value as a blob
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator eastl::span<const std::byte>() const;
|
||||
|
||||
/*
|
||||
* Get value as a blob
|
||||
*
|
||||
* \return The value
|
||||
*/
|
||||
operator std::span<const std::byte>() const;
|
||||
|
||||
private:
|
||||
/*
|
||||
* The statement
|
||||
*/
|
||||
sqlite3_stmt* m_statement;
|
||||
|
||||
/*
|
||||
* The column index
|
||||
*/
|
||||
std::uint32_t m_index;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get a column
|
||||
*
|
||||
* \param p_index Column index
|
||||
*/
|
||||
[[nodiscard]]
|
||||
Column Get(const std::uint32_t p_index) const;
|
||||
|
||||
~Request();
|
||||
|
||||
Request& operator=(const Request& p_request) = delete;
|
||||
Request& operator=(Request&& p_request) = delete;
|
||||
|
||||
private:
|
||||
/*
|
||||
* The database
|
||||
*/
|
||||
sqlite3* m_db;
|
||||
|
||||
/*
|
||||
* The statement
|
||||
*/
|
||||
sqlite3_stmt* m_statement = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* The BigFile DB
|
||||
*/
|
||||
sqlite3* m_db = nullptr;
|
||||
};
|
||||
#endif
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
#if defined BIGFOOT_TOOLMODE
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
@@ -16,4 +18,5 @@ namespace Bigfoot
|
||||
*/
|
||||
constexpr eastl::string_view BIGFILE_@BIGFILE_NAME@_LOCATION {"@BIGFILE_LOCATION@"};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*********************************************************************
|
||||
* \file DataAccessor.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_DATAACCESSOR_DATAACCESSOR_HPP
|
||||
#define BIGFOOT_ENGINE_DATAACCESSOR_DATAACCESSOR_HPP
|
||||
#include <Engine/Asset/AssetTypeID.hpp>
|
||||
|
||||
#include <System/Time/Time.hpp>
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/string.h>
|
||||
#include <EASTL/vector.h>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class DataAccessor
|
||||
{
|
||||
public:
|
||||
DataAccessor() = default;
|
||||
|
||||
DataAccessor(const DataAccessor& p_accessor) = default;
|
||||
DataAccessor(DataAccessor&& p_accessor) = default;
|
||||
|
||||
virtual ~DataAccessor() = default;
|
||||
|
||||
/**
|
||||
* Get an Asset flatbuffer
|
||||
*
|
||||
* \param p_uuid The asset to get the flatbuffer of
|
||||
* \return The flatbuffer, or an empty vector if the asset was not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<std::byte> GetAssetFlatbuffer(const UUID& p_uuid) const = 0;
|
||||
|
||||
/**
|
||||
* Get several Asset flatbuffers
|
||||
*
|
||||
* \param p_uuids The assets to get the flatbuffers of
|
||||
* \return The flatbuffers that were found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<eastl::vector<std::byte>> GetAssetFlatbuffers(eastl::span<const UUID> p_uuids) const = 0;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of a given asset
|
||||
*
|
||||
* \param p_uuid The asset to get the HardReferences of
|
||||
* \return The HardReferences of the asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<UUID> GetAssetHardReferences(const UUID& p_uuid) const = 0;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of given assets
|
||||
*
|
||||
* \param p_uuids The assets to get the HardReferences of
|
||||
* \return The HardReferences of the assets
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual eastl::vector<eastl::vector<UUID>> GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const = 0;
|
||||
|
||||
/**
|
||||
* Save an asset
|
||||
*
|
||||
* \param p_uuid The UUID
|
||||
* \param p_flatbuffer The flatbuffer of the asset
|
||||
* \param p_hardReferences The HardReferences the asset points to
|
||||
*/
|
||||
virtual void SaveAssetFlatbuffer(const UUID& p_uuid,
|
||||
const eastl::span<const std::byte> p_flatbuffer,
|
||||
const eastl::span<const UUID> p_hardReferences) = 0;
|
||||
|
||||
/**
|
||||
* Save several assets
|
||||
*
|
||||
* \param p_uuids The UUIDs
|
||||
* \param p_flatbuffers The flatbuffers of the assets, matching p_metadatas by index
|
||||
* \param p_hardReferences The HardReferences the asset points to
|
||||
*/
|
||||
virtual void SaveAssetFlatbuffers(const eastl::span<const UUID> p_uuids,
|
||||
const eastl::span<const eastl::vector<std::byte>> p_flatbuffers,
|
||||
const eastl::span<const eastl::vector<UUID>> p_hardReferences) = 0;
|
||||
|
||||
/**
|
||||
* Check if the Asset exists
|
||||
*
|
||||
* \param p_uuid The asset to check
|
||||
* \param True if it exists, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual bool Has(const UUID& p_uuid) const = 0;
|
||||
|
||||
DataAccessor& operator=(const DataAccessor& p_accessor) = default;
|
||||
DataAccessor& operator=(DataAccessor&& p_accessor) = default;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
// The global data accessor for Bigfoot
|
||||
inline DataAccessor* g_dataAccessor = nullptr;
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
/*
|
||||
* Set the data accessor
|
||||
*
|
||||
* \param p_dataAccessor The data accessor to set g_dataAccessor to.
|
||||
*/
|
||||
void SetDataAccessor(DataAccessor* p_dataAccessor);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
class ScopedDataAccessor
|
||||
{
|
||||
public:
|
||||
ScopedDataAccessor(DataAccessor* p_dataAccessor):
|
||||
m_previous(g_dataAccessor)
|
||||
{
|
||||
SetDataAccessor(p_dataAccessor);
|
||||
}
|
||||
|
||||
ScopedDataAccessor(const ScopedDataAccessor& p_scopedDataAccessor) = delete;
|
||||
ScopedDataAccessor(ScopedDataAccessor&& p_scopedDataAccessor) = delete;
|
||||
|
||||
~ScopedDataAccessor()
|
||||
{
|
||||
SetDataAccessor(m_previous);
|
||||
}
|
||||
|
||||
ScopedDataAccessor& operator=(const ScopedDataAccessor& p_scopedDataAccessor) = delete;
|
||||
ScopedDataAccessor& operator=(ScopedDataAccessor&& p_scopedDataAccessor) = delete;
|
||||
|
||||
private:
|
||||
/*
|
||||
* The previous data accessor.
|
||||
*/
|
||||
DataAccessor* m_previous = nullptr;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -6,55 +6,15 @@
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_ENGINEASSERTHANDLER_HPP
|
||||
#define BIGFOOT_ENGINE_ENGINEASSERTHANDLER_HPP
|
||||
#include <Utils/Assert.hpp>
|
||||
|
||||
#include <Engine/EngineLogger_generated.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
#include <EASTL/utility.h>
|
||||
#include <Utils/Assert/AssertHandler.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <source_location>
|
||||
#include <string_view>
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class EngineAssertHandler
|
||||
{
|
||||
public:
|
||||
EngineAssertHandler() = delete;
|
||||
|
||||
EngineAssertHandler(const EngineAssertHandler& p_handler) = delete;
|
||||
EngineAssertHandler(EngineAssertHandler&& p_handler) = delete;
|
||||
|
||||
~EngineAssertHandler() = delete;
|
||||
|
||||
/**
|
||||
* Handle an assertion.
|
||||
*
|
||||
* \param p_location Location of the assertion.
|
||||
* \param p_stacktrace The stack trace
|
||||
* \param p_format Format string for the assertion message.
|
||||
* \param p_args Arguments for the format string.
|
||||
*/
|
||||
template<typename... ARGS>
|
||||
static void Handle(const std::source_location& p_location,
|
||||
const std::string_view p_stacktrace,
|
||||
std::format_string<ARGS...> p_format,
|
||||
ARGS&&... p_args)
|
||||
{
|
||||
BIGFOOT_LOG_FATAL(ENGINE_LOGGER,
|
||||
"Assert: {} (File:{}, Line:{}, Function:{}\n{}",
|
||||
std::format(p_format, eastl::forward<ARGS>(p_args)...),
|
||||
p_location.file_name(),
|
||||
p_location.line(),
|
||||
p_location.function_name(),
|
||||
p_stacktrace);
|
||||
}
|
||||
|
||||
EngineAssertHandler& operator=(const EngineAssertHandler& p_handler) = delete;
|
||||
EngineAssertHandler& operator=(EngineAssertHandler&& p_handler) = delete;
|
||||
};
|
||||
using EngineAssertHandler = AssertHandler<ENGINE_LOGGER>;
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINELOGGER_GENERATED_HPP
|
||||
#define BIGFOOT_ENGINELOGGER_GENERATED_HPP
|
||||
#include <System/Log/Log.hpp>
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/*********************************************************************
|
||||
* \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 <Utils/TargetMacros.h>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#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(std::is_trivially_copyable_v<SasquatchHeader>);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
struct SasquatchEntry
|
||||
{
|
||||
std::array<std::byte, UUID::UUID_BYTE_SIZE> m_uuid {};
|
||||
|
||||
std::size_t m_assetOffset = 0;
|
||||
std::size_t m_assetSize = 0;
|
||||
|
||||
std::size_t m_hardReferencesOffset = 0;
|
||||
std::size_t m_hardReferencesSize = 0;
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<SasquatchEntry>);
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#if defined(BIGFOOT_TOOLMODE) || defined(BIGFOOT_BUILD_TESTS) || defined(BIGFOOT_BUILD_BENCHMARKS)
|
||||
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
|
||||
* \param p_totalHardReferencesBytes The sum of the sizes of every asset hardreferences that will be written
|
||||
*/
|
||||
SasquatchWriter(const File& p_file,
|
||||
const std::uint64_t p_assetCount,
|
||||
const std::size_t p_totalFlatbufferBytes,
|
||||
const std::size_t p_totalHardReferencesBytes);
|
||||
|
||||
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,
|
||||
eastl::span<const UUID> p_hardReferences);
|
||||
|
||||
SasquatchWriter& operator=(const SasquatchWriter& p_writer) = delete;
|
||||
SasquatchWriter& operator=(SasquatchWriter&& p_writer) = delete;
|
||||
|
||||
private:
|
||||
static constexpr std::uint64_t PREFETCH_BYTES = 4ULL * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* The number of assets expected
|
||||
*/
|
||||
std::uint64_t m_assetCount;
|
||||
|
||||
/**
|
||||
* How far into m_file has already been prefetched
|
||||
*/
|
||||
std::size_t m_prefetchedUpTo = 0;
|
||||
|
||||
/**
|
||||
* Current write position in the data region
|
||||
*/
|
||||
std::size_t m_currentDataOffset;
|
||||
|
||||
/**
|
||||
* The Sasquatch file being written, mapped at its final size
|
||||
*/
|
||||
WritableMemoryMappedFile m_file;
|
||||
|
||||
/**
|
||||
* The entries
|
||||
*/
|
||||
eastl::vector<SasquatchEntry> m_entries;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
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 the header
|
||||
*
|
||||
* \return The header
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const SasquatchHeader& GetHeader() const;
|
||||
|
||||
/**
|
||||
* Get an Asset flatbuffer
|
||||
*
|
||||
* \param p_uuid The asset to get the flatbuffer of
|
||||
* \return The flatbuffer, or an empty vector if the asset was not found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
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
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::vector<std::byte>> GetAssetFlatbuffers(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of a given asset
|
||||
*
|
||||
* \param p_uuid The asset to get the HardReferences of
|
||||
* \return The HardReferences of the asset
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<UUID> GetAssetHardReferences(const UUID& p_uuid) const override;
|
||||
|
||||
/**
|
||||
* Get the HardReferences of given assets
|
||||
*
|
||||
* \param p_uuids The assets to get the HardReferences of
|
||||
* \return The HardReferences of the assets
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<eastl::vector<UUID>> GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const override;
|
||||
|
||||
/**
|
||||
* Sasquatch is read-only: this will always fail
|
||||
*
|
||||
*/
|
||||
void SaveAssetFlatbuffer(const UUID& p_uuid,
|
||||
eastl::span<const std::byte> p_flatbuffer,
|
||||
eastl::span<const UUID> p_hardReferences) override;
|
||||
|
||||
/**
|
||||
* Sasquatch is read-only: this will always fail
|
||||
*
|
||||
*/
|
||||
void SaveAssetFlatbuffers(const eastl::span<const UUID> p_uuids,
|
||||
eastl::span<const eastl::vector<std::byte>> p_flatbuffers,
|
||||
eastl::span<const eastl::vector<UUID>> p_hardReferences) override;
|
||||
|
||||
/**
|
||||
* Check if the Asset exists
|
||||
*
|
||||
* \param p_uuid The asset to check
|
||||
* \param True if it exists, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
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_assetOffset = 0;
|
||||
std::size_t m_assetSize = 0;
|
||||
|
||||
std::size_t m_hardReferencesOffset = 0;
|
||||
std::size_t m_hardReferencesSize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Sasquatch file
|
||||
*/
|
||||
MemoryMappedFile m_file;
|
||||
|
||||
/**
|
||||
* The sasquatch header
|
||||
*/
|
||||
SasquatchHeader m_header;
|
||||
|
||||
/**
|
||||
* 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,54 @@
|
||||
/*********************************************************************
|
||||
* \file SerializationManager.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_ENGINE_SERIALIZATIONMANAGER_SERIALIZATIONMANAGER_HPP
|
||||
#define BIGFOOT_ENGINE_SERIALIZATIONMANAGER_SERIALIZATIONMANAGER_HPP
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
|
||||
namespace Bigfoot::SerializationManager
|
||||
{
|
||||
/**
|
||||
* Load an Asset, along with every HardReference it transitively points to
|
||||
* Already loaded assets are skipped. Assets that are part of a cyclic dependency are left unloaded
|
||||
*
|
||||
* \param p_uuid The asset to load
|
||||
*/
|
||||
void LoadAsset(const UUID& p_uuid);
|
||||
|
||||
/**
|
||||
* Load several Assets, along with every HardReference they transitively point to
|
||||
* Already loaded assets are skipped. Assets that are part of a cyclic dependency are left unloaded
|
||||
*
|
||||
* \param p_uuids The assets to load
|
||||
*/
|
||||
void LoadAssets(const eastl::span<const UUID> p_uuids);
|
||||
|
||||
#ifdef BIGFOOT_TOOLMODE
|
||||
/**
|
||||
* Save an Asset
|
||||
* Fails without saving if a cyclic dependency is detected starting from this asset
|
||||
*
|
||||
* \param p_uuid The asset to save
|
||||
* \return True if the asset was saved, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool SaveAsset(const UUID& p_uuid);
|
||||
|
||||
/**
|
||||
* Save several Assets
|
||||
* Fails without saving any of them if a cyclic dependency is detected starting from any of them
|
||||
*
|
||||
* \param p_uuids The assets to save
|
||||
* \return True if the assets were saved, false otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool SaveAssets(const eastl::span<const UUID> p_uuids);
|
||||
#endif
|
||||
} // namespace Bigfoot::SerializationManager
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,266 @@
|
||||
/*********************************************************************
|
||||
* \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
|
||||
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
// to delete when an actual source is in Engine
|
||||
@@ -2,8 +2,6 @@ get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName})
|
||||
|
||||
set(PublicDependencies
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:quill::quill>
|
||||
mimalloc
|
||||
stduuid::stduuid)
|
||||
set(PrivateDependencies)
|
||||
set(BigfootPublicDependencies
|
||||
@@ -18,11 +16,3 @@ bigfoot_create_package_lib(
|
||||
"")
|
||||
|
||||
bigfoot_create_logger()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PUBLIC $<$<CONFIG:Debug,RelWithDebInfo>:QUILL_NO_EXCEPTIONS>
|
||||
PUBLIC $<$<CONFIG:Debug,RelWithDebInfo>:QUILL_DISABLE_NON_PREFIXED_MACROS>
|
||||
|
||||
PUBLIC MI_SHARED_LIB)
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/MimallocImpl.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*********************************************************************
|
||||
* \file File.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#include <System/File.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
File::File(const eastl::string_view p_path):
|
||||
m_path(p_path.data()),
|
||||
m_pathString(p_path)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::IsAbsolute() const
|
||||
{
|
||||
return m_path.is_absolute();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::IsRelative() const
|
||||
{
|
||||
return m_path.is_relative();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::Exists() const
|
||||
{
|
||||
return std::filesystem::exists(m_path);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::string_view File::Path() const
|
||||
{
|
||||
return m_pathString;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File File::Absolute() const
|
||||
{
|
||||
return File {std::filesystem::absolute(m_path)};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File File::Relative() const
|
||||
{
|
||||
return File {std::filesystem::relative(m_path)};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File::File(const std::filesystem::path& p_path):
|
||||
m_path(p_path),
|
||||
m_pathString(m_path.string().c_str())
|
||||
{
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,120 @@
|
||||
/*********************************************************************
|
||||
* \file File.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <System/SystemAssertHandler.hpp>
|
||||
|
||||
#include <bit>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
File::File(const eastl::string_view p_path):
|
||||
m_path(p_path.data()),
|
||||
m_pathString(p_path)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::IsAbsolute() const
|
||||
{
|
||||
return m_path.is_absolute();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::IsRelative() const
|
||||
{
|
||||
return m_path.is_relative();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool File::Exists() const
|
||||
{
|
||||
return std::filesystem::exists(m_path);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::string_view File::GetPath() const
|
||||
{
|
||||
return m_pathString;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File File::ToAbsolute() const
|
||||
{
|
||||
return File {std::filesystem::absolute(m_path)};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File File::ToRelative() const
|
||||
{
|
||||
return File {std::filesystem::relative(m_path)};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::size_t File::GetSize() const
|
||||
{
|
||||
return static_cast<std::size_t>(std::filesystem::file_size(m_path));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::vector<std::byte> File::Read(const std::size_t p_offset, const std::size_t p_size) const
|
||||
{
|
||||
const File absoluteFile = ToAbsolute();
|
||||
const eastl::string_view path = absoluteFile.GetPath();
|
||||
|
||||
std::ifstream file {path.data(), std::ios::binary};
|
||||
CRITICAL_ASSERT(SystemAssertHandler, file.is_open(), "Failed to open file {} for reading", path);
|
||||
|
||||
file.seekg(static_cast<std::streamoff>(p_offset), std::ios::beg);
|
||||
|
||||
eastl::vector<std::byte> data;
|
||||
data.resize(p_size);
|
||||
file.read(std::bit_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||
return data;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void File::Write(const eastl::span<const std::byte> p_data, const std::size_t p_offset) const
|
||||
{
|
||||
const File absoluteFile = ToAbsolute();
|
||||
const eastl::string_view path = absoluteFile.GetPath();
|
||||
|
||||
std::ofstream {path.data(), std::ios::binary | std::ios::app};
|
||||
|
||||
std::fstream file {path.data(), std::ios::binary | std::ios::in | std::ios::out};
|
||||
CRITICAL_ASSERT(SystemAssertHandler, file.is_open(), "Failed to open file {} for writing", path);
|
||||
|
||||
file.seekp(static_cast<std::streamoff>(p_offset), std::ios::beg);
|
||||
file.write(std::bit_cast<const char*>(p_data.data()), static_cast<std::streamsize>(p_data.size()));
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void File::Delete() const
|
||||
{
|
||||
std::filesystem::remove(m_path);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
File::File(std::filesystem::path p_path):
|
||||
m_path(std::move(p_path)),
|
||||
m_pathString(m_path.string().c_str())
|
||||
{
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -0,0 +1,234 @@
|
||||
/*********************************************************************
|
||||
* \file MemoryMappedFile.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#include <System/File/MemoryMappedFile.hpp>
|
||||
|
||||
#include <System/SystemAssertHandler.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
#include <Windows.h>
|
||||
#elif defined BIGFOOT_LINUX
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
MemoryMappedFile::MemoryMappedFile(const File& p_file)
|
||||
{
|
||||
const File absoluteFile = p_file.ToAbsolute();
|
||||
const eastl::string_view path = absoluteFile.GetPath();
|
||||
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
m_fileHandle = CreateFileA(path.data(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
||||
nullptr);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_fileHandle != INVALID_HANDLE_VALUE, "Failed to open file {}", path);
|
||||
|
||||
LARGE_INTEGER fileSize {};
|
||||
[[maybe_unused]]
|
||||
const BOOL gotFileSize = GetFileSizeEx(m_fileHandle, &fileSize);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, gotFileSize, "Failed to get the size of file {}", path);
|
||||
m_size = static_cast<std::size_t>(fileSize.QuadPart);
|
||||
|
||||
if (m_size != 0)
|
||||
{
|
||||
m_mappingHandle = CreateFileMappingA(m_fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_mappingHandle != nullptr, "Failed create the file mapping {}", path);
|
||||
|
||||
m_data = static_cast<std::byte*>(MapViewOfFile(m_mappingHandle, FILE_MAP_READ, 0, 0, 0));
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_data != nullptr, "Failed to map the file {}", path);
|
||||
}
|
||||
#elif defined BIGFOOT_LINUX
|
||||
m_fileDescriptor = open(path.data(), O_RDONLY);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_fileDescriptor != -1, "Failed to open file {}", path);
|
||||
|
||||
struct stat fileStat {};
|
||||
[[maybe_unused]]
|
||||
const int statResult = fstat(m_fileDescriptor, &fileStat);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, statResult == 0, "Failed to get the size of file {}", path);
|
||||
m_size = static_cast<std::size_t>(fileStat.st_size);
|
||||
|
||||
if (m_size != 0)
|
||||
{
|
||||
void* const mapped = mmap(nullptr, m_size, PROT_READ, MAP_PRIVATE, m_fileDescriptor, 0);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, mapped != MAP_FAILED, "Failed to map file {}", path);
|
||||
m_data = static_cast<std::byte*>(mapped);
|
||||
|
||||
[[maybe_unused]]
|
||||
const int adviseResult = madvise(mapped, m_size, MADV_RANDOM);
|
||||
}
|
||||
#else
|
||||
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
MemoryMappedFile::~MemoryMappedFile()
|
||||
{
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
if (m_data != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL unmapped = UnmapViewOfFile(m_data);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, unmapped, "Failed to unmap the file");
|
||||
}
|
||||
|
||||
if (m_mappingHandle != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL closedMapping = CloseHandle(m_mappingHandle);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedMapping, "Failed to close the file mapping");
|
||||
}
|
||||
|
||||
if (m_fileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL closedFile = CloseHandle(m_fileHandle);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedFile, "Failed to close the file");
|
||||
}
|
||||
#elif defined BIGFOOT_LINUX
|
||||
if (m_data != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int unmapped = munmap(m_data, m_size);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, unmapped == 0, "Failed to unmap the file");
|
||||
}
|
||||
|
||||
if (m_fileDescriptor != -1)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int closedFile = close(m_fileDescriptor);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedFile == 0, "Failed to close the file");
|
||||
}
|
||||
#else
|
||||
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::span<const std::byte> MemoryMappedFile::GetData() const
|
||||
{
|
||||
return eastl::span<const std::byte> {m_data, m_size};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
WritableMemoryMappedFile::WritableMemoryMappedFile(const File& p_file, const std::size_t p_size):
|
||||
m_size(p_size)
|
||||
{
|
||||
const File absoluteFile = p_file.ToAbsolute();
|
||||
const eastl::string_view path = absoluteFile.GetPath();
|
||||
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
m_fileHandle =
|
||||
CreateFileA(path.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_fileHandle != INVALID_HANDLE_VALUE, "Failed to create file {}", path);
|
||||
|
||||
const LARGE_INTEGER size {.QuadPart = static_cast<LONGLONG>(p_size)};
|
||||
m_mappingHandle = CreateFileMappingA(m_fileHandle, nullptr, PAGE_READWRITE, size.HighPart, size.LowPart, nullptr);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_mappingHandle != nullptr, "Failed create the file mapping {}", path);
|
||||
|
||||
m_data = static_cast<std::byte*>(MapViewOfFile(m_mappingHandle, FILE_MAP_WRITE, 0, 0, 0));
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_data != nullptr, "Failed to map the file {}", path);
|
||||
#elif defined BIGFOOT_LINUX
|
||||
m_fileDescriptor = open(path.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, m_fileDescriptor != -1, "Failed to create file {}", path);
|
||||
|
||||
struct stat fileStat {};
|
||||
fstat(m_fileDescriptor, &fileStat);
|
||||
if (static_cast<std::size_t>(fileStat.st_size) < p_size)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int truncateResult = ftruncate(m_fileDescriptor, static_cast<off_t>(p_size));
|
||||
CRITICAL_ASSERT(SystemAssertHandler, truncateResult == 0, "Failed to size file {}", path);
|
||||
}
|
||||
|
||||
void* const mapped = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileDescriptor, 0);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, mapped != MAP_FAILED, "Failed to map file {}", path);
|
||||
m_data = static_cast<std::byte*>(mapped);
|
||||
#else
|
||||
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
WritableMemoryMappedFile::~WritableMemoryMappedFile()
|
||||
{
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
if (m_data != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL unmapped = UnmapViewOfFile(m_data);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, unmapped, "Failed to unmap the file");
|
||||
}
|
||||
|
||||
if (m_mappingHandle != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL closedMapping = CloseHandle(m_mappingHandle);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedMapping, "Failed to close the file mapping");
|
||||
}
|
||||
|
||||
if (m_fileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const BOOL closedFile = CloseHandle(m_fileHandle);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedFile, "Failed to close the file");
|
||||
}
|
||||
#elif defined BIGFOOT_LINUX
|
||||
if (m_data != nullptr)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int unmapped = munmap(m_data, m_size);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, unmapped == 0, "Failed to unmap the file");
|
||||
}
|
||||
|
||||
if (m_fileDescriptor != -1)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
const int closedFile = close(m_fileDescriptor);
|
||||
CRITICAL_ASSERT(SystemAssertHandler, closedFile == 0, "Failed to close the file");
|
||||
}
|
||||
#else
|
||||
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
eastl::span<std::byte> WritableMemoryMappedFile::GetData() const
|
||||
{
|
||||
return eastl::span<std::byte> {m_data, m_size};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
void WritableMemoryMappedFile::Prefetch(const std::size_t p_offset, const std::size_t p_size) const
|
||||
{
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
WIN32_MEMORY_RANGE_ENTRY range {.VirtualAddress = m_data + p_offset, .NumberOfBytes = p_size};
|
||||
[[maybe_unused]]
|
||||
const BOOL prefetched = PrefetchVirtualMemory(GetCurrentProcess(), 1, &range, 0);
|
||||
#elif defined BIGFOOT_LINUX
|
||||
[[maybe_unused]]
|
||||
const int adviseResult = madvise(m_data + p_offset, p_size, MADV_WILLNEED);
|
||||
#else
|
||||
CRITICAL_ASSERT(SystemAssertHandler, false, "MemoryMappedFileBase is not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
+36
-10
@@ -7,8 +7,10 @@
|
||||
#ifndef BIGFOOT_SYSTEM_FILE_HPP
|
||||
#define BIGFOOT_SYSTEM_FILE_HPP
|
||||
|
||||
#include <EASTL/span.h>
|
||||
#include <EASTL/string.h>
|
||||
#include <EASTL/string_view.h>
|
||||
#include <EASTL/vector.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
@@ -24,9 +26,6 @@ class File
|
||||
*/
|
||||
File(const eastl::string_view p_path);
|
||||
|
||||
File(const File& p_path) = default;
|
||||
File(File&& p_path) = default;
|
||||
|
||||
/**
|
||||
* Check if the file is relative to the current executable
|
||||
*
|
||||
@@ -57,7 +56,7 @@ class File
|
||||
* \return The path
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::string_view Path() const;
|
||||
eastl::string_view GetPath() const;
|
||||
|
||||
/**
|
||||
* Get the same file, relative to the current executable
|
||||
@@ -65,7 +64,7 @@ class File
|
||||
* \return The relative file
|
||||
*/
|
||||
[[nodiscard]]
|
||||
File Relative() const;
|
||||
File ToRelative() const;
|
||||
|
||||
/**
|
||||
* Get the same file, with an absolute path
|
||||
@@ -73,12 +72,39 @@ class File
|
||||
* \return The absolute file
|
||||
*/
|
||||
[[nodiscard]]
|
||||
File Absolute() const;
|
||||
File ToAbsolute() const;
|
||||
|
||||
~File() = default;
|
||||
/**
|
||||
* Get the size of the file
|
||||
*
|
||||
* \return The size in bytes
|
||||
*/
|
||||
[[nodiscard]]
|
||||
std::size_t GetSize() const;
|
||||
|
||||
File& operator=(const File& p_path) = default;
|
||||
File& operator=(File&& p_path) = default;
|
||||
/**
|
||||
* Read bytes from the file
|
||||
*
|
||||
* \param p_offset The offset in bytes
|
||||
* \param p_size The number of bytes to read
|
||||
* \return The bytes read
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::vector<std::byte> Read(std::size_t p_offset, std::size_t p_size) const;
|
||||
|
||||
/**
|
||||
* Write bytes to the file at a given offset, creating the file if needed. Content before
|
||||
* p_offset, or from past p_offset + p_data.size(), is left untouched.
|
||||
*
|
||||
* \param p_data The bytes to write
|
||||
* \param p_offset The offset, in bytes, to start writing at
|
||||
*/
|
||||
void Write(eastl::span<const std::byte> p_data, std::size_t p_offset) const;
|
||||
|
||||
/**
|
||||
* Delete the file
|
||||
*/
|
||||
void Delete() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -86,7 +112,7 @@ class File
|
||||
*
|
||||
* \param p_path The path
|
||||
*/
|
||||
File(const std::filesystem::path& p_path);
|
||||
File(std::filesystem::path p_path);
|
||||
|
||||
/**
|
||||
* The path
|
||||
@@ -0,0 +1,150 @@
|
||||
/*********************************************************************
|
||||
* \file MemoryMappedFile.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_MEMORYMAPPEDFILE_HPP
|
||||
#define BIGFOOT_SYSTEM_MEMORYMAPPEDFILE_HPP
|
||||
#include <System/File/File.hpp>
|
||||
|
||||
#include <EASTL/span.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class MemoryMappedFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* Opens and maps the whole file
|
||||
*
|
||||
* \param p_file The file to map
|
||||
*/
|
||||
explicit MemoryMappedFile(const File& p_file);
|
||||
|
||||
MemoryMappedFile(const MemoryMappedFile& p_file) = delete;
|
||||
MemoryMappedFile(MemoryMappedFile&& p_file) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
*/
|
||||
~MemoryMappedFile();
|
||||
|
||||
/**
|
||||
* Get the mapped content of the file
|
||||
*
|
||||
* \return The mapped bytes
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::span<const std::byte> GetData() const;
|
||||
|
||||
MemoryMappedFile& operator=(const MemoryMappedFile& p_file) = delete;
|
||||
MemoryMappedFile& operator=(MemoryMappedFile&& p_file) = delete;
|
||||
|
||||
private:
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
/**
|
||||
* Handle to the file
|
||||
*/
|
||||
void* m_fileHandle = nullptr;
|
||||
|
||||
/**
|
||||
* Handle to the file mapping
|
||||
*/
|
||||
void* m_mappingHandle = nullptr;
|
||||
#elif defined BIGFOOT_LINUX
|
||||
/**
|
||||
* Handle to the file
|
||||
*/
|
||||
int m_fileDescriptor = -1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The mapped size
|
||||
*/
|
||||
std::size_t m_size = 0;
|
||||
|
||||
/**
|
||||
* The mapped data
|
||||
*/
|
||||
std::byte* m_data = nullptr;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
class WritableMemoryMappedFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* Creates and maps the file
|
||||
*
|
||||
* \param p_file The file to create and map
|
||||
* \param p_size The size to map
|
||||
*/
|
||||
WritableMemoryMappedFile(const File& p_file, std::size_t p_size);
|
||||
|
||||
WritableMemoryMappedFile(const WritableMemoryMappedFile& p_file) = delete;
|
||||
WritableMemoryMappedFile(WritableMemoryMappedFile&& p_file) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
*/
|
||||
~WritableMemoryMappedFile();
|
||||
|
||||
/**
|
||||
* Get the mapped content of the file
|
||||
*
|
||||
* \return The mapped bytes
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::span<std::byte> GetData() const;
|
||||
|
||||
/**
|
||||
* Hint the OS to prefetch a range of data, so writes into that range do not each pay a page fault
|
||||
*
|
||||
* \param p_offset The offset, in bytes, to start prefetching from
|
||||
* \param p_size The number of bytes to prefetch
|
||||
*/
|
||||
void Prefetch(std::size_t p_offset, std::size_t p_size) const;
|
||||
|
||||
WritableMemoryMappedFile& operator=(const WritableMemoryMappedFile& p_file) = delete;
|
||||
WritableMemoryMappedFile& operator=(WritableMemoryMappedFile&& p_file) = delete;
|
||||
|
||||
private:
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
/**
|
||||
* Handle to the file
|
||||
*/
|
||||
void* m_fileHandle = nullptr;
|
||||
|
||||
/**
|
||||
* Handle to the file mapping
|
||||
*/
|
||||
void* m_mappingHandle = nullptr;
|
||||
#elif defined BIGFOOT_LINUX
|
||||
/**
|
||||
* Handle to the file
|
||||
*/
|
||||
int m_fileDescriptor = -1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The mapped size
|
||||
*/
|
||||
std::size_t m_size = 0;
|
||||
|
||||
/**
|
||||
* The mapped data
|
||||
*/
|
||||
std::byte* m_data = nullptr;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -1,95 +0,0 @@
|
||||
/*********************************************************************
|
||||
* \file EASTLFormatters.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_EASTLFORMATTERS_HPP
|
||||
#define BIGFOOT_SYSTEM_EASTLFORMATTERS_HPP
|
||||
#include <Utils/TargetMacros.h>
|
||||
|
||||
#if defined(BIGFOOT_NOT_OPTIMIZED)
|
||||
#include <quill/DeferredFormatCodec.h>
|
||||
#endif
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <EASTL/string.h>
|
||||
#include <EASTL/string_view.h>
|
||||
|
||||
// STRING
|
||||
|
||||
template<>
|
||||
struct std::formatter<eastl::string>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const eastl::string& p_string, FormatContext& ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "{}", p_string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
template<>
|
||||
struct fmtquill::formatter<eastl::string>
|
||||
{
|
||||
constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
auto format(const eastl::string& p_string, format_context& ctx) const
|
||||
{
|
||||
return fmtquill::format_to(ctx.out(), "{}", p_string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct quill::Codec<eastl::string>: quill::DeferredFormatCodec<eastl::string>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
// STRING_VIEW
|
||||
|
||||
template<>
|
||||
struct std::formatter<eastl::string_view>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const eastl::string_view& p_stringView, FormatContext& ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "{}", p_stringView.data());
|
||||
}
|
||||
};
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
template<>
|
||||
struct fmtquill::formatter<eastl::string_view>
|
||||
{
|
||||
constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
auto format(const eastl::string_view& p_stringView, format_context& ctx) const
|
||||
{
|
||||
return fmtquill::format_to(ctx.out(), "{}", p_stringView.data());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct quill::Codec<eastl::string_view>: quill::DeferredFormatCodec<eastl::string_view>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,182 +0,0 @@
|
||||
/*********************************************************************
|
||||
* \file Log.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_LOG_HPP
|
||||
#define BIGFOOT_SYSTEM_LOG_HPP
|
||||
#include <System/Log/EASTLFormatters.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
#include <System/Log/Log_generated.hpp>
|
||||
|
||||
#include <Utils/Singleton.hpp>
|
||||
|
||||
#include <EASTL/array.h>
|
||||
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
#pragma warning(disable: 4702)
|
||||
#endif
|
||||
#include <quill/Backend.h>
|
||||
#include <quill/Frontend.h>
|
||||
#include <quill/LogMacros.h>
|
||||
#include <quill/Logger.h>
|
||||
#include <quill/sinks/ConsoleSink.h>
|
||||
#if defined BIGFOOT_WINDOWS
|
||||
#pragma warning(default: 4702)
|
||||
#endif
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class Log
|
||||
{
|
||||
public:
|
||||
struct LoggerInfo
|
||||
{
|
||||
std::string m_name;
|
||||
Flat::LogLevel m_level;
|
||||
};
|
||||
|
||||
Log();
|
||||
|
||||
Log(const Log& p_logger) = delete;
|
||||
Log(Log&& p_logger) = delete;
|
||||
|
||||
/**
|
||||
* Register a logger.
|
||||
*
|
||||
* \param p_loggerInfo The logger to register
|
||||
*/
|
||||
[[nodiscard]]
|
||||
quill::Logger* RegisterLogger(const LoggerInfo& p_loggerInfo);
|
||||
|
||||
/**
|
||||
* Register a logger.
|
||||
*
|
||||
* \param p_loggerInfo The logger to get
|
||||
* \return The logger, nullptr if it does not exist
|
||||
*/
|
||||
[[nodiscard]]
|
||||
quill::Logger* GetLogger(const LoggerInfo& p_loggerInfo);
|
||||
|
||||
/**
|
||||
* Changes the loglevel of a Logger.
|
||||
*
|
||||
* \param p_loggerInfo The logger to change
|
||||
* \param p_level The new level
|
||||
*/
|
||||
void ChangeLoggerLogLevel(LoggerInfo& p_loggerInfo, const Flat::LogLevel p_level);
|
||||
|
||||
~Log();
|
||||
|
||||
Log& operator=(const Log& p_logger) = delete;
|
||||
Log& operator=(Log&& p_logger) = delete;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Set the LogLevel of a logger
|
||||
*
|
||||
* \param p_loggerInfo The logger to set
|
||||
*/
|
||||
void SetLoggerLevel(const LoggerInfo& p_loggerInfo);
|
||||
|
||||
/*
|
||||
* The sinks
|
||||
*/
|
||||
eastl::array<std::shared_ptr<quill::Sink>, 1> m_sinks;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
#define BIGFOOT_LOG_DEBUG(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_DEBUG(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_DEBUG(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BIGFOOT_LOG_TRACE(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_TRACE_L3(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_TRACE_L3(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BIGFOOT_LOG_INFO(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_INFO(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_INFO(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BIGFOOT_LOG_WARN(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_WARNING(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_WARNING(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BIGFOOT_LOG_ERROR(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_ERROR(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_ERROR(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define BIGFOOT_LOG_FATAL(loggerName, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (quill::Logger* logger = Bigfoot::Singleton<Bigfoot::Log>::Instance().GetLogger(loggerName)) \
|
||||
{ \
|
||||
QUILL_LOG_CRITICAL(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
QUILL_LOG_CRITICAL(Bigfoot::Singleton<Bigfoot::Log>::Instance().RegisterLogger(loggerName), \
|
||||
fmt __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define BIGFOOT_LOG_DEBUG(loggerName, fmt, ...)
|
||||
#define BIGFOOT_LOG_TRACE(loggerName, fmt, ...)
|
||||
#define BIGFOOT_LOG_INFO(loggerName, fmt, ...)
|
||||
#define BIGFOOT_LOG_WARN(loggerName, fmt, ...)
|
||||
#define BIGFOOT_LOG_ERROR(loggerName, fmt, ...)
|
||||
#define BIGFOOT_LOG_FATAL(loggerName, fmt, ...)
|
||||
#endif
|
||||
#endif
|
||||
@@ -6,55 +6,15 @@
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_SYSTEMASSERTHANDLER_HPP
|
||||
#define BIGFOOT_SYSTEM_SYSTEMASSERTHANDLER_HPP
|
||||
#include <Utils/Assert.hpp>
|
||||
|
||||
#include <System/SystemLogger_generated.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
#include <EASTL/utility.h>
|
||||
#include <Utils/Assert/AssertHandler.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <source_location>
|
||||
#include <string_view>
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class SystemAssertHandler
|
||||
{
|
||||
public:
|
||||
SystemAssertHandler() = delete;
|
||||
|
||||
SystemAssertHandler(const SystemAssertHandler& p_handler) = delete;
|
||||
SystemAssertHandler(SystemAssertHandler&& p_handler) = delete;
|
||||
|
||||
~SystemAssertHandler() = delete;
|
||||
|
||||
/**
|
||||
* Handle an assertion.
|
||||
*
|
||||
* \param p_location Location of the assertion.
|
||||
* \param p_stacktrace The stack trace
|
||||
* \param p_format Format string for the assertion message.
|
||||
* \param p_args Arguments for the format string.
|
||||
*/
|
||||
template<typename... ARGS>
|
||||
static void Handle(const std::source_location& p_location,
|
||||
const std::string_view p_stacktrace,
|
||||
std::format_string<ARGS...> p_format,
|
||||
ARGS&&... p_args)
|
||||
{
|
||||
BIGFOOT_LOG_FATAL(SYSTEM_LOGGER,
|
||||
"Assert: {} (File:{}, Line:{}, Function:{}\n{}",
|
||||
std::format(p_format, eastl::forward<ARGS>(p_args)...),
|
||||
p_location.file_name(),
|
||||
p_location.line(),
|
||||
p_location.function_name(),
|
||||
p_stacktrace);
|
||||
}
|
||||
|
||||
SystemAssertHandler& operator=(const SystemAssertHandler& p_handler) = delete;
|
||||
SystemAssertHandler& operator=(SystemAssertHandler&& p_handler) = delete;
|
||||
};
|
||||
using SystemAssertHandler = AssertHandler<SYSTEM_LOGGER>;
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEMLOGGER_GENERATED_HPP
|
||||
#define BIGFOOT_SYSTEMLOGGER_GENERATED_HPP
|
||||
#include <System/Log/Log.hpp>
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
native_include "System/Time/Time.hpp";
|
||||
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
struct Time (native_type: "::Bigfoot::Time")
|
||||
{
|
||||
epoch:ulong;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*********************************************************************
|
||||
* \file Time.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_TIME_TIME_HPP
|
||||
#define BIGFOOT_SYSTEM_TIME_TIME_HPP
|
||||
#include <chrono>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
class Time
|
||||
{
|
||||
public:
|
||||
Time(const std::uint64_t p_epoch);
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetYear() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetMonth() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetDay() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetHour() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetMinute() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetSecond() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint32_t GetMicrosecond() const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint64_t GetEpoch() const;
|
||||
|
||||
[[nodiscard]]
|
||||
bool operator==(const Time& p_other) const
|
||||
{
|
||||
return m_epoch == p_other.m_epoch;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto operator<=>(const Time& p_other) const
|
||||
{
|
||||
return m_epoch <=> p_other.m_epoch;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static Time Now();
|
||||
|
||||
private:
|
||||
std::uint64_t m_epoch;
|
||||
std::tm m_timeInfo;
|
||||
std::chrono::microseconds m_microseconds;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace Flat::Bigfoot
|
||||
{
|
||||
struct Time;
|
||||
} // namespace Flat::Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::Time Pack(const Bigfoot::Time& p_time);
|
||||
Bigfoot::Time UnPack(const Flat::Bigfoot::Time& p_flatTime);
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_TIME_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_TIME_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "System/Time/Time.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct Time;
|
||||
|
||||
inline const ::flatbuffers::TypeTable *TimeTypeTable();
|
||||
|
||||
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Time FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
uint64_t epoch_;
|
||||
|
||||
public:
|
||||
struct Traits;
|
||||
static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {
|
||||
return TimeTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Flat.Bigfoot.Time";
|
||||
}
|
||||
Time()
|
||||
: epoch_(0) {
|
||||
}
|
||||
Time(uint64_t _epoch)
|
||||
: epoch_(::flatbuffers::EndianScalar(_epoch)) {
|
||||
}
|
||||
uint64_t epoch() const {
|
||||
return ::flatbuffers::EndianScalar(epoch_);
|
||||
}
|
||||
};
|
||||
FLATBUFFERS_STRUCT_END(Time, 8);
|
||||
|
||||
struct Time::Traits {
|
||||
using type = Time;
|
||||
};
|
||||
|
||||
inline const ::flatbuffers::TypeTable *TimeTypeTable() {
|
||||
static const ::flatbuffers::TypeCode type_codes[] = {
|
||||
{ ::flatbuffers::ET_ULONG, 0, -1 }
|
||||
};
|
||||
static const int64_t values[] = { 0, 8 };
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_TIME_FLAT_BIGFOOT_H_
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Bigfoot::Flat::UUID Pack(const Bigfoot::UUID& p_uuid);
|
||||
Bigfoot::UUID UnPack(const Bigfoot::Flat::UUID& p_flatUUID);
|
||||
Flat::Bigfoot::UUID Pack(const Bigfoot::UUID& p_uuid);
|
||||
Bigfoot::UUID UnPack(const Flat::Bigfoot::UUID& p_flatUUID);
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
native_include "System/UUID/FlatUUID.hpp";
|
||||
native_include "System/UUID/UUID.hpp";
|
||||
|
||||
namespace Bigfoot.Flat;
|
||||
namespace Flat.Bigfoot;
|
||||
|
||||
struct UUID (native_type: "::Bigfoot::UUID")
|
||||
{
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_SYSTEM_UUID_HPP
|
||||
#define BIGFOOT_SYSTEM_UUID_HPP
|
||||
|
||||
#ifndef BIGFOOT_SYSTEM_UUID_UUID_HPP
|
||||
#define BIGFOOT_SYSTEM_UUID_UUID_HPP
|
||||
#include <EASTL/string.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <fmt/format.h>
|
||||
#include <uuid.h>
|
||||
|
||||
namespace Bigfoot
|
||||
@@ -16,6 +18,11 @@ class UUID
|
||||
public:
|
||||
static constexpr std::uint32_t UUID_BYTE_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Will default initialize an UUID to a valid value
|
||||
*
|
||||
*/
|
||||
UUID();
|
||||
|
||||
/**
|
||||
@@ -32,22 +39,26 @@ class UUID
|
||||
*/
|
||||
explicit UUID(const std::span<const std::byte, UUID_BYTE_SIZE> p_raw);
|
||||
|
||||
UUID(const UUID& p_uuid) = default;
|
||||
UUID(UUID&& p_uuid) noexcept = default;
|
||||
/**
|
||||
* Gets the raw bytes of a UUID
|
||||
*
|
||||
* \return A view to the raw bytes
|
||||
*/
|
||||
[[nodiscard]]
|
||||
std::span<const std::byte, UUID_BYTE_SIZE> ToBytes() const;
|
||||
|
||||
~UUID() = default;
|
||||
/**
|
||||
* Gets the string representation of a UUID
|
||||
*
|
||||
* \return A string representing the UUID
|
||||
*/
|
||||
[[nodiscard]]
|
||||
eastl::string ToString() const;
|
||||
|
||||
operator std::span<const std::byte, UUID_BYTE_SIZE>() const;
|
||||
operator std::string() const;
|
||||
operator bool() const;
|
||||
|
||||
UUID& operator=(const UUID& p_uuid) = default;
|
||||
UUID& operator=(UUID&& p_uuid) noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
bool operator==(const UUID& p_uuid) const = default;
|
||||
[[nodiscard]]
|
||||
bool operator!=(const UUID& p_uuid) const = default;
|
||||
bool operator==(const UUID& p_uuid) const;
|
||||
[[nodiscard]]
|
||||
bool operator<(const UUID& p_uuid) const;
|
||||
|
||||
@@ -73,8 +84,6 @@ class UUID
|
||||
static UUID NULL_UUID;
|
||||
|
||||
private:
|
||||
friend struct std::hash<UUID>;
|
||||
|
||||
/**
|
||||
* The uuid.
|
||||
*/
|
||||
@@ -97,27 +106,34 @@ class UUID
|
||||
template<>
|
||||
struct std::hash<Bigfoot::UUID>
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t operator()(const Bigfoot::UUID& p_uuid) const noexcept
|
||||
{
|
||||
return std::hash<uuids::uuid>()(p_uuid.m_uuid);
|
||||
const auto bytes = p_uuid.ToBytes();
|
||||
return ankerl::unordered_dense::detail::wyhash::hash(bytes.data(), bytes.size());
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
template<>
|
||||
struct std::formatter<Bigfoot::UUID, char>
|
||||
struct fmt::formatter<Bigfoot::UUID>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context& ctx)
|
||||
static constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
auto format(const Bigfoot::UUID& p_uuid, FormatContext& ctx) const
|
||||
static auto format(const Bigfoot::UUID& p_uuid, format_context& ctx)
|
||||
{
|
||||
return std::format_to(ctx.out(), "{}", static_cast<std::string>(p_uuid));
|
||||
return fmt::format_to(ctx.out(), "{}", p_uuid.ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
#include <quill/DeferredFormatCodec.h>
|
||||
|
||||
@@ -131,7 +147,7 @@ struct fmtquill::formatter<Bigfoot::UUID>
|
||||
|
||||
auto format(const Bigfoot::UUID& p_uuid, format_context& ctx) const
|
||||
{
|
||||
return fmtquill::format_to(ctx.out(), "{}", static_cast<std::string>(p_uuid));
|
||||
return fmtquill::format_to(ctx.out(), "{}", p_uuid.ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,4 +157,19 @@ struct quill::Codec<Bigfoot::UUID>: quill::DeferredFormatCodec<Bigfoot::UUID>
|
||||
};
|
||||
#endif
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace Flat::Bigfoot
|
||||
{
|
||||
struct UUID;
|
||||
} // namespace Flat::Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::UUID Pack(const Bigfoot::UUID& p_uuid);
|
||||
Bigfoot::UUID UnPack(const Flat::Bigfoot::UUID& p_flatUUID);
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_UUID_BIGFOOT_FLAT_H_
|
||||
#define FLATBUFFERS_GENERATED_UUID_BIGFOOT_FLAT_H_
|
||||
#ifndef FLATBUFFERS_GENERATED_UUID_FLAT_BIGFOOT_H_
|
||||
#define FLATBUFFERS_GENERATED_UUID_FLAT_BIGFOOT_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
@@ -12,14 +12,22 @@ static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 12 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 19,
|
||||
"Non-compatible flatbuffers version included");
|
||||
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
|
||||
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
|
||||
#ifndef FLATBUFFERS_BIGFOOT_VERSION
|
||||
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
|
||||
#endif
|
||||
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
|
||||
"Non-compatible Bigfoot flatbuffers fork version included");
|
||||
|
||||
#include "System/UUID/FlatUUID.hpp"
|
||||
#include "System/UUID/UUID.hpp"
|
||||
|
||||
#include "EASTL/unique_ptr.h"
|
||||
#include "EASTL/string.h"
|
||||
#include "EASTL/vector.h"
|
||||
|
||||
namespace Bigfoot {
|
||||
namespace Flat {
|
||||
namespace Bigfoot {
|
||||
|
||||
struct UUID;
|
||||
|
||||
@@ -35,7 +43,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(1) UUID FLATBUFFERS_FINAL_CLASS {
|
||||
return UUIDTypeTable();
|
||||
}
|
||||
static FLATBUFFERS_CONSTEXPR_CPP11 const char *GetFullyQualifiedName() {
|
||||
return "Bigfoot.Flat.UUID";
|
||||
return "Flat.Bigfoot.UUID";
|
||||
}
|
||||
UUID()
|
||||
: bytes_() {
|
||||
@@ -59,16 +67,13 @@ inline const ::flatbuffers::TypeTable *UUIDTypeTable() {
|
||||
};
|
||||
static const int16_t array_sizes[] = { 16, };
|
||||
static const int64_t values[] = { 0, 16 };
|
||||
static const char * const names[] = {
|
||||
"bytes"
|
||||
};
|
||||
static const ::flatbuffers::TypeTable tt = {
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, array_sizes, values, names
|
||||
::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, array_sizes, values, nullptr
|
||||
};
|
||||
return &tt;
|
||||
}
|
||||
|
||||
} // namespace Flat
|
||||
} // namespace Bigfoot
|
||||
} // namespace Flat
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_UUID_BIGFOOT_FLAT_H_
|
||||
#endif // FLATBUFFERS_GENERATED_UUID_FLAT_BIGFOOT_H_
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#include <System/Time.hpp>
|
||||
#include <System/Time/Time.hpp>
|
||||
|
||||
#include <System/SystemAssertHandler.hpp>
|
||||
#include <System/Time/Time_generated.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -54,56 +55,56 @@ Time::Time(const std::uint64_t p_epoch):
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Year() const
|
||||
std::uint32_t Time::GetYear() const
|
||||
{
|
||||
return m_timeInfo.tm_year + 1900;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Month() const
|
||||
std::uint32_t Time::GetMonth() const
|
||||
{
|
||||
return m_timeInfo.tm_mon + 1;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Day() const
|
||||
std::uint32_t Time::GetDay() const
|
||||
{
|
||||
return m_timeInfo.tm_mday;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Hour() const
|
||||
std::uint32_t Time::GetHour() const
|
||||
{
|
||||
return m_timeInfo.tm_hour;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Minute() const
|
||||
std::uint32_t Time::GetMinute() const
|
||||
{
|
||||
return m_timeInfo.tm_min;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Second() const
|
||||
std::uint32_t Time::GetSecond() const
|
||||
{
|
||||
return m_timeInfo.tm_sec;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint32_t Time::Microsecond() const
|
||||
std::uint32_t Time::GetMicrosecond() const
|
||||
{
|
||||
return static_cast<std::uint32_t>(m_microseconds.count());
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
std::uint64_t Time::Epoch() const
|
||||
std::uint64_t Time::GetEpoch() const
|
||||
{
|
||||
return m_epoch;
|
||||
}
|
||||
@@ -119,3 +120,20 @@ Time Time::Now()
|
||||
return Time {epochInMicroSeconds};
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::Time Pack(const Bigfoot::Time& p_time)
|
||||
{
|
||||
return Flat::Bigfoot::Time {p_time.GetEpoch()};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
Bigfoot::Time UnPack(const Flat::Bigfoot::Time& p_flatTime)
|
||||
{
|
||||
return Bigfoot::Time {p_flatTime.epoch()};
|
||||
}
|
||||
} // namespace flatbuffers
|
||||
@@ -1,27 +0,0 @@
|
||||
/*********************************************************************
|
||||
* \file FlatUUID.cpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date December 2025
|
||||
*********************************************************************/
|
||||
#include <System/UUID/FlatUUID.hpp>
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Bigfoot::Flat::UUID Pack(const Bigfoot::UUID& p_uuid)
|
||||
{
|
||||
const std::span<const std::byte, Bigfoot::UUID::UUID_BYTE_SIZE> bytes = p_uuid;
|
||||
return Bigfoot::Flat::UUID {
|
||||
std::span<const std::uint8_t, Bigfoot::UUID::UUID_BYTE_SIZE> {reinterpret_cast<const std::uint8_t*>(bytes.data()),
|
||||
bytes.size()}};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
Bigfoot::UUID UnPack(const Bigfoot::Flat::UUID& p_flatUUID)
|
||||
{
|
||||
const std::span<const std::uint8_t, Bigfoot::UUID::UUID_BYTE_SIZE> bytesView {p_flatUUID.bytes()->data(),
|
||||
p_flatUUID.bytes()->size()};
|
||||
return Bigfoot::UUID {std::as_bytes(bytesView)};
|
||||
}
|
||||
} // namespace flatbuffers
|
||||
@@ -6,6 +6,8 @@
|
||||
*********************************************************************/
|
||||
#include <System/UUID/UUID.hpp>
|
||||
|
||||
#include <System/UUID/UUID_generated.hpp>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
UUID UUID::NULL_UUID {"00000000-0000-0000-0000-000000000000"};
|
||||
@@ -41,16 +43,17 @@ UUID::UUID(const std::span<const std::byte, UUID_BYTE_SIZE> p_raw)
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
UUID::operator std::span<const std::byte, UUID::UUID_BYTE_SIZE>() const
|
||||
std::span<const std::byte, UUID::UUID_BYTE_SIZE> UUID::ToBytes() const
|
||||
{
|
||||
return m_uuid.as_bytes();
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
UUID::operator std::string() const
|
||||
eastl::string UUID::ToString() const
|
||||
{
|
||||
return uuids::to_string(m_uuid);
|
||||
const std::string uuid = uuids::to_string(m_uuid);
|
||||
return eastl::string {uuid.data(), uuid.size()};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
@@ -60,6 +63,11 @@ UUID::operator bool() const
|
||||
return *this != NULL_UUID;
|
||||
}
|
||||
|
||||
bool UUID::operator==(const UUID& p_uuid) const
|
||||
{
|
||||
return m_uuid == p_uuid.m_uuid;
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
bool UUID::operator<(const UUID& p_uuid) const
|
||||
@@ -85,3 +93,25 @@ uuids::uuid_random_generator UUID::GetUUIDGenerator()
|
||||
return uuids::uuid_random_generator {ms_randomGenerator};
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
namespace flatbuffers
|
||||
{
|
||||
Flat::Bigfoot::UUID Pack(const Bigfoot::UUID& p_uuid)
|
||||
{
|
||||
const std::span<const std::byte, Bigfoot::UUID::UUID_BYTE_SIZE> bytes = p_uuid.ToBytes();
|
||||
return Flat::Bigfoot::UUID {
|
||||
std::span<const std::uint8_t, Bigfoot::UUID::UUID_BYTE_SIZE> {reinterpret_cast<const std::uint8_t*>(bytes.data()),
|
||||
bytes.size()}};
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
Bigfoot::UUID UnPack(const Flat::Bigfoot::UUID& p_flatUUID)
|
||||
{
|
||||
const std::span<const std::uint8_t, Bigfoot::UUID::UUID_BYTE_SIZE> bytesView {p_flatUUID.bytes()->data(),
|
||||
p_flatUUID.bytes()->size()};
|
||||
return Bigfoot::UUID {std::as_bytes(bytesView)};
|
||||
}
|
||||
} // namespace flatbuffers
|
||||
|
||||
@@ -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,50 @@
|
||||
/*********************************************************************
|
||||
* \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;
|
||||
|
||||
const 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 << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
std::cout << cli << '\n';
|
||||
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>
|
||||
|
||||
#ifdef 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,65 @@
|
||||
/*********************************************************************
|
||||
* \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);
|
||||
}
|
||||
|
||||
std::uint64_t totalHardReferencesBytes = 0;
|
||||
{
|
||||
BigFile::Request sizeRequest {p_bigFile,
|
||||
"SELECT COALESCE(SUM(LENGTH(HardReference)), 0) FROM AssetHardReference;"};
|
||||
[[maybe_unused]]
|
||||
const bool hasSize = sizeRequest.Step();
|
||||
ASSERT(SasquatchBakerAssertHandler, hasSize, "Failed to sum the hardreferences sizes of the BigFile");
|
||||
totalHardReferencesBytes = sizeRequest.Get(0);
|
||||
}
|
||||
|
||||
SasquatchWriter writer {p_outputFile, assetCount, totalFlatbufferBytes, totalHardReferencesBytes};
|
||||
|
||||
BigFile::Request request {p_bigFile, "SELECT UUID FROM Asset;"};
|
||||
while (request.Step())
|
||||
{
|
||||
const std::span<const std::byte> rawUUID = request.Get(0);
|
||||
|
||||
ASSERT(SasquatchBakerAssertHandler,
|
||||
rawUUID.size() == UUID::UUID_BYTE_SIZE,
|
||||
"Unexpected UUID blob size in BigFile (got {} / expected {})",
|
||||
rawUUID.size(),
|
||||
UUID::UUID_BYTE_SIZE);
|
||||
|
||||
const UUID uuid {std::span<const std::byte, UUID::UUID_BYTE_SIZE> {rawUUID.data(), UUID::UUID_BYTE_SIZE}};
|
||||
const eastl::vector<std::byte> flatbuffer = p_bigFile.GetAssetFlatbuffer(uuid);
|
||||
const eastl::vector<UUID> hardReferences = p_bigFile.GetAssetHardReferences(uuid);
|
||||
|
||||
writer.WriteAsset(uuid, flatbuffer, hardReferences);
|
||||
}
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
@@ -2,7 +2,15 @@ get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(${PackageName})
|
||||
|
||||
set(PublicDependencies
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:cpptrace::cpptrace>)
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:quill::quill>
|
||||
$<IF:$<BOOL:${ASAN}>,mimalloc-asan,mimalloc-static>
|
||||
unordered_dense::unordered_dense
|
||||
EASTL::EASTL
|
||||
EAStdC::EAStdC
|
||||
fmt::fmt
|
||||
flatbuffers::flatbuffers
|
||||
rapidhash::rapidhash
|
||||
effolkronium_random)
|
||||
set(PrivateDependencies)
|
||||
set(BigfootPublicDependencies)
|
||||
set(BigfootPrivateDependencies)
|
||||
@@ -12,4 +20,13 @@ bigfoot_create_package_lib(
|
||||
"${PrivateLibraries}"
|
||||
"${BigfootPublicDependencies}"
|
||||
"${BigfootPrivateDependencies}"
|
||||
"")
|
||||
"")
|
||||
set_source_files_properties(../Utils/MimallocImpl.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
|
||||
set_source_files_properties(../Utils/EASTLVsnprintfImpl.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
|
||||
|
||||
bigfoot_create_logger()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:QUILL_NO_EXCEPTIONS>
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:QUILL_DISABLE_NON_PREFIXED_MACROS>)
|
||||
@@ -0,0 +1,154 @@
|
||||
/*********************************************************************
|
||||
* \file Graph.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_UTILS_ALGORITHM_GRAPH_HPP
|
||||
#define BIGFOOT_UTILS_ALGORITHM_GRAPH_HPP
|
||||
#include <Utils/UtilsAssertHandler.hpp>
|
||||
|
||||
#include <EASTL/vector.h>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/*
|
||||
* Kahn's algorithm
|
||||
*
|
||||
* \param p_graph Every discovered node's outgoing edges (eg: its dependencies)
|
||||
* \param p_nodes The set of nodes to sort
|
||||
* \param p_waves The sorted nodes
|
||||
* \param p_cyclicNodes Nodes that could not be sorted (ie: part of a cycle)
|
||||
* \return True if a cycle was found
|
||||
*/
|
||||
template<class NODE_TYPE>
|
||||
[[nodiscard]]
|
||||
bool LayeredTopologicalSort(const ankerl::unordered_dense::map<NODE_TYPE, eastl::vector<NODE_TYPE>>& p_graph,
|
||||
const ankerl::unordered_dense::set<NODE_TYPE>& p_nodes,
|
||||
eastl::vector<eastl::vector<NODE_TYPE>>& p_waves,
|
||||
eastl::vector<NODE_TYPE>& p_cyclicNodes)
|
||||
{
|
||||
ASSERT(UtilsAssertHandler, p_waves.empty(), "p_waves must be empty");
|
||||
ASSERT(UtilsAssertHandler, p_cyclicNodes.empty(), "p_cyclicNodes must be empty");
|
||||
ASSERT(UtilsAssertHandler,
|
||||
p_nodes.size() <= std::numeric_limits<std::uint32_t>::max(),
|
||||
"Can't sort more than {} nodes",
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
const auto nodeCount = static_cast<std::uint32_t>(p_nodes.size());
|
||||
|
||||
ankerl::unordered_dense::map<NODE_TYPE, std::uint32_t> nodeToIndex;
|
||||
nodeToIndex.reserve(nodeCount);
|
||||
eastl::vector<NODE_TYPE> indexToNode;
|
||||
indexToNode.reserve(nodeCount);
|
||||
for (const NODE_TYPE& node: p_nodes)
|
||||
{
|
||||
nodeToIndex.emplace(node, static_cast<std::uint32_t>(indexToNode.size()));
|
||||
indexToNode.push_back(node);
|
||||
}
|
||||
|
||||
// Step 1: Compute in-degree for each node
|
||||
eastl::vector<std::uint32_t> inDegree(nodeCount, 0);
|
||||
|
||||
// Reverse adjacency (which nodes depend on each node) stored as CSR: adjacencyOffsets[i]..adjacencyOffsets[i + 1]
|
||||
// is node i's slice of adjacency. Built in two passes (count listeners per node, then fill) so the whole
|
||||
// structure is a couple of flat allocations instead of one per node.
|
||||
eastl::vector<std::uint32_t> adjacencyOffsets(nodeCount + 1, 0);
|
||||
|
||||
// Initialize queue with nodes having in-degree 0
|
||||
eastl::vector<std::uint32_t> queue;
|
||||
queue.reserve(nodeCount);
|
||||
for (std::uint32_t index = 0; index < nodeCount; ++index)
|
||||
{
|
||||
std::uint32_t degree = 0;
|
||||
for (const NODE_TYPE& edge: p_graph.at(indexToNode[index]))
|
||||
{
|
||||
const auto nodeIt = nodeToIndex.find(edge);
|
||||
if (nodeIt != nodeToIndex.end())
|
||||
{
|
||||
++degree;
|
||||
++adjacencyOffsets[nodeIt->second + 1];
|
||||
}
|
||||
}
|
||||
|
||||
inDegree[index] = degree;
|
||||
if (degree == 0)
|
||||
{
|
||||
queue.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::uint32_t index = 0; index < nodeCount; ++index)
|
||||
{
|
||||
adjacencyOffsets[index + 1] += adjacencyOffsets[index];
|
||||
}
|
||||
|
||||
eastl::vector<std::uint32_t> adjacency(adjacencyOffsets.back());
|
||||
eastl::vector<std::uint32_t> cursor(adjacencyOffsets.begin(), adjacencyOffsets.end() - 1);
|
||||
for (std::uint32_t index = 0; index < nodeCount; ++index)
|
||||
{
|
||||
for (const NODE_TYPE& edge: p_graph.at(indexToNode[index]))
|
||||
{
|
||||
const auto nodeIt = nodeToIndex.find(edge);
|
||||
if (nodeIt != nodeToIndex.end())
|
||||
{
|
||||
adjacency[cursor[nodeIt->second]++] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Process nodes
|
||||
std::size_t resolvedCount = 0;
|
||||
eastl::vector<std::uint32_t> nextQueue;
|
||||
nextQueue.reserve(nodeCount);
|
||||
while (!queue.empty())
|
||||
{
|
||||
resolvedCount += queue.size();
|
||||
|
||||
// Reduce in-degree for neighbors
|
||||
nextQueue.clear();
|
||||
for (const std::uint32_t index: queue)
|
||||
{
|
||||
for (std::uint32_t i = adjacencyOffsets[index]; i < adjacencyOffsets[index + 1]; ++i)
|
||||
{
|
||||
const std::uint32_t next = adjacency[i];
|
||||
if (--inDegree[next] == 0)
|
||||
{
|
||||
nextQueue.push_back(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eastl::vector<NODE_TYPE> waveNodes;
|
||||
waveNodes.reserve(queue.size());
|
||||
for (const std::uint32_t index: queue)
|
||||
{
|
||||
waveNodes.push_back(indexToNode[index]);
|
||||
}
|
||||
p_waves.push_back(std::move(waveNodes));
|
||||
|
||||
queue.swap(nextQueue);
|
||||
}
|
||||
|
||||
// Step 3 & 4: Check for valid topological order
|
||||
if (resolvedCount != nodeCount)
|
||||
{
|
||||
p_cyclicNodes.reserve(nodeCount - resolvedCount);
|
||||
for (std::uint32_t index = 0; index < nodeCount; ++index)
|
||||
{
|
||||
if (inDegree[index] != 0)
|
||||
{
|
||||
p_cyclicNodes.push_back(indexToNode[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedCount != nodeCount;
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
|
||||
#endif
|
||||
@@ -1,94 +0,0 @@
|
||||
/*********************************************************************
|
||||
* \file Assert.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_UTILS_ASSERT_HPP
|
||||
#define BIGFOOT_UTILS_ASSERT_HPP
|
||||
|
||||
#if defined BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
|
||||
#if defined BIGFOOT_LINUX
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
||||
#if defined BIGFOOT_WINDOWS
|
||||
#define BREAK \
|
||||
do \
|
||||
{ \
|
||||
__debugbreak(); \
|
||||
} while (false)
|
||||
#elif defined BIGFOOT_LINUX
|
||||
#define BREAK \
|
||||
do \
|
||||
{ \
|
||||
std::raise(SIGTRAP); \
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
#define ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
do \
|
||||
{ \
|
||||
constexpr std::source_location location = std::source_location::current(); \
|
||||
if (!(p_assert)) [[unlikely]] \
|
||||
{ \
|
||||
constexpr auto stacktrace = []() -> std::string \
|
||||
{ \
|
||||
const cpptrace::stacktrace stacktrace = cpptrace::generate_trace(); \
|
||||
return stacktrace.to_string(); \
|
||||
}; \
|
||||
\
|
||||
HANDLER::Handle(location, stacktrace(), p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
BREAK; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define SOFT_ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
do \
|
||||
{ \
|
||||
constexpr std::source_location location = std::source_location::current(); \
|
||||
if (!(p_assert)) [[unlikely]] \
|
||||
{ \
|
||||
constexpr auto stacktrace = []() -> std::string \
|
||||
{ \
|
||||
const cpptrace::stacktrace stacktrace = cpptrace::generate_trace(); \
|
||||
return stacktrace.to_string(); \
|
||||
}; \
|
||||
\
|
||||
HANDLER::Handle(location, stacktrace(), p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
BREAK; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define CRITICAL_ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
do \
|
||||
{ \
|
||||
constexpr std::source_location location = std::source_location::current(); \
|
||||
if (!(p_assert)) [[unlikely]] \
|
||||
{ \
|
||||
constexpr auto stacktrace = []() -> std::string \
|
||||
{ \
|
||||
const cpptrace::stacktrace stacktrace = cpptrace::generate_trace(); \
|
||||
return stacktrace.to_string(); \
|
||||
}; \
|
||||
\
|
||||
HANDLER::Handle(location, stacktrace(), p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
BREAK; \
|
||||
std::abort(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
#define ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
|
||||
#define SOFT_ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
|
||||
#define CRITICAL_ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/*********************************************************************
|
||||
* \file Assert.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date October 2025
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_UTILS_ASSERT_HPP
|
||||
#define BIGFOOT_UTILS_ASSERT_HPP
|
||||
#include <Utils/Log/LogMacros.hpp>
|
||||
#include <Utils/TargetMacros.h>
|
||||
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
#include <Utils/Format.hpp>
|
||||
|
||||
#include <EASTL/fixed_function.h>
|
||||
#include <EASTL/string.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <source_location>
|
||||
|
||||
#ifdef BIGFOOT_LINUX
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
||||
#ifdef BIGFOOT_WINDOWS
|
||||
#define BREAK \
|
||||
do \
|
||||
{ \
|
||||
BIGFOOT_FLUSH_LOG; \
|
||||
__debugbreak(); \
|
||||
} while (false)
|
||||
#elif defined BIGFOOT_LINUX
|
||||
#define BREAK \
|
||||
do \
|
||||
{ \
|
||||
BIGFOOT_FLUSH_LOG; \
|
||||
std::raise(SIGTRAP); \
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/**
|
||||
* Assert then break
|
||||
*
|
||||
* \param p_condition The condition to check
|
||||
* \param p_message A function that will return the formatting string describing the issue
|
||||
* \param p_location The location the assert originates from
|
||||
*/
|
||||
template<typename HANDLER>
|
||||
BIGFOOT_ALWAYS_INLINE void AssertCheck(const bool p_condition,
|
||||
const eastl::fixed_function<32, eastl::string()>& p_messageFn,
|
||||
const std::source_location p_location = std::source_location::current())
|
||||
{
|
||||
if (!p_condition) [[unlikely]]
|
||||
{
|
||||
HANDLER::Handle(p_location, p_messageFn());
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert then break then kill
|
||||
*
|
||||
* \param p_condition The condition to check
|
||||
* \param p_message A function that will return the formatting string describing the issue
|
||||
* \param p_location The location the assert originates from
|
||||
*/
|
||||
template<typename HANDLER>
|
||||
BIGFOOT_ALWAYS_INLINE void CriticalAssertCheck(const bool p_condition,
|
||||
const eastl::fixed_function<32, eastl::string()>& p_messageFn,
|
||||
const std::source_location p_location = std::source_location::current())
|
||||
{
|
||||
if (!p_condition) [[unlikely]]
|
||||
{
|
||||
HANDLER::Handle(p_location, p_messageFn());
|
||||
BREAK;
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
|
||||
#define ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
::Bigfoot::AssertCheck<HANDLER>((p_assert), \
|
||||
[&] \
|
||||
{ \
|
||||
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define SOFT_ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
::Bigfoot::AssertCheck<HANDLER>((p_assert), \
|
||||
[&] \
|
||||
{ \
|
||||
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define CRITICAL_ASSERT(HANDLER, p_assert, p_message, ...) \
|
||||
::Bigfoot::CriticalAssertCheck<HANDLER>((p_assert), \
|
||||
[&] \
|
||||
{ \
|
||||
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#else
|
||||
#define ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
|
||||
#define SOFT_ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
|
||||
#define CRITICAL_ASSERT(HANDLER, p_assert, p_message, ...)
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*********************************************************************
|
||||
* \file AssertHandler.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date August 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_UTILS_ASSERTHANDLER_HPP
|
||||
#define BIGFOOT_UTILS_ASSERTHANDLER_HPP
|
||||
#include <Utils/Assert/Assert.hpp>
|
||||
#include <Utils/Log/Log.hpp>
|
||||
|
||||
#ifdef BIGFOOT_NOT_OPTIMIZED
|
||||
|
||||
#include <EASTL/string.h>
|
||||
|
||||
#include <source_location>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
template<Log::LoggerInfo& LOGGER>
|
||||
class AssertHandler
|
||||
{
|
||||
public:
|
||||
AssertHandler() = delete;
|
||||
|
||||
AssertHandler(const AssertHandler& p_handler) = delete;
|
||||
AssertHandler(AssertHandler&& p_handler) = delete;
|
||||
|
||||
~AssertHandler() = delete;
|
||||
|
||||
/**
|
||||
* Handle an assertion.
|
||||
*
|
||||
* \param p_location Location of the assertion.
|
||||
* \param p_message Message describing the assertion failure.
|
||||
*/
|
||||
static void Handle(const std::source_location& p_location, const eastl::string& p_message)
|
||||
{
|
||||
BIGFOOT_LOG_FATAL(LOGGER,
|
||||
"Assert: {} (File:{}, Line:{}, Function:{}\n",
|
||||
p_message,
|
||||
p_location.file_name(),
|
||||
p_location.line(),
|
||||
p_location.function_name());
|
||||
}
|
||||
|
||||
AssertHandler& operator=(const AssertHandler& p_handler) = delete;
|
||||
AssertHandler& operator=(AssertHandler&& p_handler) = delete;
|
||||
};
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
/*********************************************************************
|
||||
* \file Format.hpp
|
||||
*
|
||||
* \author Romain BOULLARD
|
||||
* \date September 2026
|
||||
*********************************************************************/
|
||||
#ifndef BIGFOOT_UTILS_FORMAT_HPP
|
||||
#define BIGFOOT_UTILS_FORMAT_HPP
|
||||
#include <EASTL/string.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace Bigfoot
|
||||
{
|
||||
/**
|
||||
* Format a string, like fmt::format, but returning an eastl::string instead of a std::string.
|
||||
*
|
||||
* \param p_format The format string
|
||||
* \param p_args The arguments for the format string
|
||||
* \return The formatted eastl::string
|
||||
*/
|
||||
template<typename... ARGS>
|
||||
eastl::string Format(fmt::format_string<ARGS...> p_format, ARGS&&... p_args)
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::format_to(std::back_inserter(buffer), p_format, std::forward<ARGS>(p_args)...);
|
||||
return eastl::string {buffer.data(), buffer.size()};
|
||||
}
|
||||
} // namespace Bigfoot
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user