9 Commits
Author SHA1 Message Date
rboullard 9f034fed3c Merge branch 'main' of https://git.romainboullard.com/BigfootDev/Bigfoot into Development
Bigfoot / Sonarqube (game) (push) Successful in 4m54s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m22s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 47s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m7s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 50s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 53s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 37s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m23s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m30s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 53s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 28s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m13s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m4s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 51s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 58s
Bigfoot / Clang Format Checks (push) Successful in 11s
2026-09-02 23:12:39 +02:00
rboullard 33a79ed12f Game analysis for SonarQube (#5)
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 46s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m23s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m8s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 45s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m8s
Bigfoot / Clang Format Checks (push) Successful in 13s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 49s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 31s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m3s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m10s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 56s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 34s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 41s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m27s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m16s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 37s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 56s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m2s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 53s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m3s
Bigfoot / Sonarqube (game) (push) Successful in 5m17s
Bigfoot / Sonarqube (toolmode) (push) Successful in 5m39s
Reviewed-on: #5
Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
2026-09-02 13:55:27 +00:00
rboullard c4467ea2f2 More warnings remove from tests and benchmarks
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 46s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 47s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 48s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 53s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 38s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 28s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 33s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m26s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m9s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m31s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m16s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 59s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 30s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m6s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 10s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m24s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m7s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 41s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m2s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 53s
sonarqube-game OK
Bigfoot / Sonarqube (game) (pull_request) Successful in 5m24s
sonarqube-tool OK
Bigfoot / Sonarqube (toolmode) (pull_request) Successful in 6m2s
Bigfoot / Sonarqube (game) (push) Successful in 5m19s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m23s
2026-09-02 12:24:24 +02:00
rboullard 8c9f78dbc7 More exclusions from tests and benchmarks
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 47s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m26s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 45s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 46s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m10s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m23s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 39s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 43s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m3s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m3s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 52s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m16s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 57s
Bigfoot / Clang Format Checks (push) Successful in 10s
sonarqube-game ERROR
Bigfoot / Sonarqube (game) (pull_request) Failing after 5m9s
sonarqube-tool ERROR
Bigfoot / Sonarqube (toolmode) (pull_request) Failing after 5m47s
Bigfoot / Sonarqube (game) (push) Failing after 5m21s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m30s
2026-09-02 11:19:03 +02:00
rboullard 1558a19f19 Special config for Tests and Benchmarks
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 51s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 34s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m6s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 35s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m28s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 50s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 53s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 43s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m28s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m36s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m17s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 41s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 46s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 51s
sonarqube-game ERROR
Bigfoot / Sonarqube (toolmode) (pull_request) Failing after 5m44s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m54s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m6s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 55s
Bigfoot / Clang Format Checks (push) Successful in 10s
Bigfoot / Sonarqube (game) (pull_request) Failing after 5m8s
sonarqube-tool ERROR
Bigfoot / Sonarqube (game) (push) Successful in 5m37s
2026-09-02 10:19:11 +02:00
rboullard 3a5dd1affe Merge branch 'main' of https://git.romainboullard.com/BigfootDev/Bigfoot into Development
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m26s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m6s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 47s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 53s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 31s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 44s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 46s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 31s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m18s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m13s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 39s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 28s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 38s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m22s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m30s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m6s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m3s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 53s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 10s
Bigfoot / Sonarqube (game) (push) Successful in 4m56s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m18s
sonarqube-game ERROR
Bigfoot / Sonarqube (game) (pull_request) Failing after 4m56s
sonarqube-tool ERROR
Bigfoot / Sonarqube (toolmode) (pull_request) Failing after 5m50s
2026-09-02 02:07:19 +02:00
rboullard dc534acdd5 SonarQube Pass (#3)
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 48s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 27s
Bigfoot / Sonarqube (game) (push) Successful in 5m8s
Bigfoot / Sonarqube (toolmode) (push) Failing after 5m13s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 32s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m21s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m24s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m8s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 36s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m21s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 26s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 45s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 50s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 55s
Bigfoot / Clang Format Checks (push) Successful in 12s
sonarqube-game ERROR
sonarqube-tool ERROR
Bigfoot / Sonarqube (toolmode) (pull_request) Failing after 5m25s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m15s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 38s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 55s
Bigfoot / Sonarqube (game) (pull_request) Failing after 5m0s
Add Game project analysis

Reviewed-on: #3
Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
2026-09-02 00:01:33 +00:00
rboullard 9fd1cd0d68 Asset System (#2)
Bigfoot / Sonarqube (push) Successful in 5m42s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m27s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m11s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 55s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m0s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 12s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 43s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m19s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m1s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 48s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m20s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 42s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 50s
Only time I'm allowing a merge of single feature inside main (we prefer merging a version)
But I need a first Sonarqube run

Reviewed-on: #2
Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
2026-09-01 17:32:16 +00:00
rboullard b7f4677c68 BigFile/Sasquatch (#1)
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 44s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 29s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 46s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m21s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m3s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m25s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m8s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 1m9s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m34s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 1m13s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 37s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 28s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 57s
Bigfoot / Sonarqube (push) Successful in 6m0s
sonarqube ERROR
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 49s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 30s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 36s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 1m23s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 59s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 51s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 1m5s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 56s
Bigfoot / Clang Format Checks (push) Successful in 13s
Bigfoot / Sonarqube (pull_request) Failing after 5m14s
Implementing an asset management system

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

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

Reviewed-on: #1
Co-authored-by: Romain BOULLARD <romain.boullard@protonmail.com>
2026-09-01 16:32:01 +00:00
134 changed files with 7147 additions and 1671 deletions
+26 -11
View File
@@ -127,22 +127,37 @@ IncludeCategories:
Priority: 2
- Regex: '^<Utils/'
Priority: 3
- Regex: '^<EngineTests/'
- Regex: '^<SasquatchBaker/'
Priority: 4
- Regex: '^<SystemTests/'
- Regex: '^<EngineTests/'
Priority: 5
- Regex: '^<UtilsTests/'
- Regex: '^<SystemTests/'
Priority: 6
- Regex: '^<AssetsForTesting/'
- Regex: '^<UtilsTests/'
Priority: 7
- Regex: '^<EngineBenchmarks/'
- Regex: '^<AssetsForTesting/'
Priority: 8
- Regex: '^<AssetsForBenchmarking/'
- Regex: '^<SasquatchBakerTests/'
Priority: 9
- Regex: '^<[a-zA-Z0-9_/]+\.h>'
- Regex: '^<EngineBenchmarks/'
Priority: 10
- Regex: '^<[a-zA-Z0-9_/]+\.hpp>'
Priority: 10
- Regex: '^<[a-zA-Z0-9_/]+>'
- Regex: '^<AssetsForBenchmarking/'
Priority: 11
MainIncludeChar: AngleBracket
- 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'
+5 -1
View File
@@ -11,9 +11,13 @@ 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,
-cppcoreguidelines-pro-type-reinterpret-cast
ExcludeHeaderFilterRegex: '_generated.*'
+12 -12
View File
@@ -18,10 +18,10 @@ jobs:
build_type: ["Debug", "RelWithDebInfo", "Release"]
unity_build: ["ON", "OFF"]
conan_profile: ["./ConanProfiles/host/clang", "./ConanProfiles/host/clang_asan"]
toolmode:
variant:
- { 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 }})"
name: "Build & Test ${{ matrix.build_type }} with ${{ matrix.conan_profile }} (Unity Build: ${{ matrix.unity_build }}, Variant: ${{ matrix.variant.name }})"
steps:
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
@@ -33,18 +33,18 @@ jobs:
- name: Build
run: |
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
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.variant.value }} -of build-${{ matrix.variant.name }}
. ./build-${{ matrix.variant.name }}/build/${{ matrix.build_type }}/generators/conanbuild.sh
cmake -S . -B ./build-${{ matrix.variant.name }}/${{ matrix.build_type }} --toolchain ./build-${{ matrix.variant.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.variant.name }}/${{ matrix.build_type }} --parallel $(nproc)
. ./build-${{ matrix.variant.name }}/build/${{ matrix.build_type }}/generators/deactivate_conanbuild.sh
- name: Unit Tests
run: |
cd ./build-${{ matrix.toolmode.name }}/${{ matrix.build_type }}
. ../../build-${{ matrix.toolmode.name }}/build/${{ matrix.build_type }}/generators/conanbuild.sh
cd ./build-${{ matrix.variant.name }}/${{ matrix.build_type }}
. ../../build-${{ matrix.variant.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
. ../../build-${{ matrix.variant.name }}/build/${{ matrix.build_type }}/generators/deactivate_conanbuild.sh
clang-format:
runs-on: ubuntu-latest
@@ -60,6 +60,6 @@ jobs:
uses: actions/checkout@v6
with:
submodules: recursive
- name: Clang Format Checks
run: chmod +x format.sh && ./format.sh --check Bigfoot
run: chmod +x format.sh && ./format.sh --check Bigfoot
+59 -12
View File
@@ -5,6 +5,10 @@ on:
branches:
- main
- Development
pull_request:
branches:
- main
- Development
jobs:
build-and-test:
@@ -13,7 +17,14 @@ jobs:
container:
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:development
name: "Sonarqube"
strategy:
fail-fast: false
matrix:
variant:
- { value: "True", name: "toolmode", projectKey: "Bigfoot-Tool", projectName: "Bigfoot Tool" }
- { value: "False", name: "game", projectKey: "Bigfoot-Game", projectName: "Bigfoot Game" }
name: "Sonarqube (${{ matrix.variant.name }})"
steps:
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
@@ -23,28 +34,64 @@ jobs:
with:
fetch-depth: 0
submodules: recursive
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Read version
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
- name: Build
run: |
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/*:build_tools=True -o bigfoot/*:vulkan=True
. ./build/Debug/generators/conanbuild.sh
cmake -S . -B ./build/Debug --toolchain ./build/Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja"
cmake --build build/Debug --parallel $(nproc)
. ./build/Debug/generators/deactivate_conanbuild.sh
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=${{ matrix.variant.value }} -o bigfoot/*:vulkan=True -of build-${{ matrix.variant.name }}
. ./build-${{ matrix.variant.name }}/build/Debug/generators/conanbuild.sh
cmake -S . -B ./build-${{ matrix.variant.name }}/Debug --toolchain ./build-${{ matrix.variant.name }}/build/Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja"
cmake --build build-${{ matrix.variant.name }}/Debug --parallel $(nproc)
. ./build-${{ matrix.variant.name }}/build/Debug/generators/deactivate_conanbuild.sh
- name: Run tests
run: |
cd ./build-${{ matrix.variant.name }}/Debug
. ../../build-${{ matrix.variant.name }}/build/Debug/generators/conanbuild.sh
LLVM_PROFILE_FILE="%p.profraw%c" xvfb-run ctest --output-on-failure
. ../../build-${{ matrix.variant.name }}/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-${{ matrix.variant.name }}/Debug/ > tidy_result.txt
- name: Infer
run: infer run --compilation-database build/Debug/compile_commands.json
run: infer run --compilation-database build-${{ matrix.variant.name }}/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-${{ matrix.variant.name }}/Debug -name "*.profraw") -o merged.profdata
BINARIES=($(find build-${{ matrix.variant.name }}/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.projectKey=${{ matrix.variant.projectKey }}
"-Dsonar.projectName=${{ matrix.variant.projectName }}"
-Dsonar.projectVersion=${{ steps.version.outputs.version }}
-Dsonar.cxx.jsonCompilationDatabase=./build-${{ matrix.variant.name }}/Debug/compile_commands.json
-Dsonar.cxx.xunit.reportPaths=build-${{ matrix.variant.name }}/Debug/TestResults/*.xml
-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 }}
+5
View File
@@ -0,0 +1,5 @@
InheritParentConfig: true
Checks: >
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers
@@ -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)
@@ -1,8 +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;
@@ -0,0 +1,7 @@
namespace Flat.Bigfoot;
table BenchmarkAssetReferred
{
}
root_type BenchmarkAssetReferred;
@@ -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
@@ -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_
@@ -20,6 +20,10 @@ static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
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"
@@ -31,6 +35,21 @@ 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 {
@@ -39,6 +58,7 @@ struct BenchmarkAssetT : public ::flatbuffers::NativeTable {
return "Flat.Bigfoot.BenchmarkAssetT";
}
eastl::vector<uint8_t> payload{};
eastl::vector<::Bigfoot::HardReference<::Bigfoot::BenchmarkAssetReferred>> refs{};
};
struct BenchmarkAsset FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
@@ -52,16 +72,22 @@ struct BenchmarkAsset FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
return "Flat.Bigfoot.BenchmarkAsset";
}
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PAYLOAD = 4
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;
@@ -76,6 +102,9 @@ struct BenchmarkAssetBuilder {
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();
@@ -89,8 +118,10 @@ struct BenchmarkAssetBuilder {
inline ::flatbuffers::Offset<BenchmarkAsset> CreateBenchmarkAsset(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> payload = 0) {
::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();
}
@@ -112,6 +143,7 @@ inline void BenchmarkAsset::UnPackTo(BenchmarkAssetT *_o, const ::flatbuffers::r
(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) {
@@ -123,17 +155,23 @@ inline ::flatbuffers::Offset<BenchmarkAsset> BenchmarkAsset::Pack(::flatbuffers:
(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);
_payload,
_refs);
}
inline const ::flatbuffers::TypeTable *BenchmarkAssetTypeTable() {
static const ::flatbuffers::TypeCode type_codes[] = {
{ ::flatbuffers::ET_UCHAR, 1, -1 }
{ ::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, 1, type_codes, nullptr, nullptr, nullptr, nullptr
::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, nullptr
};
return &tt;
}
@@ -189,4 +227,12 @@ inline eastl::unique_ptr<Flat::Bigfoot::BenchmarkAssetT> UnPackSizePrefixedBench
} // 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_
+3
View File
@@ -2,3 +2,6 @@ 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()
@@ -20,9 +20,30 @@ 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;
};
/****************************************************************************************/
void Insert(benchmark::State& state)
BENCHMARK_DEFINE_F(AssetContainerFixture, Insert)(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
@@ -41,29 +62,29 @@ void Insert(benchmark::State& state)
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(BenchmarkAsset)));
}
BENCHMARK(Insert)->Name("AssetContainer/Insert")->Arg(512)->Arg(8192)->Arg(131'072);
BENCHMARK_REGISTER_F(AssetContainerFixture, Insert)->Name("AssetContainer/Insert")->Arg(512)->Arg(8192)->Arg(131'072);
/****************************************************************************************/
void Get(benchmark::State& state)
BENCHMARK_DEFINE_F(AssetContainerFixture, Get)(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<SlotMapKey<>> keys;
keys.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
eastl::vector<ContainerHandle*> handles;
handles.reserve(count);
for (const UUID& uuid: uuids)
{
keys.push_back(container.Insert());
handles.push_back(m_container.AcquireContainerHandle(uuid));
}
Random::shuffle(keys);
for (auto _: state)
{
std::uint64_t sum = 0;
for (const SlotMapKey<> key: keys)
for (ContainerHandle* const handle: handles)
{
if (const BenchmarkAsset* const asset = container.Get(key))
if (const auto* const asset = static_cast<const BenchmarkAsset*>(handle->GetAsset()))
{
sum += asset->GetHeader().type_id;
}
@@ -74,30 +95,22 @@ void Get(benchmark::State& state)
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Get)->Name("AssetContainer/Get")->Arg(512)->Arg(8192)->Arg(131'072);
BENCHMARK_REGISTER_F(AssetContainerFixture, Get)->Name("AssetContainer/Get")->Arg(512)->Arg(8192)->Arg(131'072);
/****************************************************************************************/
void Has(benchmark::State& state)
BENCHMARK_DEFINE_F(AssetContainerFixture, Has)(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
for (auto _: state)
{
bool result = true;
for (const UUID& uuid: uuids)
{
result = result && container.Has(uuid);
result = result && m_container.Has(uuid);
}
benchmark::DoNotOptimize(result);
}
@@ -105,30 +118,22 @@ void Has(benchmark::State& state)
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Has)->Name("AssetContainer/Has")->Arg(512)->Arg(8192)->Arg(131'072);
BENCHMARK_REGISTER_F(AssetContainerFixture, Has)->Name("AssetContainer/Has")->Arg(512)->Arg(8192)->Arg(131'072);
/****************************************************************************************/
void AcquireContainerHandle(benchmark::State& state)
BENCHMARK_DEFINE_F(AssetContainerFixture, AcquireContainerHandle)(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
const eastl::vector<UUID> uuids = InsertRandomAssets(m_container, count);
for (auto _: state)
{
std::uint64_t sum = 0;
for (const UUID& uuid: uuids)
{
sum += container.AcquireContainerHandle(uuid)->GetHardRefCount();
sum += m_container.AcquireContainerHandle(uuid)->GetHardRefCount();
}
benchmark::DoNotOptimize(sum);
}
@@ -136,33 +141,29 @@ void AcquireContainerHandle(benchmark::State& state)
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(AcquireContainerHandle)->Name("AssetContainer/AcquireContainerHandle")->Arg(512)->Arg(8192)->Arg(131'072);
BENCHMARK_REGISTER_F(AssetContainerFixture, AcquireContainerHandle)
->Name("AssetContainer/AcquireContainerHandle")
->Arg(512)
->Arg(8192)
->Arg(131'072);
/****************************************************************************************/
void Remove(benchmark::State& state)
BENCHMARK_DEFINE_F(AssetContainerFixture, Remove)(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (auto _: state)
{
state.PauseTiming();
uuids.clear();
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
uuids = InsertRandomAssets(m_container, count);
state.ResumeTiming();
for (const UUID& uuid: uuids)
{
container.Remove(uuid);
m_container.Remove(uuid);
}
}
@@ -170,5 +171,5 @@ void Remove(benchmark::State& state)
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations() * count * sizeof(BenchmarkAsset)));
}
BENCHMARK(Remove)->Name("AssetContainer/Remove")->Arg(512)->Arg(8192)->Arg(131'072);
BENCHMARK_REGISTER_F(AssetContainerFixture, Remove)->Name("AssetContainer/Remove")->Arg(512)->Arg(8192)->Arg(131'072);
} // namespace Bigfoot
+109 -58
View File
@@ -10,6 +10,7 @@
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
#include <EASTL/optional.h>
#include <EASTL/vector.h>
#include <benchmark/benchmark.h>
@@ -19,96 +20,119 @@
namespace Bigfoot
{
template<template<class> class REFERENCE>
void Construct(benchmark::State& state)
class ReferenceFixture: public benchmark::Fixture
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
protected:
using ReferenceType = REFERENCE<BenchmarkAsset>;
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
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)
{
REFERENCE<BenchmarkAsset> reference {uuid};
ReferenceType reference {m_uuid};
benchmark::DoNotOptimize(reference);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_TEMPLATE(Construct, HardReference)->Name("Reference/HardReference/Construct");
BENCHMARK_TEMPLATE(Construct, SoftReference)->Name("Reference/SoftReference/Construct");
BENCHMARK_REGISTER_F(ReferenceFixture, HardReferenceConstruct)->Name("Reference/HardReference/Construct");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Get(benchmark::State& state)
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, SoftReferenceConstruct, SoftReference)(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
REFERENCE<BenchmarkAsset> reference {uuid};
for (auto _: state)
{
benchmark::DoNotOptimize(reference.Get());
ReferenceType reference {m_uuid};
benchmark::DoNotOptimize(reference);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_TEMPLATE(Get, HardReference)->Name("Reference/HardReference/Get");
BENCHMARK_TEMPLATE(Get, SoftReference)->Name("Reference/SoftReference/Get");
BENCHMARK_REGISTER_F(ReferenceFixture, SoftReferenceConstruct)->Name("Reference/SoftReference/Construct");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Copy(benchmark::State& state)
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceGet, HardReference)(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
REFERENCE<BenchmarkAsset> reference {uuid};
for (auto _: state)
{
REFERENCE<BenchmarkAsset> copy {reference};
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_TEMPLATE(Copy, HardReference)->Name("Reference/HardReference/Copy");
BENCHMARK_TEMPLATE(Copy, SoftReference)->Name("Reference/SoftReference/Copy");
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");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Move(benchmark::State& state)
BENCHMARK_TEMPLATE_DEFINE_F(ReferenceFixture, HardReferenceMove, HardReference)(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
constexpr std::size_t BATCH_SIZE = 128;
eastl::vector<REFERENCE<BenchmarkAsset>> batch;
eastl::vector<ReferenceType> batch;
batch.reserve(BATCH_SIZE);
for (auto _: state)
@@ -117,13 +141,13 @@ void Move(benchmark::State& state)
batch.clear();
for (std::size_t i = 0; i < BATCH_SIZE; ++i)
{
batch.emplace_back(uuid);
batch.emplace_back(m_uuid);
}
state.ResumeTiming();
for (REFERENCE<BenchmarkAsset>& reference: batch)
for (ReferenceType& reference: batch)
{
REFERENCE<BenchmarkAsset> moved {std::move(reference)};
ReferenceType moved {std::move(reference)};
benchmark::DoNotOptimize(moved);
}
}
@@ -131,6 +155,33 @@ void Move(benchmark::State& state)
state.SetItemsProcessed(state.iterations() * BATCH_SIZE);
}
BENCHMARK_TEMPLATE(Move, HardReference)->Name("Reference/HardReference/Move");
BENCHMARK_TEMPLATE(Move, SoftReference)->Name("Reference/SoftReference/Move");
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
+114 -78
View File
@@ -12,7 +12,10 @@
#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>
@@ -23,47 +26,30 @@
namespace Bigfoot
{
#ifdef BIGFOOT_TOOLMODE
namespace
{
using Random = effolkronium::random_static;
constexpr std::uint32_t ASSET_COUNT = 20'000;
constexpr std::size_t ASSET_PAYLOAD_SIZE = 256;
class Fixture: public benchmark::Fixture
class BigFileFixture: public benchmark::Fixture
{
public:
void SetUp(benchmark::State&) override
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
{
BigFile::Request {m_bigFile, "DELETE FROM Asset;"}.Execute();
BigFile::Request {m_bigFile, "DELETE FROM AssetMetadata;"}.Execute();
m_uuids.clear();
m_uuids.reserve(ASSET_COUNT);
eastl::vector<AssetMetadata> metadatas;
eastl::vector<eastl::vector<std::byte>> flatbuffers;
metadatas.reserve(ASSET_COUNT);
flatbuffers.reserve(ASSET_COUNT);
for (std::uint32_t i = 0; i < ASSET_COUNT; ++i)
{
BenchmarkAsset asset;
asset.SetName(asset.GetHeader().uuid.ToString().c_str());
asset.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB});
metadatas.push_back(AssetMetadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name});
flatbuffers.push_back(asset.Pack());
m_uuids.push_back(asset.GetHeader().uuid);
}
m_bigFile.SaveAssetFlatbuffers(metadatas, flatbuffers);
// 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();
}
protected:
[[nodiscard]]
eastl::vector<UUID> RandomBatch(const std::size_t p_batchSize) const
{
@@ -91,38 +77,75 @@ class Fixture: public benchmark::Fixture
return pool;
}
BigFile m_bigFile {File {BIGFILE_ENGINEBENCHMARKS_LOCATION}};
eastl::vector<UUID> m_uuids;
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;
}
};
} // namespace
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffer)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffer)(benchmark::State& state)
{
std::int64_t bytesProcessed = 0;
for (auto _: state)
{
eastl::optional<eastl::vector<std::byte>> flatbuffer = m_bigFile.GetAssetFlatbuffer(
eastl::vector<std::byte> flatbuffer = m_bigFile->GetAssetFlatbuffer(
m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]);
benchmark::DoNotOptimize(flatbuffer);
if (flatbuffer.has_value())
{
bytesProcessed += static_cast<std::int64_t>(flatbuffer->size());
}
bytesProcessed += static_cast<std::int64_t>(flatbuffer.size());
}
state.SetItemsProcessed(state.iterations());
state.SetBytesProcessed(bytesProcessed);
}
BENCHMARK_REGISTER_F(Fixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer");
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer")->UseRealTime();
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffers)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffers)(benchmark::State& state)
{
const auto batchSize = static_cast<std::size_t>(state.range(0));
@@ -134,7 +157,7 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffers)(benchmark::State& state)
for (auto _: state)
{
eastl::vector<eastl::vector<std::byte>> flatbuffers = m_bigFile.GetAssetFlatbuffers(batchPool[batchIndex]);
eastl::vector<eastl::vector<std::byte>> flatbuffers = m_bigFile->GetAssetFlatbuffers(batchPool[batchIndex]);
benchmark::DoNotOptimize(flatbuffers);
for (const eastl::vector<std::byte>& flatbuffer: flatbuffers)
@@ -149,31 +172,34 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetFlatbuffers)(benchmark::State& state)
state.SetBytesProcessed(bytesProcessed);
}
BENCHMARK_REGISTER_F(Fixture, GetAssetFlatbuffers)
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffers)
->Name("BigFile/GetAssetFlatbuffers")
->Arg(1)
->Arg(8)
->Arg(64)
->Arg(512)
->Arg(4096);
->Arg(4096)
->UseRealTime();
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, GetAssetMetadata)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetHardReferences)(benchmark::State& state)
{
for (auto _: state)
{
benchmark::DoNotOptimize(m_bigFile.GetAssetMetadata(m_uuids[Random::get<std::size_t>(0, m_uuids.size() - 1)]));
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(Fixture, GetAssetMetadata)->Name("BigFile/GetAssetMetadata");
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetHardReferences)->Name("BigFile/GetAssetHardReferences")->UseRealTime();
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, GetAssetMetadatas)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, GetAssetsHardReferences)(benchmark::State& state)
{
const auto batchSize = static_cast<std::size_t>(state.range(0));
@@ -183,24 +209,27 @@ BENCHMARK_DEFINE_F(Fixture, GetAssetMetadatas)(benchmark::State& state)
std::size_t batchIndex = 0;
for (auto _: state)
{
benchmark::DoNotOptimize(m_bigFile.GetAssetMetadatas(batchPool[batchIndex]));
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(Fixture, GetAssetMetadatas)
->Name("BigFile/GetAssetMetadatas")
BENCHMARK_REGISTER_F(BigFileFixture, GetAssetsHardReferences)
->Name("BigFile/GetAssetsHardReferences")
->Arg(1)
->Arg(8)
->Arg(64)
->Arg(512)
->Arg(4096);
->Arg(4096)
->UseRealTime();
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffer)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffer)(benchmark::State& state)
{
std::int64_t bytesProcessed = 0;
@@ -208,29 +237,30 @@ BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffer)(benchmark::State& state)
{
state.PauseTiming();
BenchmarkAsset asset;
asset.SetName(asset.GetHeader().uuid.ToString().c_str());
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 AssetMetadata metadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name};
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(metadata, flatbuffer);
m_bigFile->SaveAssetFlatbuffer(asset.GetHeader().uuid, flatbuffer, hardReferences);
}
state.SetItemsProcessed(state.iterations());
state.SetBytesProcessed(bytesProcessed);
}
BENCHMARK_REGISTER_F(Fixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer");
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer")->UseRealTime();
/****************************************************************************************/
BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffers)(benchmark::State& state)
BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffers)(benchmark::State& state)
{
const auto batchSize = static_cast<std::size_t>(state.range(0));
std::int64_t bytesProcessed = 0;
@@ -238,39 +268,45 @@ BENCHMARK_DEFINE_F(Fixture, SaveAssetFlatbuffers)(benchmark::State& state)
for (auto _: state)
{
state.PauseTiming();
eastl::vector<AssetMetadata> metadatas;
eastl::vector<UUID> uuids;
eastl::vector<eastl::vector<std::byte>> flatbuffers;
metadatas.reserve(batchSize);
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().c_str());
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)]);
}
metadatas.push_back(AssetMetadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name});
flatbuffers.push_back(asset.Pack());
bytesProcessed += static_cast<std::int64_t>(flatbuffers.back().size());
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(metadatas, flatbuffers);
m_bigFile->SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
}
state.SetItemsProcessed(state.iterations() * batchSize);
state.SetBytesProcessed(bytesProcessed);
}
BENCHMARK_REGISTER_F(Fixture, SaveAssetFlatbuffers)
BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffers)
->Name("BigFile/SaveAssetFlatbuffers")
->Arg(1)
->Arg(8)
->Arg(64)
->Arg(512)
->Arg(4096);
->Arg(4096)
->UseRealTime();
#endif
} // namespace Bigfoot
+2
View File
@@ -10,3 +10,5 @@ target_link_libraries(${PROJECT_NAME}
$<LINK_LIBRARY:WHOLE_ARCHIVE,AssetsForBenchmarking>)
bigfoot_create_bigfile("Benchmarks/Bigfoot")
bigfoot_create_sasquatch_info()
@@ -16,7 +16,7 @@ namespace Bigfoot
/*
* BigFile location
*/
constexpr eastl::string_view BIGFILE_ENGINEBENCHMARKS_LOCATION {"./EngineBenchmarks-bigfile.db"};
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
+1
View File
@@ -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")
@@ -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
@@ -11,10 +11,7 @@
namespace Bigfoot
{
namespace
{
using Random = effolkronium::random_static;
} // namespace
struct MyComplexStruct
{
+4 -1
View File
@@ -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,63 @@
/*********************************************************************
* \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 = reinterpret_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
@@ -8,6 +8,77 @@
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;
+409 -165
View File
@@ -8,9 +8,9 @@
#include <Engine/EngineAssertHandler.hpp>
#include <Utils/Format.hpp>
#include <EASTL/algorithm.h>
#include <EASTL/sort.h>
#include <fmt/format.h>
namespace Bigfoot
{
@@ -69,6 +69,19 @@ void BigFile::RollbackTransaction()
/****************************************************************************************/
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]]
@@ -78,14 +91,14 @@ BigFile::~BigFile()
/****************************************************************************************/
eastl::optional<eastl::vector<std::byte>> BigFile::GetAssetFlatbuffer(const UUID& p_uuid) const
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 eastl::nullopt;
return {};
}
const eastl::span<const std::byte> flatbuffer = assetRequest.Get(0);
@@ -96,58 +109,74 @@ eastl::optional<eastl::vector<std::byte>> BigFile::GetAssetFlatbuffer(const UUID
eastl::vector<eastl::vector<std::byte>> BigFile::GetAssetFlatbuffers(const eastl::span<const UUID> p_uuids) const
{
eastl::vector<eastl::vector<std::byte>> flatbuffers;
eastl::vector<eastl::vector<std::byte>> flatbuffers(p_uuids.size());
if (p_uuids.empty())
{
return flatbuffers;
}
flatbuffers.reserve(p_uuids.size());
if (p_uuids.size() == 1)
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)
{
if (eastl::optional<eastl::vector<std::byte>> flatbuffer = GetAssetFlatbuffer(p_uuids[0]);
flatbuffer.has_value())
{
flatbuffers.push_back(std::move(*flatbuffer));
}
return flatbuffers;
uuidToIndices[p_uuids[i]].push_back(i);
}
eastl::vector<UUID> uniqueUUIDs {p_uuids.begin(), p_uuids.end()};
eastl::sort(uniqueUUIDs.begin(), uniqueUUIDs.end());
uniqueUUIDs.erase(eastl::unique(uniqueUUIDs.begin(), uniqueUUIDs.end()), uniqueUUIDs.end());
eastl::string placeholders;
placeholders.reserve(uniqueUUIDs.size() * 3);
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
eastl::vector<UUID> distinctUUIDs;
distinctUUIDs.reserve(uuidToIndices.size());
for (const auto& [uuid, indices]: uuidToIndices)
{
if (i > 0)
{
placeholders += ", ";
}
placeholders += "?";
distinctUUIDs.push_back(uuid);
}
const std::string sqlRequest = fmt::format("SELECT Asset FROM Asset WHERE UUID IN ({});", placeholders);
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;
BigFile::Request assetRequest {*this, sqlRequest.c_str()};
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
for (std::size_t chunkStart = 0; chunkStart < distinctUUIDs.size(); chunkStart += maxRowsPerStatement)
{
assetRequest.Bind(static_cast<std::uint32_t>(i + 1), uniqueUUIDs[i].ToBytes());
}
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, distinctUUIDs.size() - chunkStart);
flatbuffers.reserve(uniqueUUIDs.size());
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
{
if (!assetRequest.Step())
eastl::string placeholders;
placeholders.reserve(chunkSize * 2);
for (std::size_t i = 0; i < chunkSize; ++i)
{
break;
if (i > 0)
{
placeholders += ", ";
}
placeholders += "?";
}
const eastl::span<const std::byte> flatbuffer = assetRequest.Get(0);
flatbuffers.emplace_back(flatbuffer.begin(), flatbuffer.end());
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;
@@ -155,133 +184,271 @@ eastl::vector<eastl::vector<std::byte>> BigFile::GetAssetFlatbuffers(const eastl
/****************************************************************************************/
eastl::optional<AssetMetadata> BigFile::GetAssetMetadata(const UUID& p_uuid) const
eastl::vector<UUID> BigFile::GetAssetHardReferences(const UUID& p_uuid) const
{
BigFile::Request assetRequest {
*this,
"SELECT UUID, Name, TypeID, TypeName, CreateTime, ModificationTime FROM AssetMetadata WHERE UUID = ?1;"};
assetRequest.Bind(1, p_uuid.ToBytes());
BigFile::Request hardReferencesRequest {*this, "SELECT HardReference FROM AssetHardReference WHERE UUID = ?1;"};
hardReferencesRequest.Bind(1, p_uuid.ToBytes());
if (!assetRequest.Step())
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 AssetMetadata {.m_uuid = UUID {std::span<const std::byte, UUID::UUID_BYTE_SIZE> {
static_cast<eastl::span<const std::byte>>(assetRequest.Get(0)).data(),
UUID::UUID_BYTE_SIZE}},
.m_name = eastl::string {static_cast<eastl::string_view>(assetRequest.Get(1))},
.m_typeID = assetRequest.Get(2),
.m_typeName = eastl::string {static_cast<eastl::string_view>(assetRequest.Get(3))},
.m_creationTime = Time {static_cast<std::uint64_t>(assetRequest.Get(4))},
.m_modificationTime = Time {static_cast<std::uint64_t>(assetRequest.Get(5))}};
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<AssetMetadata> BigFile::GetAssetMetadatas(const eastl::span<const UUID> p_uuids) const
eastl::vector<eastl::optional<BigFile::AssetTime>> BigFile::GetAssetsTime(const eastl::span<const UUID> p_uuids) const
{
eastl::vector<AssetMetadata> metadatas;
eastl::vector<eastl::optional<AssetTime>> times(p_uuids.size());
if (p_uuids.empty())
{
return metadatas;
return times;
}
metadatas.reserve(p_uuids.size());
if (p_uuids.size() == 1)
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)
{
if (eastl::optional<AssetMetadata> metadata = GetAssetMetadata(p_uuids[0]); metadata.has_value())
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)
{
metadatas.push_back(std::move(*metadata));
if (i > 0)
{
placeholders += ", ";
}
placeholders += "?";
}
return metadatas;
}
const eastl::string sql = Format(
"SELECT UUID, CreateTime, ModificationTime FROM AssetMetadata WHERE UUID IN ({});",
placeholders);
eastl::vector<UUID> uniqueUUIDs {p_uuids.begin(), p_uuids.end()};
eastl::sort(uniqueUUIDs.begin(), uniqueUUIDs.end());
uniqueUUIDs.erase(eastl::unique(uniqueUUIDs.begin(), uniqueUUIDs.end()), uniqueUUIDs.end());
eastl::string placeholders;
placeholders.reserve(uniqueUUIDs.size() * 3);
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
{
if (i > 0)
BigFile::Request timeRequest {*this, sql};
for (std::size_t i = 0; i < chunkSize; ++i)
{
placeholders += ", ";
}
placeholders += "?";
}
const std::string sqlRequest = fmt::format("SELECT UUID, Name, TypeID, TypeName, CreateTime, ModificationTime "
"FROM AssetMetadata WHERE UUID IN ({});",
placeholders);
BigFile::Request assetRequest {*this, sqlRequest.c_str()};
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
{
assetRequest.Bind(static_cast<std::uint32_t>(i + 1), uniqueUUIDs[i].ToBytes());
}
metadatas.reserve(uniqueUUIDs.size());
for (std::size_t i = 0; i < uniqueUUIDs.size(); ++i)
{
if (!assetRequest.Step())
{
break;
timeRequest.Bind(static_cast<std::uint32_t>(i + 1), distinctUUIDs[chunkStart + i].ToBytes());
}
metadatas.push_back(
AssetMetadata {.m_uuid = UUID {std::span<const std::byte, UUID::UUID_BYTE_SIZE> {
static_cast<eastl::span<const std::byte>>(assetRequest.Get(0)).data(),
UUID::UUID_BYTE_SIZE}},
.m_name = eastl::string {static_cast<eastl::string_view>(assetRequest.Get(1))},
.m_typeID = assetRequest.Get(2),
.m_typeName = eastl::string {static_cast<eastl::string_view>(assetRequest.Get(3))},
.m_creationTime = Time {static_cast<std::uint64_t>(assetRequest.Get(4))},
.m_modificationTime = Time {static_cast<std::uint64_t>(assetRequest.Get(5))}});
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 metadatas;
return times;
}
/****************************************************************************************/
void BigFile::SaveAssetFlatbuffer(const AssetMetadata& p_metadata, const eastl::span<const std::byte> p_flatbuffer)
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, Name, TypeID, TypeName) VALUES (?1, ?2, ?3, ?4) "
"ON CONFLICT(UUID) DO UPDATE SET Name = excluded.Name, TypeID = excluded.TypeID, TypeName = excluded.TypeName;"};
metadataRequest.Bind(1, p_metadata.m_uuid.ToBytes());
metadataRequest.Bind(2, p_metadata.m_name);
metadataRequest.Bind(3, p_metadata.m_typeID);
metadataRequest.Bind(4, p_metadata.m_typeName);
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_metadata.m_uuid.ToBytes());
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 AssetMetadata> p_metadatas,
const eastl::span<const eastl::vector<std::byte>> p_flatbuffers)
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_metadatas.size() == p_flatbuffers.size(),
"p_metadatas and p_flatbuffers must have the same size");
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_metadatas.empty())
if (p_uuids.empty())
{
return;
}
@@ -289,45 +456,41 @@ void BigFile::SaveAssetFlatbuffers(const eastl::span<const AssetMetadata> p_meta
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_metadatas.size(); chunkStart += maxRowsPerStatement)
for (std::size_t chunkStart = 0; chunkStart < p_uuids.size(); chunkStart += maxRowsPerStatement)
{
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, p_metadatas.size() - chunkStart);
const std::size_t chunkSize = eastl::min(maxRowsPerStatement, p_uuids.size() - chunkStart);
eastl::string metadataPlaceholders;
metadataPlaceholders.reserve(chunkSize * 14);
metadataPlaceholders.reserve(chunkSize * METADATA_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
for (std::size_t i = 0; i < chunkSize; ++i)
{
if (i > 0)
{
metadataPlaceholders += ", ";
}
metadataPlaceholders += "(?, ?, ?, ?)";
metadataPlaceholders += "(?)";
}
const std::string metadataSQL = fmt::format(
"INSERT INTO AssetMetadata (UUID, Name, TypeID, TypeName) VALUES {} "
"ON CONFLICT(UUID) DO UPDATE SET Name = excluded.Name, TypeID = excluded.TypeID, TypeName = "
"excluded.TypeName;",
const eastl::string metadataSQL = Format(
"INSERT INTO AssetMetadata (UUID) VALUES {} ON CONFLICT(UUID) DO NOTHING;",
metadataPlaceholders);
BigFile::Request metadataRequest {*this, metadataSQL.c_str()};
BigFile::Request metadataRequest {*this, metadataSQL};
std::uint32_t metadataBindIndex = 1;
for (std::size_t i = 0; i < chunkSize; ++i)
{
const AssetMetadata& metadata = p_metadatas[chunkStart + i];
metadataRequest.Bind(metadataBindIndex++, metadata.m_uuid.ToBytes());
metadataRequest.Bind(metadataBindIndex++, metadata.m_name);
metadataRequest.Bind(metadataBindIndex++, metadata.m_typeID);
metadataRequest.Bind(metadataBindIndex++, metadata.m_typeName);
metadataRequest.Bind(metadataBindIndex++, p_uuids[chunkStart + i].ToBytes());
}
metadataRequest.Execute();
eastl::string assetPlaceholders;
assetPlaceholders.reserve(chunkSize * 8);
assetPlaceholders.reserve(chunkSize * ASSET_PLACEHOLDER_RESERVE_CHARS_PER_ROW);
for (std::size_t i = 0; i < chunkSize; ++i)
{
if (i > 0)
@@ -337,18 +500,88 @@ void BigFile::SaveAssetFlatbuffers(const eastl::span<const AssetMetadata> p_meta
assetPlaceholders += "(?, ?)";
}
const std::string assetSQL = fmt::format(
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.c_str()};
BigFile::Request assetRequest {*this, assetSQL};
std::uint32_t assetBindIndex = 1;
for (std::size_t i = 0; i < chunkSize; ++i)
{
assetRequest.Bind(assetBindIndex++, p_metadatas[chunkStart + i].m_uuid.ToBytes());
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();
@@ -372,7 +605,7 @@ BigFile::Request::Request(const BigFile& p_bigFile, const eastl::string_view p_r
[[maybe_unused]]
const int result = sqlite3_prepare_v2(m_db,
p_request.data(),
static_cast<std::uint32_t>(p_request.size()),
static_cast<int>(p_request.size()),
&m_statement,
nullptr);
CRITICAL_ASSERT(EngineAssertHandler,
@@ -386,11 +619,11 @@ BigFile::Request::Request(const BigFile& p_bigFile, const eastl::string_view p_r
void BigFile::Request::Bind(const std::uint32_t p_index, const std::int32_t p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, p_value);
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));
}
@@ -399,11 +632,11 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const std::int32_t p_va
void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint32_t p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, p_value);
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));
}
@@ -412,11 +645,11 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint32_t p_v
void BigFile::Request::Bind(const std::uint32_t p_index, const std::int64_t p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, p_value);
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));
}
@@ -425,11 +658,11 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const std::int64_t p_va
void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint64_t p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, std::bit_cast<std::int64_t>(p_value));
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));
}
@@ -438,11 +671,11 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const std::uint64_t p_v
void BigFile::Request::Bind(const std::uint32_t p_index, const float p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, p_value);
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));
}
@@ -451,11 +684,11 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const float p_value)
void BigFile::Request::Bind(const std::uint32_t p_index, const double p_value)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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, p_index, p_value);
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));
}
@@ -464,14 +697,14 @@ void BigFile::Request::Bind(const std::uint32_t p_index, const double p_value)
void BigFile::Request::Bind(const std::uint32_t p_index, const eastl::string_view p_value, const CopyValue p_copy)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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,
p_index,
static_cast<int>(p_index),
p_value.data(),
static_cast<std::uint32_t>(p_value.size()),
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));
}
@@ -483,14 +716,14 @@ void BigFile::Request::Bind(const std::uint32_t p_index,
const CopyValue p_copy)
{
ASSERT(EngineAssertHandler,
(p_index >= 1) && (p_index <= static_cast<std::uint32_t>(sqlite3_bind_parameter_count(m_statement))),
(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,
p_index,
static_cast<int>(p_index),
p_value.data(),
static_cast<std::uint32_t>(p_value.size()),
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));
}
@@ -531,58 +764,69 @@ BigFile::Request::Column::Column(const Request& p_request, const std::uint32_t p
BigFile::Request::Column::operator std::int32_t() const
{
return sqlite3_column_int(m_statement, m_index);
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, m_index);
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, m_index);
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, m_index));
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, m_index));
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, m_index);
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, m_index)),
static_cast<std::size_t>(sqlite3_column_bytes(m_statement, m_index))};
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, m_index)),
static_cast<std::size_t>(sqlite3_column_bytes(m_statement, m_index))};
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)))};
}
/****************************************************************************************/
@@ -1,27 +1,16 @@
PRAGMA journal_mode=WAL;
CREATE TABLE IF NOT EXISTS AssetMetadata (
UUID BLOB NOT NULL,
Name TEXT NOT NULL UNIQUE,
TypeID INTEGER NOT NULL,
TypeName TEXT NOT NULL,
UUID BLOB NOT NULL,
CreateTime INTEGER NOT NULL DEFAULT(CAST(unixepoch('subsec') * 1000000 AS INTEGER)),
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 AssetMetadata_UpdateTime
AFTER UPDATE OF Name, TypeID, TypeName ON AssetMetadata FOR EACH ROW
BEGIN
UPDATE AssetMetadata
SET ModificationTime = CAST(unixepoch('subsec') * 1000000 AS INTEGER)
WHERE UUID = NEW.UUID;
END;
CREATE TABLE IF NOT EXISTS Asset (
UUID BLOB NOT NULL,
UUID BLOB NOT NULL,
Asset BLOB NOT NULL,
@@ -35,4 +24,15 @@ BEGIN
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);
@@ -10,7 +10,6 @@ table AssetHeader
name: string (required, id: 1);
type_id: AssetTypeID (required, native_inline, id: 2);
type_name: string (required, id: 3);
version: uint = 0 (id: 4);
}
table Asset
@@ -6,39 +6,27 @@
*********************************************************************/
#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/reflection.h>
#include <flatbuffers/minireflect.h>
#include <rapidhash.h>
namespace Bigfoot
{
class AssetBase
{
public:
virtual ~AssetBase() = default;
protected:
AssetBase() = default;
AssetBase(const AssetBase& p_asset) = delete;
AssetBase(AssetBase&& p_asset) = default;
AssetBase& operator=(const AssetBase& p_asset) = delete;
AssetBase& operator=(AssetBase&& p_asset) = default;
};
template<class ASSET>
requires AssetTraits<ASSET>
class Asset: public AssetBase
{
public:
using FLAT_ASSET = typename ASSET::FLAT;
using FLAT_ASSET = ASSET::FLAT;
/**
* Constructor
@@ -58,7 +46,7 @@ class Asset: public AssetBase
* \return The header of the asset
*/
[[nodiscard]]
const ::Flat::Bigfoot::AssetHeaderT& GetHeader() const
const ::Flat::Bigfoot::AssetHeaderT& GetHeader() const override
{
return m_header;
}
@@ -73,23 +61,13 @@ class Asset: public AssetBase
m_header.name = p_name;
}
/**
* Set the version of the asset
*
* \param p_name The version of the asset
*/
void SetVersion(const std::uint32_t p_version)
{
m_header.version = p_version;
}
/**
* Get the asset
*
* \return The asset
*/
[[nodiscard]]
const typename FLAT_ASSET::NativeTableType& GetAsset() const
const FLAT_ASSET::NativeTableType& GetAsset() const
{
return m_asset;
}
@@ -100,7 +78,7 @@ class Asset: public AssetBase
* \return The asset
*/
[[nodiscard]]
typename FLAT_ASSET::NativeTableType& GetAsset()
FLAT_ASSET::NativeTableType& GetAsset()
{
return m_asset;
}
@@ -110,20 +88,21 @@ class Asset: public AssetBase
*
* \param p_flatbuffer The flatbuffer to unpack
*/
void Unpack(const eastl::span<const std::byte> p_flatbuffer)
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()};
flatbuffers::Verifier verifier {reinterpret_cast<const 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!");
#else
const ::Flat::Bigfoot::Asset* asset = flatbuffers::GetRoot<::Flat::Bigfoot::Asset>(p_flatbuffer.data());
#endif
asset->header()->UnPackTo(&m_header);
@@ -136,7 +115,7 @@ class Asset: public AssetBase
* \return The packed asset
*/
[[nodiscard]]
eastl::vector<std::byte> Pack() const
eastl::vector<std::byte> Pack() const override
{
flatbuffers::FlatBufferBuilder innerAssetFbb;
innerAssetFbb.Finish(FLAT_ASSET::Pack(innerAssetFbb, &m_asset));
@@ -146,8 +125,39 @@ class Asset: public AssetBase
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())};
return eastl::vector<std::byte> {
reinterpret_cast<std::byte*>(assetFbb.GetBufferPointer()),
reinterpret_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 {reinterpret_cast<const 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();
}
/**
@@ -178,12 +188,30 @@ class Asset: public AssetBase
/**
* The asset
*/
typename FLAT_ASSET::NativeTableType m_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
@@ -10,6 +10,8 @@
#include <System/UUID/UUID.hpp>
#include <flatbuffers/minireflect.h>
#include <concepts>
namespace Bigfoot
@@ -25,6 +27,7 @@ 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*>;
};
/**
@@ -6,7 +6,7 @@
*********************************************************************/
#ifndef BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
#define BIGFOOT_ENGINE_ASSET_ASSETCONTAINER_HPP
#include <Engine/Asset/Asset.hpp>
#include <Engine/Asset/AssetBase.hpp>
#include <Engine/Asset/AssetConcepts.hpp>
#include <Engine/EngineAssertHandler.hpp>
@@ -18,40 +18,41 @@
namespace Bigfoot
{
class AssetContainerBase;
/**
* An indirection that allows References to immediately get the asset, without fetching the actual container
*/
class ContainerHandle
{
public:
/**
* Constructor
*
* \param p_assetContainer The originating assetcontainer
* \param p_uuid The UUID of tha asset
*/
ContainerHandle(AssetContainerBase* p_assetContainer, const UUID p_uuid);
/*
* Get the HardRef count
*
* \return The HardRef count.
*/
[[nodiscard]]
std::uint32_t GetHardRefCount() const
{
return m_hardRefCount;
}
std::uint32_t GetHardRefCount() const;
/*
* Increment the HardRef count
*
*/
void IncrementHardRefCount()
{
++m_hardRefCount;
}
void IncrementHardRefCount();
/*
* Decrement the HardRef count
*
*/
void DecrementHardRefCount()
{
--m_hardRefCount;
}
void DecrementHardRefCount();
/*
* Get the SoftRef count
@@ -59,28 +60,19 @@ class ContainerHandle
* \return The SoftRef count.
*/
[[nodiscard]]
std::uint32_t GetSoftRefCount() const
{
return m_softRefCount;
}
std::uint32_t GetSoftRefCount() const;
/*
* Increment the SoftRef count
*
*/
void IncrementSoftRefCount()
{
++m_softRefCount;
}
void IncrementSoftRefCount();
/*
* Decrement the SoftRef count
*
*/
void DecrementSoftRefCount()
{
--m_softRefCount;
}
void DecrementSoftRefCount();
/*
* Get the Asset
@@ -88,10 +80,7 @@ class ContainerHandle
* \return The asset, can be nullptr
*/
[[nodiscard]]
AssetBase* GetAsset()
{
return m_asset;
}
AssetBase* GetAsset();
/*
* Get the Asset
@@ -99,20 +88,14 @@ class ContainerHandle
* \return The asset
*/
[[nodiscard]]
const AssetBase* GetAsset() const
{
return m_asset;
}
const AssetBase* GetAsset() const;
/*
* Set the asset
*
* \param p_asset The asset
*/
void SetAsset(AssetBase* p_asset)
{
m_asset = p_asset;
}
void SetAsset(AssetBase* p_asset);
private:
/*
@@ -129,6 +112,16 @@ class ContainerHandle
* The asset
*/
AssetBase* m_asset = nullptr;
/**
* The asset container
*/
AssetContainerBase* m_assetContainer = nullptr;
/**
* The UUID of the asset
*/
UUID m_uuid = UUID::NULL_UUID;
};
/****************************************************************************************/
@@ -141,6 +134,23 @@ class AssetContainerBase
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
*
@@ -148,11 +158,7 @@ class AssetContainerBase
* \return True if in AssetContainer, false otherwise
*/
[[nodiscard]]
bool Has(const UUID& p_uuid) const
{
const Entry* entry = FindEntry(p_uuid);
return entry != nullptr && entry->m_assetSlotKey.Valid();
}
bool Has(const UUID& p_uuid) const;
/*
* Acquires a ContainerHandle for a given UUID
@@ -162,27 +168,44 @@ class AssetContainerBase
* \return A ContainerHandle for the UUID
*/
[[nodiscard]]
ContainerHandle* AcquireContainerHandle(const UUID& p_uuid)
{
return GetOrCreateEntry(p_uuid).m_handle;
}
ContainerHandle* AcquireContainerHandle(const UUID& p_uuid);
/*
* Remove an Asset from the AssetContainer
*
* \param p_uuid The UUID to remove
*/
virtual void Remove(const UUID& p_uuid)
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr)
{
m_handles.Remove(entry->m_handleSlotKey);
m_uuidToEntry.erase(p_uuid);
}
}
void Remove(const UUID& p_uuid);
/**
* Add an asset to be removed
*
* \param p_uuid UUID of the asset to remove
*/
void QueueForRemoval(const UUID& p_uuid);
/**
* Remove an asset from the pending remove queue
*
* \param p_uuid UUID of the asset to cancel removal
*/
void CancelRemoval(const UUID& p_uuid);
/**
* Process the assets that have been marked for remove
*
*/
void ProcessPendingRemovals();
[[nodiscard]]
bool IsMarkedForRemove(const UUID& p_uuid);
AssetContainerBase& operator=(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
{
@@ -204,7 +227,14 @@ class AssetContainerBase
AssetContainerBase() = default;
AssetContainerBase(AssetContainerBase&& p_container) = delete;
/*
* 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
@@ -213,24 +243,7 @@ class AssetContainerBase
* \return Pointer to an Entry, nullptr otherwise
*/
[[nodiscard]]
Entry* FindEntry(const UUID& p_uuid)
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
/*
* 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
{
const auto it = m_uuidToEntry.find(p_uuid);
return it != m_uuidToEntry.end() ? &it->second : nullptr;
}
const Entry* FindEntry(const UUID& p_uuid) const;
/*
* Finds an entry matching the Asset
@@ -240,17 +253,14 @@ class AssetContainerBase
* \return The entry matching the Asset
*/
[[nodiscard]]
Entry& GetOrCreateEntry(const UUID& p_uuid)
{
auto [it, inserted] = m_uuidToEntry.try_emplace(p_uuid);
if (inserted)
{
it->second.m_handleSlotKey = m_handles.Insert();
it->second.m_handle = m_handles.Get(it->second.m_handleSlotKey);
}
Entry& GetOrCreateEntry(const UUID& p_uuid);
return it->second;
}
/*
* Remove an asset
*
* \param p_key The key to the asset
*/
virtual void RemoveAsset(SlotMapKey p_slotKey) = 0;
private:
/*
@@ -262,6 +272,11 @@ class AssetContainerBase
* Every Asset added to the map
*/
ankerl::unordered_dense::map<UUID, Entry> m_uuidToEntry;
/**
* Assets queued for removal
*/
eastl::vector<UUID> m_pendingRemovalUuids;
};
/****************************************************************************************/
@@ -277,16 +292,42 @@ class AssetContainer: public AssetContainerBase
/*
* Inserts an Asset to the AssetContainer
*
* \tparam ARGS Arguments to construct the asset
* \param p_args The arguments
* \return The key to the inserted asset.
* \param p_flatbuffer The flatbuffer to unpack to the asset
* \return The handle of the inserted asset
*/
template<class... ARGS>
[[nodiscard]]
SlotMapKey Insert(ARGS&&... p_args)
ContainerHandle* Insert(const eastl::span<const std::byte> p_flatbuffer) override
{
SlotMapKey slotKey = m_assets.Insert(std::forward<ARGS>(p_args)...);
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())
@@ -300,51 +341,19 @@ class AssetContainer: public AssetContainerBase
entry.m_handle->SetAsset(asset);
}
return slotKey;
return entry.m_handle;
}
/*
* Get the Asset
* Remove an asset
*
* \param p_key The key to the asset
* \return Pointer to the Asset
*/
[[nodiscard]]
ASSET* Get(const SlotMapKey p_key)
void RemoveAsset(SlotMapKey p_key) override
{
return m_assets.Get(p_key);
m_assets.Remove(p_key);
}
/*
* Get the Asset
*
* \param p_key The key to the asset
* \return Pointer to the Asset
*/
[[nodiscard]]
const ASSET* Get(const SlotMapKey p_key) const
{
return m_assets.Get(p_key);
}
/*
* Remove an Asset from the AssetContainer
*
* \param p_uuid The UUID to remove
*/
void Remove(const UUID& p_uuid) override
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr && entry->m_assetSlotKey.Valid())
{
entry->m_handle->SetAsset(nullptr);
m_assets.Remove(entry->m_assetSlotKey);
entry->m_assetSlotKey = SlotMapKey {};
}
AssetContainerBase::Remove(p_uuid);
}
private:
/*
* The assets
*/
@@ -19,6 +19,13 @@
return AssetType::GetTypeID(); \
} \
AssetContainer<AssetType> g_containerFor##AssetName; \
const bool g_insertedContainerFor##AssetName = g_assetLibrary.Insert(&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
@@ -52,15 +52,7 @@ class AssetLibrary
* \return The AssetContainer for the AssetType. Nullptr if not found
*/
[[nodiscard]]
AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID)
{
if (const auto container = m_assetContainers.find(p_typeID); container != m_assetContainers.end())
{
return container->second;
}
return nullptr;
}
AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID);
/**
* Get the AssetContainer for a given AssetType
@@ -69,19 +61,46 @@ class AssetLibrary
* \return The AssetContainer for the AssetType. Nullptr if not found
*/
[[nodiscard]]
const AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID) const
{
if (const auto container = m_assetContainers.find(p_typeID); container != m_assetContainers.end())
{
return container->second;
}
const AssetContainerBase* GetAssetContainer(const AssetTypeID p_typeID) const;
return nullptr;
}
/**
* 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;
@@ -15,13 +15,10 @@ using AssetTypeID = std::uint64_t;
/****************************************************************************************/
namespace Flat
{
namespace Bigfoot
namespace Flat::Bigfoot
{
struct AssetTypeID;
}
} // namespace Flat
} // namespace Flat::Bigfoot
/****************************************************************************************/
@@ -53,7 +53,6 @@ struct AssetHeaderT : public ::flatbuffers::NativeTable {
eastl::string name{};
::Bigfoot::AssetTypeID type_id{};
eastl::string type_name{};
uint32_t version = 0;
};
struct AssetHeader FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
@@ -70,8 +69,7 @@ struct AssetHeader FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
VT_UUID = 4,
VT_NAME = 6,
VT_TYPE_ID = 8,
VT_TYPE_NAME = 10,
VT_VERSION = 12
VT_TYPE_NAME = 10
};
const Flat::Bigfoot::UUID *uuid() const {
return GetStruct<const Flat::Bigfoot::UUID *>(VT_UUID);
@@ -85,9 +83,6 @@ struct AssetHeader FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
const ::flatbuffers::String *type_name() const {
return GetPointer<const ::flatbuffers::String *>(VT_TYPE_NAME);
}
uint32_t version() const {
return GetField<uint32_t>(VT_VERSION, 0);
}
template <bool B = false>
bool Verify(::flatbuffers::VerifierTemplate<B> &verifier) const {
return VerifyTableStart(verifier) &&
@@ -97,7 +92,6 @@ struct AssetHeader FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
VerifyFieldRequired<Flat::Bigfoot::AssetTypeID>(verifier, VT_TYPE_ID, 8) &&
VerifyOffsetRequired(verifier, VT_TYPE_NAME) &&
verifier.VerifyString(type_name()) &&
VerifyField<uint32_t>(verifier, VT_VERSION, 4) &&
verifier.EndTable();
}
AssetHeaderT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const;
@@ -121,9 +115,6 @@ struct AssetHeaderBuilder {
void add_type_name(::flatbuffers::Offset<::flatbuffers::String> type_name) {
fbb_.AddOffset(AssetHeader::VT_TYPE_NAME, type_name);
}
void add_version(uint32_t version) {
fbb_.AddElement<uint32_t>(AssetHeader::VT_VERSION, version, 0);
}
explicit AssetHeaderBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
@@ -144,10 +135,8 @@ inline ::flatbuffers::Offset<AssetHeader> CreateAssetHeader(
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,
uint32_t version = 0) {
::flatbuffers::Offset<::flatbuffers::String> type_name = 0) {
AssetHeaderBuilder builder_(_fbb);
builder_.add_version(version);
builder_.add_type_name(type_name);
builder_.add_type_id(type_id);
builder_.add_name(name);
@@ -262,7 +251,6 @@ inline void AssetHeader::UnPackTo(AssetHeaderT *_o, const ::flatbuffers::resolve
{ 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()); }
{ auto _e = version(); _o->version = _e; }
}
inline ::flatbuffers::Offset<AssetHeader> CreateAssetHeader(::flatbuffers::FlatBufferBuilder &_fbb, const AssetHeaderT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) {
@@ -277,14 +265,12 @@ inline ::flatbuffers::Offset<AssetHeader> AssetHeader::Pack(::flatbuffers::FlatB
auto _name = _fbb.CreateString(_o->name);
auto _type_id = ::flatbuffers::Pack(_o->type_id);
auto _type_name = _fbb.CreateString(_o->type_name);
auto _version = _o->version;
return Flat::Bigfoot::CreateAssetHeader(
_fbb,
&_uuid,
_name,
&_type_id,
_type_name,
_version);
_type_name);
}
inline AssetT::AssetT(const AssetT &o)
@@ -332,15 +318,14 @@ inline const ::flatbuffers::TypeTable *AssetHeaderTypeTable() {
{ ::flatbuffers::ET_SEQUENCE, 0, 0 },
{ ::flatbuffers::ET_STRING, 0, -1 },
{ ::flatbuffers::ET_SEQUENCE, 0, 1 },
{ ::flatbuffers::ET_STRING, 0, -1 },
{ ::flatbuffers::ET_UINT, 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, 5, type_codes, type_refs, nullptr, nullptr, nullptr
::flatbuffers::ST_TABLE, 4, type_codes, type_refs, nullptr, nullptr, nullptr
};
return &tt;
}
@@ -94,7 +94,9 @@ class ReferenceBase
return m_uuid == p_reference.m_uuid;
}
protected:
private:
friend DERIVED;
ReferenceBase() = default;
ReferenceBase(const UUID& p_uuid):
@@ -116,7 +118,6 @@ class ReferenceBase
{
}
private:
/**
* Get the associated handle container for the asset
* Register the reference inside the handle
@@ -152,7 +153,6 @@ class ReferenceBase
}
}
private:
/**
* The UUID
*/
@@ -9,12 +9,17 @@
#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
@@ -49,15 +54,22 @@ class BigFile: public DataAccessor
*/
void RollbackTransaction();
~BigFile();
/**
* 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 nullopt if the asset was not found
* \return The flatbuffer, or an empty vector if the asset was not found
*/
eastl::optional<eastl::vector<std::byte>> GetAssetFlatbuffer(const UUID& p_uuid) const override;
[[nodiscard]]
eastl::vector<std::byte> GetAssetFlatbuffer(const UUID& p_uuid) const override;
/**
* Get several Asset flatbuffers
@@ -65,40 +77,75 @@ class BigFile: public DataAccessor
* \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 an Asset metadata
* Get the HardReferences of a given asset
*
* \param p_uuid The asset to get the metadata of
* \return The metadata, or nullopt if the asset was not found
* \param p_uuid The asset to get the HardReferences of
* \return The HardReferences of the asset
*/
eastl::optional<AssetMetadata> GetAssetMetadata(const UUID& p_uuid) const override;
[[nodiscard]]
eastl::vector<UUID> GetAssetHardReferences(const UUID& p_uuid) const override;
/**
* Get several Asset metadatas
* Get the HardReferences of given assets
*
* \param p_uuids The assets to get the metadatas of
* \return The metadatas that were found
* \param p_uuids The assets to get the HardReferences of
* \return The HardReferences of the assets
*/
eastl::vector<AssetMetadata> GetAssetMetadatas(eastl::span<const UUID> p_uuids) const override;
[[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
*
* \p_metadata The metadata
* \p_flatbuffer The flatbuffer of the 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 AssetMetadata& p_metadata, const eastl::span<const std::byte> p_flatbuffer) override;
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
*
* \p_metadatas The metadatas
* \p_flatbuffers The flatbuffers of the assets, matching p_metadatas by index
* \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(eastl::span<const AssetMetadata> p_metadatas,
eastl::span<const eastl::vector<std::byte>> p_flatbuffers) override;
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
@@ -106,6 +153,7 @@ class BigFile: public DataAccessor
* \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;
@@ -280,6 +328,13 @@ class BigFile: public DataAccessor
*/
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
@@ -314,14 +369,14 @@ class BigFile: public DataAccessor
/*
* The statement
*/
sqlite3_stmt* m_statement;
sqlite3_stmt* m_statement = nullptr;
};
private:
/**
* The BigFile DB
*/
sqlite3* m_db;
sqlite3* m_db = nullptr;
};
#endif
} // namespace Bigfoot
@@ -11,25 +11,12 @@
#include <System/Time/Time.hpp>
#include <System/UUID/UUID.hpp>
#include <EASTL/optional.h>
#include <EASTL/span.h>
#include <EASTL/string.h>
#include <EASTL/vector.h>
namespace Bigfoot
{
struct AssetMetadata
{
UUID m_uuid = UUID::NULL_UUID;
eastl::string m_name {};
AssetTypeID m_typeID = 0;
eastl::string m_typeName {};
Time m_creationTime = 0;
Time m_modificationTime = 0;
};
class DataAccessor
{
public:
@@ -44,9 +31,10 @@ class DataAccessor
* Get an Asset flatbuffer
*
* \param p_uuid The asset to get the flatbuffer of
* \return The flatbuffer, or nullopt if the asset was not found
* \return The flatbuffer, or an empty vector if the asset was not found
*/
virtual eastl::optional<eastl::vector<std::byte>> GetAssetFlatbuffer(const UUID& p_uuid) const = 0;
[[nodiscard]]
virtual eastl::vector<std::byte> GetAssetFlatbuffer(const UUID& p_uuid) const = 0;
/**
* Get several Asset flatbuffers
@@ -54,41 +42,48 @@ class DataAccessor
* \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 an Asset metadata
* Get the HardReferences of a given asset
*
* \param p_uuid The asset to get the metadata of
* \return The metadata, or nullopt if the asset was not found
* \param p_uuid The asset to get the HardReferences of
* \return The HardReferences of the asset
*/
virtual eastl::optional<AssetMetadata> GetAssetMetadata(const UUID& p_uuid) const = 0;
[[nodiscard]]
virtual eastl::vector<UUID> GetAssetHardReferences(const UUID& p_uuid) const = 0;
/**
* Get several Asset metadatas
* Get the HardReferences of given assets
*
* \param p_uuids The assets to get the metadatas of
* \return The metadatas that were found
* \param p_uuids The assets to get the HardReferences of
* \return The HardReferences of the assets
*/
virtual eastl::vector<AssetMetadata> GetAssetMetadatas(eastl::span<const UUID> p_uuids) const = 0;
[[nodiscard]]
virtual eastl::vector<eastl::vector<UUID>> GetAssetsHardReferences(eastl::span<const UUID> p_uuids) const = 0;
/**
* Save an asset
*
* \p_metadata The metadata
* \p_flatbuffer The flatbuffer of the 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 AssetMetadata& p_metadata,
const eastl::span<const std::byte> p_flatbuffer) = 0;
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
*
* \p_metadatas The metadatas
* \p_flatbuffers The flatbuffers of the assets, matching p_metadatas by index
* \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(eastl::span<const AssetMetadata> p_metadatas,
eastl::span<const eastl::vector<std::byte>> p_flatbuffers) = 0;
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
@@ -96,6 +91,7 @@ class DataAccessor
* \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;
@@ -10,7 +10,7 @@
#include <Utils/Assert/AssertHandler.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
namespace Bigfoot
{
@@ -15,7 +15,7 @@ namespace Bigfoot
/*
* Logger
*/
inline Log::LoggerInfo ENGINE_LOGGER {"ENGINE_LOGGER", Flat::LogLevel::Trace};
inline Log::LoggerInfo ENGINE_LOGGER { .m_name = "ENGINE_LOGGER", .m_level = Flat::LogLevel::Trace};
} // namespace Bigfoot
#endif
#endif
@@ -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,249 @@
/*********************************************************************
* \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 {reinterpret_cast<const 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
+4 -5
View File
@@ -8,7 +8,6 @@
#include <System/SystemAssertHandler.hpp>
#include <bit>
#include <fstream>
#include <string>
@@ -83,7 +82,7 @@ eastl::vector<std::byte> File::Read(const std::size_t p_offset, const std::size_
eastl::vector<std::byte> data;
data.resize(p_size);
file.read(std::bit_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
file.read(reinterpret_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
return data;
}
@@ -100,7 +99,7 @@ void File::Write(const eastl::span<const std::byte> p_data, const std::size_t p_
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()));
file.write(reinterpret_cast<const char*>(p_data.data()), static_cast<std::streamsize>(p_data.size()));
}
/****************************************************************************************/
@@ -112,8 +111,8 @@ void File::Delete() const
/****************************************************************************************/
File::File(const std::filesystem::path& p_path):
m_path(p_path),
File::File(std::filesystem::path p_path):
m_path(std::move(p_path)),
m_pathString(m_path.string().c_str())
{
}
@@ -10,7 +10,7 @@
#include <string>
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
#include <Windows.h>
#elif defined BIGFOOT_LINUX
#include <fcntl.h>
@@ -26,7 +26,7 @@ MemoryMappedFile::MemoryMappedFile(const File& p_file)
const File absoluteFile = p_file.ToAbsolute();
const eastl::string_view path = absoluteFile.GetPath();
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
m_fileHandle = CreateFileA(path.data(),
GENERIC_READ,
FILE_SHARE_READ,
@@ -78,7 +78,7 @@ MemoryMappedFile::MemoryMappedFile(const File& p_file)
MemoryMappedFile::~MemoryMappedFile()
{
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
if (m_data != nullptr)
{
[[maybe_unused]]
@@ -133,7 +133,7 @@ WritableMemoryMappedFile::WritableMemoryMappedFile(const File& p_file, const std
const File absoluteFile = p_file.ToAbsolute();
const eastl::string_view path = absoluteFile.GetPath();
#if defined BIGFOOT_WINDOWS
#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);
@@ -169,7 +169,7 @@ WritableMemoryMappedFile::WritableMemoryMappedFile(const File& p_file, const std
WritableMemoryMappedFile::~WritableMemoryMappedFile()
{
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
if (m_data != nullptr)
{
[[maybe_unused]]
@@ -220,7 +220,7 @@ eastl::span<std::byte> WritableMemoryMappedFile::GetData() const
void WritableMemoryMappedFile::Prefetch(const std::size_t p_offset, const std::size_t p_size) const
{
#if defined BIGFOOT_WINDOWS
#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);
@@ -112,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
@@ -47,7 +47,7 @@ class MemoryMappedFile
MemoryMappedFile& operator=(MemoryMappedFile&& p_file) = delete;
private:
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
/**
* Handle to the file
*/
@@ -118,7 +118,7 @@ class WritableMemoryMappedFile
WritableMemoryMappedFile& operator=(WritableMemoryMappedFile&& p_file) = delete;
private:
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
/**
* Handle to the file
*/
@@ -10,7 +10,7 @@
#include <Utils/Assert/AssertHandler.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
namespace Bigfoot
{
@@ -15,7 +15,7 @@ namespace Bigfoot
/*
* Logger
*/
inline Log::LoggerInfo SYSTEM_LOGGER {"SYSTEM_LOGGER", Flat::LogLevel::Trace};
inline Log::LoggerInfo SYSTEM_LOGGER { .m_name = "SYSTEM_LOGGER", .m_level = Flat::LogLevel::Trace};
} // namespace Bigfoot
#endif
#endif
@@ -65,13 +65,10 @@ class Time
/****************************************************************************************/
namespace Flat
{
namespace Bigfoot
namespace Flat::Bigfoot
{
struct Time;
}
} // namespace Flat
} // namespace Flat::Bigfoot
/****************************************************************************************/
@@ -6,6 +6,7 @@
*********************************************************************/
#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>
@@ -52,7 +53,7 @@ class UUID
* \return A string representing the UUID
*/
[[nodiscard]]
std::string ToString() const;
eastl::string ToString() const;
operator bool() const;
@@ -120,33 +121,33 @@ struct std::hash<Bigfoot::UUID>
template<>
struct fmt::formatter<Bigfoot::UUID>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const Bigfoot::UUID& p_uuid, format_context& ctx) const
static auto format(const Bigfoot::UUID& p_uuid, format_context& ctx)
{
return fmt::format_to(ctx.out(), "{}", p_uuid.ToString());
return fmt::format_to(ctx.out(), "{}", p_uuid.ToString().c_str());
}
};
/****************************************************************************************/
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <quill/DeferredFormatCodec.h>
template<>
struct fmtquill::formatter<Bigfoot::UUID>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const Bigfoot::UUID& p_uuid, format_context& ctx) const
static auto format(const Bigfoot::UUID& p_uuid, format_context& ctx)
{
return fmtquill::format_to(ctx.out(), "{}", p_uuid.ToString());
return fmtquill::format_to(ctx.out(), "{}", p_uuid.ToString().c_str());
}
};
@@ -158,13 +159,10 @@ struct quill::Codec<Bigfoot::UUID>: quill::DeferredFormatCodec<Bigfoot::UUID>
/****************************************************************************************/
namespace Flat
{
namespace Bigfoot
namespace Flat::Bigfoot
{
struct UUID;
}
} // namespace Flat
} // namespace Flat::Bigfoot
/****************************************************************************************/
+2 -1
View File
@@ -57,7 +57,8 @@ Time::Time(const std::uint64_t p_epoch):
std::uint32_t Time::GetYear() const
{
return m_timeInfo.tm_year + 1900;
constexpr std::uint32_t TM_YEAR_EPOCH = 1900;
return m_timeInfo.tm_year + TM_YEAR_EPOCH;
}
/****************************************************************************************/
+4 -3
View File
@@ -35,7 +35,7 @@ UUID::UUID(const std::string_view p_string):
UUID::UUID(const std::span<const std::byte, UUID_BYTE_SIZE> p_raw)
{
const std::span<std::uint8_t, UUID_BYTE_SIZE> raw {
const_cast<std::uint8_t*>(std::bit_cast<const std::uint8_t*>(p_raw.data())),
const_cast<std::uint8_t*>(reinterpret_cast<const std::uint8_t*>(p_raw.data())),
UUID_BYTE_SIZE};
m_uuid = uuids::uuid {raw};
@@ -50,9 +50,10 @@ std::span<const std::byte, UUID::UUID_BYTE_SIZE> UUID::ToBytes() const
/****************************************************************************************/
std::string UUID::ToString() 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()};
}
/****************************************************************************************/
+2
View File
@@ -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
@@ -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
@@ -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 { .m_name = "SASQUATCHBAKER_LOGGER", .m_level = 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
@@ -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
@@ -7,26 +7,23 @@
#ifndef BIGFOOT_UTILS_ASSERT_HPP
#define BIGFOOT_UTILS_ASSERT_HPP
#include <Utils/Log/LogMacros.hpp>
#include <Utils/TargetMacros.h>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <Utils/Format.hpp>
#include <EASTL/fixed_function.h>
#include <EASTL/string.h>
#include <cstdlib>
#include <source_location>
#include <string>
#if defined BIGFOOT_LINUX
#ifdef BIGFOOT_LINUX
#include <csignal>
#endif
#define BIGFOOT_FLUSH_LOG \
do \
{ \
if (Bigfoot::Singleton<Bigfoot::Log>::HasInstance()) \
{ \
Bigfoot::Singleton<Bigfoot::Log>::GetInstance().Flush(); \
} \
} while (false)
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
#define BREAK \
do \
{ \
@@ -42,39 +39,68 @@
} 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, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
HANDLER::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
BREAK; \
} \
} while (false)
::Bigfoot::AssertCheck<HANDLER>((p_assert), \
[&] \
{ \
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
})
#define SOFT_ASSERT(HANDLER, p_assert, p_message, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
HANDLER::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
BREAK; \
} \
} while (false)
::Bigfoot::AssertCheck<HANDLER>((p_assert), \
[&] \
{ \
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
})
#define CRITICAL_ASSERT(HANDLER, p_assert, p_message, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
HANDLER::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
BREAK; \
std::abort(); \
} \
} while (false)
::Bigfoot::CriticalAssertCheck<HANDLER>((p_assert), \
[&] \
{ \
return ::Bigfoot::Format(p_message __VA_OPT__(, ) __VA_ARGS__); \
})
#else
#define ASSERT(HANDLER, p_assert, p_message, ...)
@@ -9,9 +9,9 @@
#include <Utils/Assert/Assert.hpp>
#include <Utils/Log/Log.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <fmt/format.h>
#include <EASTL/string.h>
#include <source_location>
@@ -32,15 +32,13 @@ class AssertHandler
* Handle an assertion.
*
* \param p_location Location of the assertion.
* \param p_format Format string for the assertion message.
* \param p_args Arguments for the format string.
* \param p_message Message describing the assertion failure.
*/
template<typename... ARGS>
static void Handle(const std::source_location& p_location, fmt::format_string<ARGS...> p_format, ARGS&&... p_args)
static void Handle(const std::source_location& p_location, const eastl::string& p_message)
{
BIGFOOT_LOG_FATAL(LOGGER,
"Assert: {} (File:{}, Line:{}, Function:{}\n",
fmt::format(p_format, std::forward<ARGS>(p_args)...),
p_message,
p_location.file_name(),
p_location.line(),
p_location.function_name());
+9 -13
View File
@@ -6,37 +6,33 @@
*********************************************************************/
#ifndef BIGFOOT_UTILS_CASTER_HPP
#define BIGFOOT_UTILS_CASTER_HPP
#include <EASTL/type_traits.h>
#include <type_traits>
namespace Bigfoot
{
template<class TO,
class FROM,
eastl::enable_if_t<eastl::is_base_of_v<FROM, TO> || eastl::is_base_of_v<TO, FROM>, bool> = true>
template<class TO, class FROM>
requires(std::is_base_of_v<FROM, TO> || std::is_base_of_v<TO, FROM>)
constexpr TO* Cast(FROM* p_object)
{
return static_cast<TO*>(p_object);
}
template<class TO,
class FROM,
eastl::enable_if_t<eastl::is_base_of_v<FROM, TO> || eastl::is_base_of_v<TO, FROM>, bool> = true>
template<class TO, class FROM>
requires(std::is_base_of_v<FROM, TO> || std::is_base_of_v<TO, FROM>)
constexpr const TO* Cast(const FROM* p_object)
{
return static_cast<const TO*>(p_object);
}
template<class TO,
class FROM,
eastl::enable_if_t<eastl::is_base_of_v<FROM, TO> || eastl::is_base_of_v<TO, FROM>, bool> = true>
template<class TO, class FROM>
requires(std::is_base_of_v<FROM, TO> || std::is_base_of_v<TO, FROM>)
constexpr TO& Cast(FROM& p_object)
{
return static_cast<TO&>(p_object);
}
template<class TO,
class FROM,
eastl::enable_if_t<eastl::is_base_of_v<FROM, TO> || eastl::is_base_of_v<TO, FROM>, bool> = true>
template<class TO, class FROM>
requires(std::is_base_of_v<FROM, TO> || std::is_base_of_v<TO, FROM>)
constexpr const TO& Cast(const FROM& p_object)
{
return static_cast<const TO&>(p_object);
@@ -28,7 +28,7 @@ class SlotMapKey
private:
static constexpr std::uint32_t VERSION_BIT_COUNT = sizeof(VersionType) * std::numeric_limits<unsigned char>::digits;
static constexpr std::uint32_t INDEX_BIT_COUNT = sizeof(IndexType) * std::numeric_limits<unsigned char>::digits;
static_assert(VERSION_BIT_COUNT + INDEX_BIT_COUNT <= 64,
static_assert(VERSION_BIT_COUNT + INDEX_BIT_COUNT <= std::numeric_limits<std::uint64_t>::digits,
"We cant construct a 64 bit key from the given Version and Index types!");
static constexpr std::uint64_t INDEX_MASK = (static_cast<std::uint64_t>(1) << INDEX_BIT_COUNT) - 1;
@@ -115,10 +115,10 @@ class DenseSlotMap
public:
using SlotMapKey = SLOT_MAP_KEY_TYPE;
using iterator = typename eastl::vector<T>::iterator;
using const_iterator = typename eastl::vector<T>::const_iterator;
using reverse_iterator = typename eastl::vector<T>::reverse_iterator;
using const_reverse_iterator = typename eastl::vector<T>::const_reverse_iterator;
using iterator = eastl::vector<T>::iterator;
using const_iterator = eastl::vector<T>::const_iterator;
using reverse_iterator = eastl::vector<T>::reverse_iterator;
using const_reverse_iterator = eastl::vector<T>::const_reverse_iterator;
/**
* Check if the map has the key
@@ -402,7 +402,6 @@ class DenseSlotMap
m_freeListHead = p_key.GetIndex();
}
private:
/**
* The actual storage for our data
*/
@@ -431,12 +430,13 @@ class DenseSlotMap
template<class T, class SLOT_MAP_KEY_TYPE = SlotMapKey<>>
class StableSlotMap
{
private:
public:
static constexpr std::size_t PAGE_SIZE = 64;
private:
struct AlignedSlot
{
alignas(T) std::byte m_bytes[sizeof(T)];
alignas(T) eastl::array<std::byte, sizeof(T)> m_bytes;
};
struct Page
@@ -467,7 +467,8 @@ class StableSlotMap
T* Get(const std::size_t p_offset)
{
ASSERT(UtilsAssertHandler, m_occupied[p_offset], "Requesting a dead data!");
return std::launder(reinterpret_cast<T*>(m_data[p_offset].m_bytes));
void* const bytes = m_data[p_offset].m_bytes.data();
return std::launder(static_cast<T*>(bytes));
}
/**
@@ -479,7 +480,8 @@ class StableSlotMap
const T* Get(const std::size_t p_offset) const
{
ASSERT(UtilsAssertHandler, m_occupied[p_offset], "Requesting a dead data!");
return std::launder(reinterpret_cast<const T*>(m_data[p_offset].m_bytes));
const void* const bytes = m_data[p_offset].m_bytes.data();
return std::launder(static_cast<const T*>(bytes));
}
/**
@@ -527,7 +529,8 @@ class StableSlotMap
IteratorImpl() = default;
// Allows an iterator to be implicitly used as a const_iterator.
template<class OTHER_VALUE_TYPE, class = std::enable_if_t<std::is_same_v<VALUE_TYPE, const OTHER_VALUE_TYPE>>>
template<class OTHER_VALUE_TYPE>
requires std::is_same_v<VALUE_TYPE, const OTHER_VALUE_TYPE>
IteratorImpl(const IteratorImpl<OTHER_VALUE_TYPE>& p_other):
m_map(p_other.m_map),
m_pageIt(p_other.m_pageIt),
@@ -777,7 +780,7 @@ class StableSlotMap
Page* const page = GetPageAt(pageIndex);
const std::size_t offsetInPage = GetDataOffset(key);
new (page->m_data[offsetInPage].m_bytes) T(std::forward<ARGS>(p_args)...);
new (page->m_data[offsetInPage].m_bytes.data()) T(std::forward<ARGS>(p_args)...);
page->m_occupied.set(offsetInPage);
return key;
@@ -835,7 +838,7 @@ class StableSlotMap
{
std::destroy_at(page->Get(offset));
const std::size_t keyIndex = pageIndex * PAGE_SIZE + offset;
const std::size_t keyIndex = (pageIndex * PAGE_SIZE) + offset;
RecycleKey(SlotMapKey {m_keys[keyIndex].GetVersion(), static_cast<SlotMapKey::IndexType>(keyIndex)});
}
page->m_occupied.reset();
@@ -870,9 +873,9 @@ class StableSlotMap
[[nodiscard]]
iterator begin()
{
iterator it {this};
it.SeekFirstFrom(m_pages.begin());
return it;
iterator result {this};
result.SeekFirstFrom(m_pages.begin());
return result;
}
[[nodiscard]]
@@ -884,9 +887,9 @@ class StableSlotMap
[[nodiscard]]
const_iterator begin() const
{
const_iterator it {this};
it.SeekFirstFrom(m_pages.begin());
return it;
const_iterator result {this};
result.SeekFirstFrom(m_pages.begin());
return result;
}
[[nodiscard]]
@@ -1024,7 +1027,6 @@ class StableSlotMap
return m_pages[p_pageIndex].get();
}
private:
/**
* The pages
*/
@@ -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
@@ -8,40 +8,43 @@
#define BIGFOOT_UTILS_LOG_EASTLFORMATTERS_HPP
#include <Utils/TargetMacros.h>
#if defined(BIGFOOT_NOT_OPTIMIZED)
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <quill/core/Codec.h>
#endif
#include <EASTL/string.h>
#include <EASTL/string_view.h>
#include <EASTL/vector.h>
#include <fmt/format.h>
#include <cstddef>
// STRING
template<>
struct fmt::formatter<eastl::string>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::string& p_string, format_context& ctx) const
static auto format(const eastl::string& p_string, format_context& ctx)
{
return fmt::format_to(ctx.out(), "{}", p_string.c_str());
}
};
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
template<>
struct fmtquill::formatter<eastl::string>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::string& p_string, format_context& ctx) const
static auto format(const eastl::string& p_string, format_context& ctx)
{
return fmtquill::format_to(ctx.out(), "{}", p_string.c_str());
}
@@ -53,27 +56,27 @@ struct fmtquill::formatter<eastl::string>
template<>
struct fmt::formatter<eastl::string_view>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::string_view& p_stringView, format_context& ctx) const
static auto format(const eastl::string_view& p_stringView, format_context& ctx)
{
return fmt::format_to(ctx.out(), "{}", p_stringView.data());
}
};
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
template<>
struct fmtquill::formatter<eastl::string_view>
{
constexpr auto parse(format_parse_context& ctx)
static constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::string_view& p_stringView, format_context& ctx) const
static auto format(const eastl::string_view& p_stringView, format_context& ctx)
{
return fmtquill::format_to(ctx.out(), "{}", p_stringView.data());
}
@@ -112,4 +115,61 @@ struct quill::Codec<eastl::string_view>: Bigfoot::Codec::string_view<eastl::stri
{
};
#endif
// VECTOR
template<class T>
struct fmt::formatter<eastl::vector<T>>
{
constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::vector<T>& p_vector, format_context& ctx) const
{
auto out = fmt::format_to(ctx.out(), "[");
for (std::size_t i = 0; i < p_vector.size(); ++i)
{
if (i > 0)
{
out = fmt::format_to(out, ", ");
}
out = fmt::format_to(out, "{}", p_vector[i]);
}
return fmt::format_to(out, "]");
}
};
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <quill/DeferredFormatCodec.h>
template<class T>
struct fmtquill::formatter<eastl::vector<T>>
{
constexpr auto parse(format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const eastl::vector<T>& p_vector, format_context& ctx) const
{
auto out = fmtquill::format_to(ctx.out(), "[");
for (std::size_t i = 0; i < p_vector.size(); ++i)
{
if (i > 0)
{
out = fmtquill::format_to(out, ", ");
}
out = fmtquill::format_to(out, "{}", p_vector[i]);
}
return fmtquill::format_to(out, "]");
}
};
template<class T>
struct quill::Codec<eastl::vector<T>>: quill::DeferredFormatCodec<eastl::vector<T>>
{
};
#endif
#endif
@@ -2,7 +2,8 @@ namespace Bigfoot.Flat;
enum LogSinkType: byte
{
Console
Console,
File
}
enum LogLevel: byte
@@ -8,10 +8,11 @@
#define BIGFOOT_UTILS_LOG_LOG_HPP
#include <Utils/Log/EASTLFormatters.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#include <Utils/Log/Log_generated.hpp>
#include <EASTL/array.h>
#include <EASTL/string_view.h>
#ifdef BIGFOOT_WINDOWS
#pragma warning(disable: 4702)
@@ -21,7 +22,8 @@
#include <quill/LogMacros.h>
#include <quill/Logger.h>
#include <quill/sinks/ConsoleSink.h>
#if defined BIGFOOT_WINDOWS
#include <quill/sinks/FileSink.h>
#ifdef BIGFOOT_WINDOWS
#pragma warning(default: 4702)
#endif
@@ -36,6 +38,11 @@ class Log
Flat::LogLevel m_level;
};
/**
* The path of the file the File sink writes to
*/
static constexpr eastl::string_view FILE_SINK_PATH = "Bigfoot.log";
Log();
Log(const Log& p_logger) = delete;
@@ -95,7 +102,7 @@ class Log
/*
* The sinks
*/
eastl::array<std::shared_ptr<quill::Sink>, 1> m_sinks;
eastl::array<std::shared_ptr<quill::Sink>, 2> m_sinks;
};
} // namespace Bigfoot
#endif
@@ -9,7 +9,16 @@
#include <Utils/Log/Log.hpp>
#include <Utils/Singleton.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#define BIGFOOT_FLUSH_LOG \
do \
{ \
if (Bigfoot::Singleton<Bigfoot::Log>::HasInstance()) \
{ \
Bigfoot::Singleton<Bigfoot::Log>::GetInstance().Flush(); \
} \
} while (false)
#define BIGFOOT_LOG_DEBUG(loggerName, fmt, ...) \
do \
{ \
@@ -101,4 +110,67 @@
#define BIGFOOT_LOG_ERROR(loggerName, fmt, ...)
#define BIGFOOT_LOG_FATAL(loggerName, fmt, ...)
#endif
#ifdef BIGFOOT_NOT_OPTIMIZED
#define BIGFOOT_LOG_DEBUG_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_DEBUG(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#define BIGFOOT_LOG_TRACE_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_TRACE(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#define BIGFOOT_LOG_INFO_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_INFO(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#define BIGFOOT_LOG_WARN_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_WARN(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#define BIGFOOT_LOG_ERROR_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_ERROR(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#define BIGFOOT_LOG_FATAL_IF(condition, loggerName, fmt, ...) \
do \
{ \
if (condition) \
{ \
BIGFOOT_LOG_FATAL(loggerName, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
#else
#define BIGFOOT_LOG_DEBUG_IF(condition, loggerName, fmt, ...)
#define BIGFOOT_LOG_TRACE_IF(condition, loggerName, fmt, ...)
#define BIGFOOT_LOG_INFO_IF(condition, loggerName, fmt, ...)
#define BIGFOOT_LOG_WARN_IF(condition, loggerName, fmt, ...)
#define BIGFOOT_LOG_ERROR_IF(condition, loggerName, fmt, ...)
#define BIGFOOT_LOG_FATAL_IF(condition, loggerName, fmt, ...)
#endif
#endif
@@ -29,27 +29,30 @@ namespace Flat {
enum class LogSinkType : int8_t {
Console = 0,
File = 1,
MIN = Console,
MAX = Console
MAX = File
};
inline const LogSinkType (&EnumValuesLogSinkType())[1] {
inline const LogSinkType (&EnumValuesLogSinkType())[2] {
static const LogSinkType values[] = {
LogSinkType::Console
LogSinkType::Console,
LogSinkType::File
};
return values;
}
inline const char * const *EnumNamesLogSinkType() {
static const char * const names[2] = {
static const char * const names[3] = {
"Console",
"File",
nullptr
};
return names;
}
inline const char *EnumNameLogSinkType(LogSinkType e) {
if (::flatbuffers::IsOutRange(e, LogSinkType::Console, LogSinkType::Console)) return "";
if (::flatbuffers::IsOutRange(e, LogSinkType::Console, LogSinkType::File)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLogSinkType()[index];
}
@@ -98,13 +101,14 @@ inline const char *EnumNameLogLevel(LogLevel e) {
inline const ::flatbuffers::TypeTable *LogSinkTypeTypeTable() {
static const ::flatbuffers::TypeCode type_codes[] = {
{ ::flatbuffers::ET_CHAR, 0, 0 },
{ ::flatbuffers::ET_CHAR, 0, 0 }
};
static const ::flatbuffers::TypeFunction type_refs[] = {
Bigfoot::Flat::LogSinkTypeTypeTable
};
static const ::flatbuffers::TypeTable tt = {
::flatbuffers::ST_ENUM, 1, type_codes, type_refs, nullptr, nullptr, nullptr
::flatbuffers::ST_ENUM, 2, type_codes, type_refs, nullptr, nullptr, nullptr
};
return &tt;
}
@@ -15,7 +15,7 @@ namespace Bigfoot
/*
* Logger
*/
inline Log::LoggerInfo @LOGGER_NAME@ {"@LOGGER_NAME@", Flat::LogLevel::Trace};
inline Log::LoggerInfo @LOGGER_NAME@ { .m_name = "@LOGGER_NAME@", .m_level = Flat::LogLevel::Trace};
} // namespace Bigfoot
#endif
#endif
@@ -33,6 +33,7 @@ class TaggedType
return m_value;
}
[[nodiscard]]
constexpr TYPE Get() const
{
return m_value;
@@ -7,33 +7,39 @@
#ifndef BIGFOOT_UTILS_TARGETMACROS_H
#define BIGFOOT_UTILS_TARGETMACROS_H
#if defined BIGFOOT_WINDOWS
#ifdef BIGFOOT_WINDOWS
#define BIGFOOT_WINDOWS_OR_LINUX(p_windows, p_linux) p_windows
#endif
#if defined BIGFOOT_LINUX
#ifdef BIGFOOT_LINUX
#define BIGFOOT_WINDOWS_OR_LINUX(p_windows, p_linux) p_linux
#endif
#if defined BIGFOOT_OPTIMIZED
#ifdef BIGFOOT_WINDOWS
#define BIGFOOT_ALWAYS_INLINE [[msvc::forceinline]]
#elif defined BIGFOOT_LINUX
#define BIGFOOT_ALWAYS_INLINE [[gnu::always_inline]] inline
#endif
#ifdef BIGFOOT_OPTIMIZED
#define BIGFOOT_OPTIMIZED_ONLY(...) __VA_ARGS__
#define BIGFOOT_NOT_OPTIMIZED_ONLY(...)
#define BIGFOOT_OPTIMIZED_OR_NOT(p_optimized, p_notOptimized) p_optimized
#endif
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
#define BIGFOOT_OPTIMIZED_ONLY(...)
#define BIGFOOT_NOT_OPTIMIZED_ONLY(...) __VA_ARGS__
#define BIGFOOT_OPTIMIZED_OR_NOT(p_optimized, p_notOptimized) p_notOptimized
#endif
#if defined BIGFOOT_TOOLMODE
#ifdef BIGFOOT_TOOLMODE
#define BIGFOOT_TOOLMODE_ONLY(...) __VA_ARGS__
#define BIGFOOT_GAME_ONLY(...)
#define BIGFOOT_GAME_OR_TOOLMODE(p_game, p_toolmode) p_toolmode
#endif
#if defined BIGFOOT_GAME
#ifdef BIGFOOT_GAME
#define BIGFOOT_TOOLMODE_ONLY(...)
#define BIGFOOT_GAME_ONLY(...) __VA_ARGS__
#define BIGFOOT_GAME_OR_TOOLMODE(p_game, p_toolmode) p_game
@@ -9,7 +9,7 @@
#include <Utils/Assert/AssertHandler.hpp>
#include <Utils/UtilsLogger_generated.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
namespace Bigfoot
{
@@ -15,7 +15,7 @@ namespace Bigfoot
/*
* Logger
*/
inline Log::LoggerInfo UTILS_LOGGER {"UTILS_LOGGER", Flat::LogLevel::Trace};
inline Log::LoggerInfo UTILS_LOGGER { .m_name = "UTILS_LOGGER", .m_level = Flat::LogLevel::Trace};
} // namespace Bigfoot
#endif
#endif
@@ -7,17 +7,22 @@
#ifndef BIGFOOT_UTILS_VERSION_HPP
#define BIGFOOT_UTILS_VERSION_HPP
#include <Utils/Caster.hpp>
#include <Utils/Format.hpp>
#include <Utils/TargetMacros.h>
#include <EASTL/string.h>
#include <fmt/format.h>
#include <cstdint>
#include <string>
namespace Bigfoot
{
class Version
{
private:
static constexpr std::uint32_t MAJOR_SHIFT = 16;
static constexpr std::uint32_t MINOR_SHIFT = 8;
public:
Version() = default;
@@ -39,7 +44,7 @@ class Version
* \param p_patch Patch
*/
constexpr Version(const std::uint8_t p_major, const std::uint8_t p_minor, const std::uint8_t p_patch):
m_combined((p_major << 16) | (p_minor << 8) | p_patch)
m_combined((p_major << MAJOR_SHIFT) | (p_minor << MINOR_SHIFT) | p_patch)
{
}
@@ -99,7 +104,7 @@ class Version
* \return The string
*/
[[nodiscard]]
std::string ToString() const;
eastl::string ToString() const;
[[nodiscard]]
constexpr auto operator<=>(const Version& p_version) const = default;
@@ -114,7 +119,6 @@ class Version
/****************************************************************************************/
#if defined BIGFOOT_NOT_OPTIMIZED
template<>
struct fmt::formatter<Bigfoot::Version>
{
@@ -127,8 +131,7 @@ struct fmt::formatter<Bigfoot::Version>
template<typename FormatContext>
auto format(const Bigfoot::Version& p_version, FormatContext& p_context) const
{
return fmt::format_to(p_context.out(), "{}", p_version.ToString());
return fmt::format_to(p_context.out(), "{}", p_version.ToString().c_str());
}
};
#endif
#endif
+9 -2
View File
@@ -6,7 +6,7 @@
*********************************************************************/
#include <Utils/Log/Log.hpp>
#if defined BIGFOOT_NOT_OPTIMIZED
#ifdef BIGFOOT_NOT_OPTIMIZED
namespace Bigfoot
{
@@ -17,6 +17,12 @@ Log::Log()
m_sinks[static_cast<std::size_t>(Flat::LogSinkType::Console)] =
quill::Frontend::create_or_get_sink<quill::ConsoleSink>(
std::string {Flat::EnumNameLogSinkType(Flat::LogSinkType::Console)});
quill::FileSinkConfig fileSinkConfig;
fileSinkConfig.set_open_mode('w');
m_sinks[static_cast<std::size_t>(Flat::LogSinkType::File)] = quill::Frontend::create_or_get_sink<quill::FileSink>(
std::string {FILE_SINK_PATH.data()},
fileSinkConfig);
}
/****************************************************************************************/
@@ -25,7 +31,8 @@ quill::Logger* Log::RegisterLogger(const LoggerInfo& p_loggerInfo)
{
quill::Logger* logger = quill::Frontend::create_or_get_logger(
p_loggerInfo.m_name,
m_sinks[static_cast<std::size_t>(Flat::LogSinkType::Console)]);
{m_sinks[static_cast<std::size_t>(Flat::LogSinkType::Console)],
m_sinks[static_cast<std::size_t>(Flat::LogSinkType::File)]});
SetLoggerLevel(p_loggerInfo);
return logger;
+2
View File
@@ -4,6 +4,7 @@
* \author Romain BOULLARD
* \date October 2025
*********************************************************************/
// NOLINTBEGIN
#include <Utils/Profiler.hpp>
#if defined BIGFOOT_WINDOWS
@@ -96,3 +97,4 @@
#pragma clang diagnostic pop
#endif
#endif
// NOLINTEND
+2 -2
View File
@@ -8,8 +8,8 @@
namespace Bigfoot
{
std::string Version::ToString() const
eastl::string Version::ToString() const
{
return fmt::format("{}.{}.{}", GetMajor(), GetMinor(), GetPatch());
return Format("{}.{}.{}", GetMajor(), GetMinor(), GetPatch());
}
} // namespace Bigfoot
+5
View File
@@ -0,0 +1,5 @@
InheritParentConfig: true
Checks: >
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers
+4 -1
View File
@@ -2,4 +2,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Misc)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/System)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine)
if(${TOOLMODE})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Tools)
endif()
+101 -29
View File
@@ -10,6 +10,7 @@
#include <AssetsForTesting/Asset/AssetB.hpp>
#include <AssetsForTesting/Asset/AssetC.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace Bigfoot
@@ -17,43 +18,66 @@ namespace Bigfoot
class AssetFixture: public ::testing::Test
{
protected:
AssetA m_asset;
void SetUp() override
{
SetActiveAssetLibrary(&m_library);
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuidA = m_assetA.GetHeader().uuid;
const UUID uuidB = m_assetB.GetHeader().uuid;
m_assetA.GetAsset().ref_a = SoftReference<AssetA> {uuidA};
m_assetB.GetAsset().ref_a = HardReference<AssetA> {uuidA};
m_assetB.GetAsset().refs_a.push_back(SoftReference<AssetA> {uuidA});
m_assetC.GetAsset().inner_table->asset_a_ref = HardReference<AssetA> {uuidA};
m_assetC.GetAsset().inner_table->asset_a_refs.push_back(HardReference<AssetA> {uuidA});
m_assetC.GetAsset().inner_table->asset_a_refs.push_back(HardReference<AssetA> {uuidA});
m_assetC.GetAsset().asset_b_ref = HardReference<AssetB> {uuidB};
m_assetC.GetAsset().asset_b_refs.push_back(SoftReference<AssetB> {uuidB});
}
void TearDown() override
{
SetActiveAssetLibrary(&g_assetLibrary);
}
AssetLibrary m_library;
AssetContainer<AssetA> m_containerA;
AssetContainer<AssetB> m_containerB;
AssetA m_assetA;
AssetB m_assetB;
AssetC m_assetC;
};
/****************************************************************************************/
TEST_F(AssetFixture, Constructor_ShouldInitializeHeaderTypeIdAndTypeName)
{
EXPECT_EQ(m_asset.GetHeader().type_id, AssetA::GetTypeID());
EXPECT_EQ(m_asset.GetHeader().type_name, eastl::string {AssetA::GetTypeName()});
EXPECT_EQ(m_assetA.GetHeader().type_id, AssetA::GetTypeID());
EXPECT_EQ(m_assetA.GetHeader().type_name, eastl::string {AssetA::GetTypeName()});
}
/****************************************************************************************/
TEST_F(AssetFixture, SetName_ShouldSetTheName)
{
m_asset.SetName("MyAsset");
m_assetA.SetName("MyAsset");
EXPECT_STREQ(m_asset.GetHeader().name.c_str(), "MyAsset");
}
/****************************************************************************************/
TEST_F(AssetFixture, SetVersion_ShouldSetTheVersion)
{
m_asset.SetVersion(5);
EXPECT_EQ(m_asset.GetHeader().version, 5);
EXPECT_STREQ(m_assetA.GetHeader().name.c_str(), "MyAsset");
}
/****************************************************************************************/
TEST_F(AssetFixture, GetAsset_ShouldReturnAMutableReferenceToTheUnderlyingAsset)
{
m_asset.GetAsset().health = 10;
m_assetA.GetAsset().health = 10;
EXPECT_EQ(m_asset.GetAsset().health, 10);
EXPECT_EQ(std::as_const(m_asset).GetAsset().health, 10);
EXPECT_EQ(m_assetA.GetAsset().health, 10);
EXPECT_EQ(std::as_const(m_assetA).GetAsset().health, 10);
}
/****************************************************************************************/
@@ -83,20 +107,68 @@ TEST_F(AssetFixture, GetTypeName_ShouldReturnTheFlatbufferFullyQualifiedName)
TEST_F(AssetFixture, PackThenUnpack_ShouldRoundTripTheHeaderAndTheAsset)
{
m_asset.SetName("MyAsset");
m_asset.SetVersion(3);
m_asset.GetAsset().health = 42;
m_asset.GetAsset().mana = 7;
m_assetA.SetName("MyAsset");
m_assetA.GetAsset().health = 42;
m_assetA.GetAsset().mana = 7;
AssetA unpacked;
unpacked.Unpack(m_asset.Pack());
unpacked.Unpack(m_assetA.Pack());
EXPECT_EQ(unpacked.GetHeader().uuid, m_asset.GetHeader().uuid);
EXPECT_STREQ(unpacked.GetHeader().name.c_str(), m_asset.GetHeader().name.c_str());
EXPECT_EQ(unpacked.GetHeader().version, m_asset.GetHeader().version);
EXPECT_EQ(unpacked.GetHeader().type_id, m_asset.GetHeader().type_id);
EXPECT_STREQ(unpacked.GetHeader().type_name.c_str(), m_asset.GetHeader().type_name.c_str());
EXPECT_EQ(unpacked.GetAsset().health, m_asset.GetAsset().health);
EXPECT_EQ(unpacked.GetAsset().mana, m_asset.GetAsset().mana);
EXPECT_EQ(unpacked.GetHeader().uuid, m_assetA.GetHeader().uuid);
EXPECT_STREQ(unpacked.GetHeader().name.c_str(), m_assetA.GetHeader().name.c_str());
EXPECT_EQ(unpacked.GetHeader().type_id, m_assetA.GetHeader().type_id);
EXPECT_STREQ(unpacked.GetHeader().type_name.c_str(), m_assetA.GetHeader().type_name.c_str());
EXPECT_EQ(unpacked.GetAsset().health, m_assetA.GetAsset().health);
EXPECT_EQ(unpacked.GetAsset().mana, m_assetA.GetAsset().mana);
}
/****************************************************************************************/
TEST_F(AssetFixture, GetHardReferences_ShouldReturnTheHardReferencesOfTheAsset)
{
const eastl::vector<UUID> hardReferences = m_assetC.GetHardReferences(m_assetC.Pack());
EXPECT_THAT(hardReferences, ::testing::UnorderedElementsAre(m_assetA.GetHeader().uuid, m_assetB.GetHeader().uuid));
}
/****************************************************************************************/
TEST_F(AssetFixture, CreateAsset_ShouldReturnTheCreatedAsset)
{
EXPECT_NE(CreateAsset<AssetA>(), nullptr);
}
/****************************************************************************************/
TEST_F(AssetFixture, CreateAsset_ShouldCreateUniqueAssets)
{
const AssetA* first = CreateAsset<AssetA>();
const AssetA* second = CreateAsset<AssetA>();
EXPECT_NE(first->GetHeader().uuid, second->GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(AssetFixture, CreateAsset_ShouldReturnTheSameAssetAsItsContainerHandle)
{
const AssetA* asset = CreateAsset<AssetA>();
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(asset->GetHeader().uuid);
EXPECT_EQ(handle->GetAsset(), asset);
}
/****************************************************************************************/
TEST_F(AssetFixture, CreateAsset_ShouldInsertTheAssetIntoItsCorrespondingContainer)
{
const AssetA* assetA = CreateAsset<AssetA>();
const AssetB* assetB = CreateAsset<AssetB>();
EXPECT_TRUE(m_containerA.Has(assetA->GetHeader().uuid));
EXPECT_FALSE(m_containerB.Has(assetA->GetHeader().uuid));
EXPECT_TRUE(m_containerB.Has(assetB->GetHeader().uuid));
EXPECT_FALSE(m_containerA.Has(assetB->GetHeader().uuid));
}
} // namespace Bigfoot
+130 -43
View File
@@ -17,18 +17,14 @@ class ContainerHandleFixture: public ::testing::Test
protected:
void SetUp() override
{
const auto key = m_containerA.Insert();
m_uuid = m_containerA.Get(key)->GetHeader().uuid;
m_handle = m_containerA.AcquireContainerHandle(m_uuid);
m_handle = m_containerA.Insert();
m_uuid = m_handle->GetAsset()->GetHeader().uuid;
}
AssetContainer<AssetA> m_containerA;
UUID m_uuid;
ContainerHandle* m_handle;
ContainerHandle* m_handle = nullptr;
};
/****************************************************************************************/
@@ -88,6 +84,40 @@ TEST_F(ContainerHandleFixture, DecrementSoftRefCount_ShouldDecrementTheSoftRefCo
/****************************************************************************************/
TEST_F(ContainerHandleFixture, DecrementHardRefCount_ShouldQueueTheAssetForRemovalWhenItReachesZero)
{
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
EXPECT_TRUE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
TEST_F(ContainerHandleFixture, DecrementHardRefCount_ShouldNotQueueTheAssetForRemovalIfItDoesNotReachZero)
{
m_handle->IncrementHardRefCount();
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
EXPECT_FALSE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
TEST_F(ContainerHandleFixture, IncrementHardRefCount_ShouldCancelAPendingRemovalWhenGoingFromZero)
{
m_handle->IncrementHardRefCount();
m_handle->DecrementHardRefCount();
ASSERT_TRUE(m_containerA.IsMarkedForRemove(m_uuid));
m_handle->IncrementHardRefCount();
EXPECT_FALSE(m_containerA.IsMarkedForRemove(m_uuid));
}
/****************************************************************************************/
class AssetContainerFixture: public ::testing::Test
{
protected:
@@ -98,8 +128,8 @@ class AssetContainerFixture: public ::testing::Test
TEST_F(AssetContainerFixture, Has_ShouldReturnTrueIfTheContainerHasTheAsset)
{
const auto key = m_containerA.Insert();
EXPECT_TRUE(m_containerA.Has(m_containerA.Get(key)->GetHeader().uuid));
const ContainerHandle* handle = m_containerA.Insert();
EXPECT_TRUE(m_containerA.Has(handle->GetAsset()->GetHeader().uuid));
}
/****************************************************************************************/
@@ -113,11 +143,10 @@ TEST_F(AssetContainerFixture, Has_ShouldReturnFalseIfTheContainerDoesNotHaveTheA
TEST_F(AssetContainerFixture, AcquireHandle_ShouldReturnTheAssociatedContainerHandle)
{
const auto key = m_containerA.Insert();
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(m_containerA.Get(key)->GetHeader().uuid);
EXPECT_EQ(static_cast<const AssetA*>(handle->GetAsset())->GetHeader().uuid,
m_containerA.Get(key)->GetHeader().uuid);
EXPECT_EQ(m_containerA.AcquireContainerHandle(uuid), handle);
}
/****************************************************************************************/
@@ -130,53 +159,111 @@ TEST_F(AssetContainerFixture, AcquireHandle_ShouldCreateTheAssociatedContainerHa
/****************************************************************************************/
TEST_F(AssetContainerFixture, Insert_ShouldReturnValidAndUniqueSlotKeyOnInsert)
TEST_F(AssetContainerFixture, Insert_ShouldReturnAHandleWithTheInsertedAsset)
{
const SlotMapKey slotKey = m_containerA.Insert();
EXPECT_TRUE(slotKey.Valid());
EXPECT_NE(slotKey, m_containerA.Insert());
const ContainerHandle* handle = m_containerA.Insert();
EXPECT_NE(handle, nullptr);
EXPECT_NE(handle->GetAsset(), nullptr);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Insert_ShouldReturnTheSameSlotKeyInCaseOfDuplicatedUUID)
TEST_F(AssetContainerFixture, Insert_ShouldReuseTheExistingHandleWhenInsertingAnAssetWithAnAlreadyExistingUUID)
{
const SlotMapKey slotKey = m_containerA.Insert();
ContainerHandle* const firstHandle = m_containerA.Insert();
const AssetBase* const firstAsset = firstHandle->GetAsset();
const eastl::vector<std::byte> packed = firstAsset->Pack();
AssetA duplicate;
duplicate.Unpack(m_containerA.Get(slotKey)->Pack());
ContainerHandle* const secondHandle = m_containerA.Insert(packed);
EXPECT_EQ(slotKey, m_containerA.Insert(std::move(duplicate)));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Get_ShouldReturnTheAssetWithTheSlotKey)
{
const SlotMapKey slotKey = m_containerA.Insert();
EXPECT_NE(m_containerA.Get(slotKey), nullptr);
EXPECT_NE(std::as_const(m_containerA).Get(slotKey), nullptr);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Get_ShouldReturnNullptrIfSlotKeyNotInContainer)
{
EXPECT_EQ(m_containerA.Get(SlotMapKey<> {1, 0}), nullptr);
EXPECT_EQ(std::as_const(m_containerA).Get(SlotMapKey<> {1, 0}), nullptr);
EXPECT_EQ(secondHandle, firstHandle);
EXPECT_EQ(secondHandle->GetAsset(), firstAsset);
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, Remove_ShouldRemoveTheAsset)
{
const SlotMapKey slotKey = m_containerA.Insert();
const UUID uuid = m_containerA.Get(slotKey)->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.Remove(uuid);
EXPECT_EQ(m_containerA.Get(slotKey), nullptr);
EXPECT_EQ(handle->GetAsset(), nullptr);
EXPECT_FALSE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, IsMarkedForRemove_ShouldReturnTrueIfTheAssetIsMarkedForDelete)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
EXPECT_TRUE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, IsMarkedForRemove_ShouldReturnFalseIfTheAssetIsNotMarkedForDelete)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
EXPECT_FALSE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, QueueForRemoval_ShouldMarkTheAssetForRemoval)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
EXPECT_TRUE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, CancelRemoval_ShouldUnmarkAQueuedAsset)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
m_containerA.CancelRemoval(uuid);
EXPECT_FALSE(m_containerA.IsMarkedForRemove(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, ProcessPendingRemovals_ShouldRemoveQueuedAssets)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuid);
m_containerA.ProcessPendingRemovals();
EXPECT_EQ(handle->GetAsset(), nullptr);
EXPECT_FALSE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetContainerFixture, ProcessPendingRemovals_ShouldNotRemoveAssetsThatAreNotQueued)
{
const ContainerHandle* handle = m_containerA.Insert();
const UUID uuid = handle->GetAsset()->GetHeader().uuid;
m_containerA.ProcessPendingRemovals();
EXPECT_NE(handle->GetAsset(), nullptr);
EXPECT_TRUE(m_containerA.Has(uuid));
}
} // namespace Bigfoot
+167
View File
@@ -73,6 +73,173 @@ TEST_F(AssetLibraryFixture, GetAssetContainer_ShouldReturnNullptrIfTheContainerD
/****************************************************************************************/
TEST_F(AssetLibraryFixture, GetAssetContainer_ShouldReturnTheContainerThatHasTheAsset)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuid = m_containerB.Insert()->GetAsset()->GetHeader().uuid;
EXPECT_EQ(m_library.GetAssetContainer(uuid), &m_containerB);
EXPECT_EQ(std::as_const(m_library).GetAssetContainer(uuid), &m_containerB);
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, GetAssetContainer_ShouldReturnNullptrIfNoContainerHasTheAsset)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuid;
EXPECT_EQ(m_library.GetAssetContainer(uuid), nullptr);
EXPECT_EQ(std::as_const(m_library).GetAssetContainer(uuid), nullptr);
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, IsLoaded_ShouldReturnTrueIfTheAssetIsLoaded)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuid = m_containerB.Insert()->GetAsset()->GetHeader().uuid;
EXPECT_TRUE(m_library.IsLoaded(uuid));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, IsLoaded_ShouldReturnFalseIfTheAssetIsNotLoaded)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuid;
EXPECT_FALSE(m_library.IsLoaded(uuid));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, IsLoaded_ShouldReturnFalseIfTheAssetWasRemovedFromItsContainer)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuid = m_containerB.Insert()->GetAsset()->GetHeader().uuid;
ASSERT_TRUE(m_library.IsLoaded(uuid));
m_containerB.Remove(uuid);
EXPECT_FALSE(m_library.IsLoaded(uuid));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, ProcessPendingRemovals_ShouldProcessRemovalsOnEveryContainer)
{
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
const UUID uuidA = m_containerA.Insert()->GetAsset()->GetHeader().uuid;
const UUID uuidB = m_containerB.Insert()->GetAsset()->GetHeader().uuid;
m_containerA.QueueForRemoval(uuidA);
m_containerB.QueueForRemoval(uuidB);
m_library.ProcessPendingRemovals();
EXPECT_FALSE(m_containerA.Has(uuidA));
EXPECT_FALSE(m_containerB.Has(uuidB));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, ProcessPendingRemovals_ShouldNotRemoveAssetsThatAreNotQueued)
{
std::ignore = m_library.Insert(&m_containerA);
const UUID uuid = m_containerA.Insert()->GetAsset()->GetHeader().uuid;
m_library.ProcessPendingRemovals();
EXPECT_TRUE(m_containerA.Has(uuid));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, ProcessPendingRemovals_ShouldCascadeThroughHardReferencedAssetsOneLevelPerCall)
{
const ScopedActiveAssetLibrary activeAssetLibraryScoped {&m_library};
std::ignore = m_library.Insert(&m_containerA);
std::ignore = m_library.Insert(&m_containerB);
std::ignore = m_library.Insert(&m_containerC);
UUID leafUUID;
UUID middleUUID;
UUID rootUUID;
{
// leaf
AssetA* leaf = CreateAsset<AssetA>();
leafUUID = leaf->GetHeader().uuid;
// middle --> leaf
AssetB* middle = CreateAsset<AssetB>();
middle->GetAsset().ref_a = HardReference<AssetA> {leafUUID};
middleUUID = middle->GetHeader().uuid;
// root
// / \
// v v
// middle --> leaf
AssetC* root = CreateAsset<AssetC>();
root->GetAsset().asset_b_ref = HardReference<AssetB> {middleUUID};
root->GetAsset().inner_table->asset_a_ref = HardReference<AssetA> {leafUUID};
rootUUID = root->GetHeader().uuid;
const HardReference<AssetC> rootRef {rootUUID};
const HardReference<AssetB> middleRef {middleUUID};
const HardReference<AssetA> leafRef {leafUUID};
ASSERT_NE(rootRef.Get(), nullptr);
ASSERT_NE(middleRef.Get(), nullptr);
ASSERT_NE(leafRef.Get(), nullptr);
}
// Releasing the outer HardReferences only drops root's count to zero: middle and leaf are
// still kept alive by root's own internal HardReferences to them.
EXPECT_TRUE(m_containerC.IsMarkedForRemove(rootUUID));
EXPECT_FALSE(m_containerB.IsMarkedForRemove(middleUUID));
EXPECT_FALSE(m_containerA.IsMarkedForRemove(leafUUID));
EXPECT_TRUE(m_library.IsLoaded(rootUUID));
EXPECT_TRUE(m_library.IsLoaded(middleUUID));
EXPECT_TRUE(m_library.IsLoaded(leafUUID));
// 1st pass: removing root destroys its HardReference to middle, dropping middle's count to
// zero; leaf is still held alive by middle's own HardReference to it.
m_library.ProcessPendingRemovals();
EXPECT_FALSE(m_library.IsLoaded(rootUUID));
EXPECT_TRUE(m_containerB.IsMarkedForRemove(middleUUID));
EXPECT_TRUE(m_library.IsLoaded(middleUUID));
EXPECT_TRUE(m_library.IsLoaded(leafUUID));
// 2nd pass: removing middle destroys its HardReference to leaf, dropping leaf's count to zero.
m_library.ProcessPendingRemovals();
EXPECT_FALSE(m_library.IsLoaded(middleUUID));
EXPECT_TRUE(m_containerA.IsMarkedForRemove(leafUUID));
EXPECT_TRUE(m_library.IsLoaded(leafUUID));
// 3rd pass: nothing references leaf anymore, it finally gets removed.
m_library.ProcessPendingRemovals();
EXPECT_FALSE(m_library.IsLoaded(leafUUID));
}
/****************************************************************************************/
TEST_F(AssetLibraryFixture, SetActiveAssetLibrary_ShouldSetTheActiveAssetLibraryToTheGivenLibrary)
{
SetActiveAssetLibrary(&m_library);
@@ -0,0 +1,34 @@
/*********************************************************************
* \file AssetTypeID.cpp
*
* \author Romain BOULLARD
* \date August 2026
*********************************************************************/
#include <Engine/Asset/AssetTypeID.hpp>
#include <Engine/Asset/AssetTypeID_generated.hpp>
#include <gtest/gtest.h>
#include <limits>
namespace Bigfoot
{
TEST(AssetTypeID, Pack_ShouldGiveAFlatAssetTypeIDWithTheSameValue)
{
constexpr AssetTypeID assetTypeID = 42;
const ::Flat::Bigfoot::AssetTypeID flatAssetTypeID = flatbuffers::Pack(assetTypeID);
EXPECT_EQ(flatAssetTypeID.value(), assetTypeID);
}
/****************************************************************************************/
TEST(AssetTypeID, UnPack_ShouldGiveBackTheOriginalValue)
{
constexpr AssetTypeID assetTypeID = 42;
const ::Flat::Bigfoot::AssetTypeID flatAssetTypeID = flatbuffers::Pack(assetTypeID);
EXPECT_EQ(flatbuffers::UnPack(flatAssetTypeID), assetTypeID);
}
} // namespace Bigfoot
+152 -337
View File
@@ -1,26 +1,49 @@
/*********************************************************************
* \file AssetLibrary.cpp
* \file Reference.cpp
*
* \author Romain BOULLARD
* \date July 2026
* \date August 2026
*********************************************************************/
#include <Engine/Asset/Reference.hpp>
#include <Engine/Asset/AssetLibrary.hpp>
#include <AssetsForTesting/Asset/AssetA.hpp>
#include <AssetsForTesting/Asset/AssetB.hpp>
#include <AssetsForTesting/Asset/AssetC.hpp>
#include <gtest/gtest.h>
namespace Bigfoot
{
class HardReferenceFixture: public ::testing::Test
template<class REFERENCE>
struct RefCountTraits;
template<>
struct RefCountTraits<HardReference<AssetA>>
{
[[nodiscard]]
static std::uint32_t Get(const ContainerHandle* p_handle)
{
return p_handle->GetHardRefCount();
}
};
template<>
struct RefCountTraits<SoftReference<AssetA>>
{
[[nodiscard]]
static std::uint32_t Get(const ContainerHandle* p_handle)
{
return p_handle->GetSoftRefCount();
}
};
template<class REFERENCE>
class ReferenceFixture: public ::testing::Test
{
protected:
void SetUp() override
{
SetActiveAssetLibrary(&m_library);
std::ignore = m_library.Insert(&m_containerA);
}
@@ -29,452 +52,244 @@ class HardReferenceFixture: public ::testing::Test
SetActiveAssetLibrary(&g_assetLibrary);
}
AssetContainer<AssetA> m_containerA;
AssetLibrary m_library;
};
/****************************************************************************************/
TEST_F(HardReferenceFixture, GetUUID_ShouldReturnTheUUID)
{
UUID uuid;
HardReference<AssetA> hardRef {uuid};
EXPECT_EQ(hardRef.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, Get_ShouldReturnTheAsset)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
HardReference<AssetA> hardRef {uuid};
EXPECT_EQ(hardRef.Get(), m_containerA.Get(key));
EXPECT_EQ(std::as_const(hardRef).Get(), std::as_const(m_containerA).Get(key));
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, Get_ShouldReturnNullptrIfTheAssetDoesNotExist)
{
UUID uuid;
HardReference<AssetA> hardRef {uuid};
EXPECT_EQ(hardRef.Get(), nullptr);
EXPECT_EQ(std::as_const(hardRef).Get(), nullptr);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, Get_ShouldReturnNullptrAfterTheAssetIsRemovedFromTheContainer)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
HardReference<AssetA> hardRef {uuid};
m_containerA.Remove(uuid);
EXPECT_EQ(hardRef.Get(), nullptr);
EXPECT_EQ(std::as_const(hardRef).Get(), nullptr);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, ShouldTouchTheHardRefCountOfTheHandle)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
EXPECT_EQ(handle->GetHardRefCount(), 0);
[[nodiscard]]
static std::uint32_t GetRefCount(const ContainerHandle* p_handle)
{
HardReference<AssetA> hardRef1 {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 1);
{
HardReference<AssetA> hardRef2 {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 2);
}
EXPECT_EQ(handle->GetHardRefCount(), 1);
}
EXPECT_EQ(handle->GetHardRefCount(), 0);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, CopyConstructor_ShouldIncrementTheHardRefCountAndCopyTheState)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
HardReference<AssetA> hardRef1 {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 1);
HardReference<AssetA> hardRef2 {hardRef1};
EXPECT_EQ(handle->GetHardRefCount(), 2);
EXPECT_EQ(hardRef2.GetUUID(), uuid);
EXPECT_EQ(hardRef2.Get(), m_containerA.Get(key));
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, MoveConstructor_ShouldTransferTheStateWithoutChangingTheHardRefCount)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
HardReference<AssetA> hardRef1 {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 1);
HardReference<AssetA> hardRef2 {std::move(hardRef1)};
EXPECT_EQ(handle->GetHardRefCount(), 1);
EXPECT_EQ(hardRef2.GetUUID(), uuid);
EXPECT_EQ(hardRef2.Get(), m_containerA.Get(key));
EXPECT_EQ(hardRef1.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(hardRef1.Get(), nullptr);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, CopyAssignment_ShouldReleaseThePreviousHandleAndAcquireTheNewOne)
{
UUID uuidA;
UUID uuidB;
const ContainerHandle* handleA = m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = m_containerA.AcquireContainerHandle(uuidB);
HardReference<AssetA> hardRefA {uuidA};
HardReference<AssetA> hardRefB {uuidB};
EXPECT_EQ(handleA->GetHardRefCount(), 1);
EXPECT_EQ(handleB->GetHardRefCount(), 1);
hardRefB = hardRefA;
EXPECT_EQ(handleA->GetHardRefCount(), 2);
EXPECT_EQ(handleB->GetHardRefCount(), 0);
EXPECT_EQ(hardRefB.GetUUID(), uuidA);
EXPECT_EQ(hardRefB.Get(), hardRefA.Get());
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, CopyAssignment_SelfAssignment_ShouldNotChangeTheHardRefCount)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
HardReference<AssetA> hardRef {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 1);
HardReference<AssetA>* selfPtr = &hardRef;
hardRef = *selfPtr;
EXPECT_EQ(handle->GetHardRefCount(), 1);
EXPECT_EQ(hardRef.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, MoveAssignment_ShouldReleaseThePreviousHandleAndTransferTheNewOne)
{
UUID uuidA;
UUID uuidB;
const ContainerHandle* handleA = m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = m_containerA.AcquireContainerHandle(uuidB);
HardReference<AssetA> hardRefA {uuidA};
HardReference<AssetA> hardRefB {uuidB};
EXPECT_EQ(handleA->GetHardRefCount(), 1);
EXPECT_EQ(handleB->GetHardRefCount(), 1);
hardRefB = std::move(hardRefA);
EXPECT_EQ(handleA->GetHardRefCount(), 1);
EXPECT_EQ(handleB->GetHardRefCount(), 0);
EXPECT_EQ(hardRefB.GetUUID(), uuidA);
EXPECT_EQ(hardRefA.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(hardRefA.Get(), nullptr);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, MoveAssignment_SelfAssignment_ShouldNotChangeTheHardRefCount)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
HardReference<AssetA> hardRef {uuid};
EXPECT_EQ(handle->GetHardRefCount(), 1);
HardReference<AssetA>* selfPtr = &hardRef;
hardRef = std::move(*selfPtr);
EXPECT_EQ(handle->GetHardRefCount(), 1);
EXPECT_EQ(hardRef.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, EqualityOperator_ShouldReturnTrueForReferencesWithTheSameUUID)
{
UUID uuid;
HardReference<AssetA> hardRef1 {uuid};
HardReference<AssetA> hardRef2 {uuid};
EXPECT_EQ(hardRef1, hardRef2);
}
/****************************************************************************************/
TEST_F(HardReferenceFixture, EqualityOperator_ShouldReturnFalseForReferencesWithDifferentUUIDs)
{
UUID uuid1;
UUID uuid2;
HardReference<AssetA> hardRef1 {uuid1};
HardReference<AssetA> hardRef2 {uuid2};
EXPECT_NE(hardRef1, hardRef2);
}
/****************************************************************************************/
class SoftReferenceFixture: public ::testing::Test
{
protected:
void SetUp() override
{
SetActiveAssetLibrary(&m_library);
std::ignore = m_library.Insert(&m_containerA);
}
void TearDown() override
{
SetActiveAssetLibrary(&g_assetLibrary);
return RefCountTraits<REFERENCE>::Get(p_handle);
}
AssetContainer<AssetA> m_containerA;
AssetLibrary m_library;
};
class ReferenceTypeNameGenerator
{
public:
template<typename T>
static std::string GetName([[maybe_unused]] int p_index)
{
if constexpr (std::is_same_v<T, HardReference<AssetA>>)
{
return "HardReference";
}
if constexpr (std::is_same_v<T, SoftReference<AssetA>>)
{
return "SoftReference";
}
}
};
using ReferenceTypes = ::testing::Types<HardReference<AssetA>, SoftReference<AssetA>>;
TYPED_TEST_SUITE(ReferenceFixture, ReferenceTypes, ReferenceTypeNameGenerator);
/****************************************************************************************/
TEST_F(SoftReferenceFixture, GetUUID_ShouldReturnTheUUID)
TYPED_TEST(ReferenceFixture, GetUUID_ShouldReturnTheUUID)
{
UUID uuid;
SoftReference<AssetA> hardRef {uuid};
TypeParam ref {uuid};
EXPECT_EQ(hardRef.GetUUID(), uuid);
EXPECT_EQ(ref.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, Get_ShouldReturnTheAsset)
TYPED_TEST(ReferenceFixture, Get_ShouldReturnTheAsset)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const AssetA* inserted = CreateAsset<AssetA>();
const UUID uuid = inserted->GetHeader().uuid;
SoftReference<AssetA> hardRef {uuid};
TypeParam ref {uuid};
EXPECT_EQ(hardRef.Get(), m_containerA.Get(key));
EXPECT_EQ(std::as_const(hardRef).Get(), std::as_const(m_containerA).Get(key));
EXPECT_EQ(ref.Get(), inserted);
EXPECT_EQ(std::as_const(ref).Get(), inserted);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, Get_ShouldReturnNullptrIfTheAssetDoesNotExist)
TYPED_TEST(ReferenceFixture, Get_ShouldReturnNullptrIfTheAssetDoesNotExist)
{
UUID uuid;
SoftReference<AssetA> hardRef {uuid};
TypeParam ref {uuid};
EXPECT_EQ(hardRef.Get(), nullptr);
EXPECT_EQ(std::as_const(hardRef).Get(), nullptr);
EXPECT_EQ(ref.Get(), nullptr);
EXPECT_EQ(std::as_const(ref).Get(), nullptr);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, Get_ShouldReturnNullptrAfterTheAssetIsRemovedFromTheContainer)
TYPED_TEST(ReferenceFixture, Get_ShouldReturnNullptrAfterTheAssetIsRemovedFromTheContainer)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const AssetA* inserted = CreateAsset<AssetA>();
const UUID uuid = inserted->GetHeader().uuid;
SoftReference<AssetA> hardRef {uuid};
TypeParam ref {uuid};
m_containerA.Remove(uuid);
this->m_containerA.Remove(uuid);
EXPECT_EQ(hardRef.Get(), nullptr);
EXPECT_EQ(std::as_const(hardRef).Get(), nullptr);
EXPECT_EQ(ref.Get(), nullptr);
EXPECT_EQ(std::as_const(ref).Get(), nullptr);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, ShouldTouchTheSoftRefCountOfTheHandle)
TYPED_TEST(ReferenceFixture, ShouldTouchTheRefCountOfTheHandle)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
const ContainerHandle* handle = this->m_containerA.AcquireContainerHandle(uuid);
EXPECT_EQ(handle->GetSoftRefCount(), 0);
EXPECT_EQ(this->GetRefCount(handle), 0);
{
SoftReference<AssetA> softRef1 {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
TypeParam ref1 {uuid};
EXPECT_EQ(this->GetRefCount(handle), 1);
{
SoftReference<AssetA> softRef2 {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 2);
TypeParam ref2 {uuid};
EXPECT_EQ(this->GetRefCount(handle), 2);
}
EXPECT_EQ(handle->GetSoftRefCount(), 1);
EXPECT_EQ(this->GetRefCount(handle), 1);
}
EXPECT_EQ(handle->GetSoftRefCount(), 0);
EXPECT_EQ(this->GetRefCount(handle), 0);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, CopyConstructor_ShouldIncrementTheSoftRefCountAndCopyTheState)
TYPED_TEST(ReferenceFixture, CopyConstructor_ShouldIncrementTheRefCountAndCopyTheState)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
const AssetA* inserted = CreateAsset<AssetA>();
const UUID uuid = inserted->GetHeader().uuid;
const ContainerHandle* handle = this->m_containerA.AcquireContainerHandle(uuid);
SoftReference<AssetA> softRef1 {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
TypeParam ref1 {uuid};
EXPECT_EQ(this->GetRefCount(handle), 1);
SoftReference<AssetA> softRef2 {softRef1};
TypeParam ref2 {ref1};
EXPECT_EQ(handle->GetSoftRefCount(), 2);
EXPECT_EQ(softRef2.GetUUID(), uuid);
EXPECT_EQ(softRef2.Get(), m_containerA.Get(key));
EXPECT_EQ(this->GetRefCount(handle), 2);
EXPECT_EQ(ref2.GetUUID(), uuid);
EXPECT_EQ(ref2.Get(), inserted);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, MoveConstructor_ShouldTransferTheStateWithoutChangingTheSoftRefCount)
TYPED_TEST(ReferenceFixture, MoveConstructor_ShouldTransferTheStateWithoutChangingTheRefCount)
{
const auto key = m_containerA.Insert();
const UUID uuid = m_containerA.Get(key)->GetHeader().uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
const AssetA* inserted = CreateAsset<AssetA>();
const UUID uuid = inserted->GetHeader().uuid;
const ContainerHandle* handle = this->m_containerA.AcquireContainerHandle(uuid);
SoftReference<AssetA> softRef1 {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
TypeParam ref1 {uuid};
EXPECT_EQ(this->GetRefCount(handle), 1);
SoftReference<AssetA> softRef2 {std::move(softRef1)};
TypeParam ref2 {std::move(ref1)};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
EXPECT_EQ(softRef2.GetUUID(), uuid);
EXPECT_EQ(softRef2.Get(), m_containerA.Get(key));
EXPECT_EQ(softRef1.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(softRef1.Get(), nullptr);
EXPECT_EQ(this->GetRefCount(handle), 1);
EXPECT_EQ(ref2.GetUUID(), uuid);
EXPECT_EQ(ref2.Get(), inserted);
EXPECT_EQ(ref1.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(ref1.Get(), nullptr);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, CopyAssignment_ShouldReleaseThePreviousHandleAndAcquireTheNewOne)
TYPED_TEST(ReferenceFixture, CopyAssignment_ShouldReleaseThePreviousHandleAndAcquireTheNewOne)
{
UUID uuidA;
UUID uuidB;
const ContainerHandle* handleA = m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = m_containerA.AcquireContainerHandle(uuidB);
const ContainerHandle* handleA = this->m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = this->m_containerA.AcquireContainerHandle(uuidB);
SoftReference<AssetA> softRefA {uuidA};
SoftReference<AssetA> softRefB {uuidB};
EXPECT_EQ(handleA->GetSoftRefCount(), 1);
EXPECT_EQ(handleB->GetSoftRefCount(), 1);
TypeParam refA {uuidA};
TypeParam refB {uuidB};
EXPECT_EQ(this->GetRefCount(handleA), 1);
EXPECT_EQ(this->GetRefCount(handleB), 1);
softRefB = softRefA;
refB = refA;
EXPECT_EQ(handleA->GetSoftRefCount(), 2);
EXPECT_EQ(handleB->GetSoftRefCount(), 0);
EXPECT_EQ(softRefB.GetUUID(), uuidA);
EXPECT_EQ(softRefB.Get(), softRefA.Get());
EXPECT_EQ(this->GetRefCount(handleA), 2);
EXPECT_EQ(this->GetRefCount(handleB), 0);
EXPECT_EQ(refB.GetUUID(), uuidA);
EXPECT_EQ(refB.Get(), refA.Get());
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, CopyAssignment_SelfAssignment_ShouldNotChangeTheSoftRefCount)
TYPED_TEST(ReferenceFixture, CopyAssignment_SelfAssignment_ShouldNotChangeTheRefCount)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
const ContainerHandle* handle = this->m_containerA.AcquireContainerHandle(uuid);
SoftReference<AssetA> softRef {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
TypeParam ref {uuid};
EXPECT_EQ(this->GetRefCount(handle), 1);
SoftReference<AssetA>* selfPtr = &softRef;
softRef = *selfPtr;
TypeParam* selfPtr = &ref;
ref = *selfPtr;
EXPECT_EQ(handle->GetSoftRefCount(), 1);
EXPECT_EQ(softRef.GetUUID(), uuid);
EXPECT_EQ(this->GetRefCount(handle), 1);
EXPECT_EQ(ref.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, MoveAssignment_ShouldReleaseThePreviousHandleAndTransferTheNewOne)
TYPED_TEST(ReferenceFixture, MoveAssignment_ShouldReleaseThePreviousHandleAndTransferTheNewOne)
{
UUID uuidA;
UUID uuidB;
const ContainerHandle* handleA = m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = m_containerA.AcquireContainerHandle(uuidB);
const ContainerHandle* handleA = this->m_containerA.AcquireContainerHandle(uuidA);
const ContainerHandle* handleB = this->m_containerA.AcquireContainerHandle(uuidB);
SoftReference<AssetA> softRefA {uuidA};
SoftReference<AssetA> softRefB {uuidB};
EXPECT_EQ(handleA->GetSoftRefCount(), 1);
EXPECT_EQ(handleB->GetSoftRefCount(), 1);
TypeParam refA {uuidA};
TypeParam refB {uuidB};
EXPECT_EQ(this->GetRefCount(handleA), 1);
EXPECT_EQ(this->GetRefCount(handleB), 1);
softRefB = std::move(softRefA);
refB = std::move(refA);
EXPECT_EQ(handleA->GetSoftRefCount(), 1);
EXPECT_EQ(handleB->GetSoftRefCount(), 0);
EXPECT_EQ(softRefB.GetUUID(), uuidA);
EXPECT_EQ(softRefA.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(softRefA.Get(), nullptr);
EXPECT_EQ(this->GetRefCount(handleA), 1);
EXPECT_EQ(this->GetRefCount(handleB), 0);
EXPECT_EQ(refB.GetUUID(), uuidA);
EXPECT_EQ(refA.GetUUID(), UUID::NULL_UUID);
EXPECT_EQ(refA.Get(), nullptr);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, MoveAssignment_SelfAssignment_ShouldNotChangeTheSoftRefCount)
TYPED_TEST(ReferenceFixture, MoveAssignment_SelfAssignment_ShouldNotChangeTheRefCount)
{
UUID uuid;
const ContainerHandle* handle = m_containerA.AcquireContainerHandle(uuid);
const ContainerHandle* handle = this->m_containerA.AcquireContainerHandle(uuid);
SoftReference<AssetA> softRef {uuid};
EXPECT_EQ(handle->GetSoftRefCount(), 1);
TypeParam ref {uuid};
EXPECT_EQ(this->GetRefCount(handle), 1);
SoftReference<AssetA>* selfPtr = &softRef;
softRef = std::move(*selfPtr);
TypeParam* selfPtr = &ref;
ref = std::move(*selfPtr);
EXPECT_EQ(handle->GetSoftRefCount(), 1);
EXPECT_EQ(softRef.GetUUID(), uuid);
EXPECT_EQ(this->GetRefCount(handle), 1);
EXPECT_EQ(ref.GetUUID(), uuid);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, EqualityOperator_ShouldReturnTrueForReferencesWithTheSameUUID)
TYPED_TEST(ReferenceFixture, EqualityOperator_ShouldReturnTrueForReferencesWithTheSameUUID)
{
UUID uuid;
SoftReference<AssetA> softRef1 {uuid};
SoftReference<AssetA> softRef2 {uuid};
TypeParam ref1 {uuid};
TypeParam ref2 {uuid};
EXPECT_EQ(softRef1, softRef2);
EXPECT_EQ(ref1, ref2);
}
/****************************************************************************************/
TEST_F(SoftReferenceFixture, EqualityOperator_ShouldReturnFalseForReferencesWithDifferentUUIDs)
TYPED_TEST(ReferenceFixture, EqualityOperator_ShouldReturnFalseForReferencesWithDifferentUUIDs)
{
UUID uuid1;
UUID uuid2;
SoftReference<AssetA> softRef1 {uuid1};
SoftReference<AssetA> softRef2 {uuid2};
TypeParam ref1 {uuid1};
TypeParam ref2 {uuid2};
EXPECT_NE(softRef1, softRef2);
EXPECT_NE(ref1, ref2);
}
} // namespace Bigfoot
+404 -210
View File
@@ -11,7 +11,11 @@
#include <EngineTests/BigFileInfo_generated.hpp>
#include <AssetsForTesting/Asset/AssetA.hpp>
#include <AssetsForTesting/Asset/AssetB.hpp>
#include <AssetsForTesting/Asset/AssetC.hpp>
#include <EASTL/array.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <chrono>
@@ -25,8 +29,49 @@ class BigFileFixture: public ::testing::Test
protected:
void SetUp() override
{
BigFile::Request {m_bigFile, "DELETE FROM Asset;"}.Execute();
BigFile::Request {m_bigFile, "DELETE FROM AssetMetadata;"}.Execute();
m_bigFile.Clear();
}
/*
* Packs an asset and saves it to the BigFile
*
* \param p_asset The asset to save
* \return The UUID the asset was saved under
*/
template<class ASSET>
UUID SaveAsset(ASSET& p_asset)
{
const eastl::vector<std::byte> flatbuffer = p_asset.Pack();
m_bigFile.SaveAssetFlatbuffer(p_asset.GetHeader().uuid, flatbuffer, p_asset.GetHardReferences(flatbuffer));
return p_asset.GetHeader().uuid;
}
/*
* Packs and saves several assets to the BigFile in a single SaveAssetFlatbuffers
*
* \param p_assets The assets to save
*/
void SaveAssets(const eastl::vector<AssetBase*>& p_assets)
{
eastl::vector<UUID> uuids;
eastl::vector<eastl::vector<std::byte>> flatbuffers;
eastl::vector<eastl::vector<UUID>> hardReferences;
uuids.reserve(p_assets.size());
flatbuffers.reserve(p_assets.size());
hardReferences.reserve(p_assets.size());
for (AssetBase* const asset: p_assets)
{
eastl::vector<std::byte> flatbuffer = asset->Pack();
hardReferences.push_back(asset->GetHardReferences(flatbuffer));
uuids.push_back(asset->GetHeader().uuid);
flatbuffers.push_back(std::move(flatbuffer));
}
m_bigFile.SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences);
}
BigFile m_bigFile {File {BIGFILE_ENGINETESTS_LOCATION}};
@@ -34,11 +79,39 @@ class BigFileFixture: public ::testing::Test
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetFlatbuffer_ShouldReturnNulloptForNonExistentUUID)
TEST_F(BigFileFixture, Clear_ShouldRemoveAllAssetsMetadataAndHardReferences)
{
AssetA referencedAsset;
referencedAsset.SetName("referenced");
SaveAsset(referencedAsset);
AssetB asset;
asset.SetName("hello");
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAsset.GetHeader().uuid};
SaveAsset(asset);
ASSERT_TRUE(m_bigFile.Has(referencedAsset.GetHeader().uuid));
ASSERT_TRUE(m_bigFile.Has(asset.GetHeader().uuid));
ASSERT_FALSE(m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid).empty());
m_bigFile.Clear();
EXPECT_FALSE(m_bigFile.Has(referencedAsset.GetHeader().uuid));
EXPECT_FALSE(m_bigFile.Has(asset.GetHeader().uuid));
EXPECT_TRUE(m_bigFile.GetAssetFlatbuffer(referencedAsset.GetHeader().uuid).empty());
EXPECT_TRUE(m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid).empty());
EXPECT_TRUE(m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid).empty());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetFlatbuffer_ShouldReturnEmptyForNonExistentUUID)
{
const UUID uuid;
EXPECT_FALSE(m_bigFile.GetAssetFlatbuffer(uuid).has_value());
EXPECT_TRUE(m_bigFile.GetAssetFlatbuffer(uuid).empty());
}
/****************************************************************************************/
@@ -49,294 +122,422 @@ TEST_F(BigFileFixture, GetAssetFlatbuffer_ShouldGetTheAssetFlatbuffer)
asset.SetName("hello");
asset.GetAsset().health = 42;
asset.GetAsset().mana = 7;
const eastl::vector<std::byte> flatbuffer = asset.Pack();
SaveAsset(asset);
const AssetMetadata metadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name};
m_bigFile.SaveAssetFlatbuffer(metadata, flatbuffer);
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer = m_bigFile.GetAssetFlatbuffer(
asset.GetHeader().uuid);
ASSERT_TRUE(savedFlatbuffer.has_value());
EXPECT_EQ(*savedFlatbuffer, flatbuffer);
const eastl::vector<std::byte> savedFlatbuffer = m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid);
EXPECT_EQ(savedFlatbuffer, asset.Pack());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetFlatbuffers_ShouldOmitUUIDsThatDoNotExist)
TEST_F(BigFileFixture, GetAssetFlatbuffers_ShouldReturnEmptyForUUIDsThatDoNotExist)
{
AssetA assetA;
assetA.SetName("hello");
const eastl::vector<std::byte> flatbufferA = assetA.Pack();
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetA.GetHeader().uuid,
.m_name = assetA.GetHeader().name,
.m_typeID = assetA.GetHeader().type_id,
.m_typeName = assetA.GetHeader().type_name},
flatbufferA);
SaveAsset(assetA);
AssetA assetB;
assetB.SetName("world");
const eastl::vector<std::byte> flatbufferB = assetB.Pack();
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetB.GetHeader().uuid,
.m_name = assetB.GetHeader().name,
.m_typeID = assetB.GetHeader().type_id,
.m_typeName = assetB.GetHeader().type_name},
flatbufferB);
SaveAsset(assetB);
const eastl::vector<UUID> uuids {assetA.GetHeader().uuid, UUID {}, assetB.GetHeader().uuid};
const eastl::array<UUID, 3> uuids {assetA.GetHeader().uuid, UUID {}, assetB.GetHeader().uuid};
const eastl::vector<eastl::vector<std::byte>> flatbuffers = m_bigFile.GetAssetFlatbuffers(uuids);
ASSERT_EQ(flatbuffers.size(), 2);
EXPECT_TRUE((flatbuffers[0] == flatbufferA && flatbuffers[1] == flatbufferB) ||
(flatbuffers[0] == flatbufferB && flatbuffers[1] == flatbufferA));
ASSERT_EQ(flatbuffers.size(), 3);
EXPECT_EQ(flatbuffers[0], assetA.Pack());
EXPECT_TRUE(flatbuffers[1].empty());
EXPECT_EQ(flatbuffers[2], assetB.Pack());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetFlatbuffers_ShouldNotDuplicateResultsForADuplicateUUID)
TEST_F(BigFileFixture, GetAssetFlatbuffers_ShouldReturnEmptyForASingleUUIDThatDoesNotExist)
{
AssetA assetA;
assetA.SetName("hello");
const eastl::vector<std::byte> flatbufferA = assetA.Pack();
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetA.GetHeader().uuid,
.m_name = assetA.GetHeader().name,
.m_typeID = assetA.GetHeader().type_id,
.m_typeName = assetA.GetHeader().type_name},
flatbufferA);
const eastl::vector<UUID> uuids {assetA.GetHeader().uuid, assetA.GetHeader().uuid};
const eastl::array<UUID, 1> uuids {UUID {}};
const eastl::vector<eastl::vector<std::byte>> flatbuffers = m_bigFile.GetAssetFlatbuffers(uuids);
ASSERT_EQ(flatbuffers.size(), 1);
EXPECT_EQ(flatbuffers[0], flatbufferA);
EXPECT_TRUE(flatbuffers[0].empty());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetMetadata_ShouldReturnNulloptForNonExistentUUID)
{
const UUID uuid;
EXPECT_FALSE(m_bigFile.GetAssetMetadata(uuid).has_value());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetMetadata_ShouldGetTheAssetMetadata)
{
AssetA asset;
asset.SetName("hello");
asset.GetAsset().health = 42;
asset.GetAsset().mana = 7;
const eastl::vector<std::byte> flatbuffer = asset.Pack();
const AssetMetadata metadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name};
m_bigFile.SaveAssetFlatbuffer(metadata, flatbuffer);
const eastl::optional<AssetMetadata> savedMetadata = m_bigFile.GetAssetMetadata(asset.GetHeader().uuid);
ASSERT_TRUE(savedMetadata.has_value());
EXPECT_EQ(savedMetadata->m_uuid, metadata.m_uuid);
EXPECT_EQ(savedMetadata->m_name, metadata.m_name);
EXPECT_EQ(savedMetadata->m_typeID, metadata.m_typeID);
EXPECT_EQ(savedMetadata->m_typeName, metadata.m_typeName);
EXPECT_GT(savedMetadata->m_creationTime, Time {0});
EXPECT_GT(savedMetadata->m_modificationTime, Time {0});
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetMetadatas_ShouldOmitUUIDsThatDoNotExist)
TEST_F(BigFileFixture, GetAssetFlatbuffers_ShouldDuplicateResultsForADuplicateUUID)
{
AssetA assetA;
assetA.SetName("hello");
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetA.GetHeader().uuid,
.m_name = assetA.GetHeader().name,
.m_typeID = assetA.GetHeader().type_id,
.m_typeName = assetA.GetHeader().type_name},
assetA.Pack());
SaveAsset(assetA);
AssetA assetB;
assetB.SetName("world");
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = assetB.GetHeader().uuid,
.m_name = assetB.GetHeader().name,
.m_typeID = assetB.GetHeader().type_id,
.m_typeName = assetB.GetHeader().type_name},
assetB.Pack());
const eastl::array<UUID, 2> uuids {assetA.GetHeader().uuid, assetA.GetHeader().uuid};
const eastl::vector<eastl::vector<std::byte>> flatbuffers = m_bigFile.GetAssetFlatbuffers(uuids);
ASSERT_EQ(flatbuffers.size(), 2);
EXPECT_EQ(flatbuffers[0], assetA.Pack());
EXPECT_EQ(flatbuffers[1], assetA.Pack());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetHardReference_ShouldReturnEmptyVectorForNonExistentUUID)
{
const UUID uuid;
const eastl::vector<UUID> hardReferences = m_bigFile.GetAssetHardReferences(uuid);
EXPECT_TRUE(hardReferences.empty());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetHardReference_ShouldGetTheAssetHardReferences)
{
AssetA referencedAssetX;
referencedAssetX.SetName("referencedX");
AssetA referencedAssetY;
referencedAssetY.SetName("referencedY");
AssetB asset;
asset.SetName("hello");
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetX.GetHeader().uuid};
asset.GetAsset().refs_a.push_back(SoftReference<AssetA> {referencedAssetY.GetHeader().uuid});
SaveAsset(asset);
const eastl::vector<UUID> hardReferences = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
EXPECT_THAT(hardReferences, ::testing::ElementsAre(referencedAssetX.GetHeader().uuid));
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetHardReferences_ShouldReturnEmptyVectorsForUUIDsThatDoNotExistOrHaveNoReferences)
{
AssetA referencedAsset;
referencedAsset.SetName("referenced");
AssetA assetWithoutReferences;
assetWithoutReferences.SetName("noRefs");
SaveAsset(assetWithoutReferences);
AssetB assetWithReferences;
assetWithReferences.SetName("withRefs");
assetWithReferences.GetAsset().ref_a = HardReference<AssetA> {referencedAsset.GetHeader().uuid};
SaveAsset(assetWithReferences);
const UUID missingUUID;
const eastl::vector<UUID> uuids {assetA.GetHeader().uuid, missingUUID, assetB.GetHeader().uuid};
const eastl::array<UUID, 3> uuids {assetWithoutReferences.GetHeader().uuid,
missingUUID,
assetWithReferences.GetHeader().uuid};
const eastl::vector<AssetMetadata> metadatas = m_bigFile.GetAssetMetadatas(uuids);
const eastl::vector<eastl::vector<UUID>> hardReferences = m_bigFile.GetAssetsHardReferences(uuids);
ASSERT_EQ(metadatas.size(), 2);
EXPECT_TRUE((metadatas[0].m_uuid == assetA.GetHeader().uuid && metadatas[0].m_name == "hello" &&
metadatas[1].m_uuid == assetB.GetHeader().uuid && metadatas[1].m_name == "world") ||
(metadatas[0].m_uuid == assetB.GetHeader().uuid && metadatas[0].m_name == "world" &&
metadatas[1].m_uuid == assetA.GetHeader().uuid && metadatas[1].m_name == "hello"));
ASSERT_EQ(hardReferences.size(), 3);
EXPECT_TRUE(hardReferences[0].empty());
EXPECT_TRUE(hardReferences[1].empty());
ASSERT_EQ(hardReferences[2].size(), 1);
EXPECT_EQ(hardReferences[2][0], referencedAsset.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetHardReferences_ShouldDuplicateResultsForADuplicateUUID)
{
AssetA referencedAssetX;
referencedAssetX.SetName("referencedX");
AssetA referencedAssetY;
referencedAssetY.SetName("referencedY");
AssetB asset;
asset.SetName("hello");
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetX.GetHeader().uuid};
asset.GetAsset().refs_a.push_back(SoftReference<AssetA> {referencedAssetY.GetHeader().uuid});
SaveAsset(asset);
const eastl::array<UUID, 2> uuids {asset.GetHeader().uuid, asset.GetHeader().uuid};
const eastl::vector<eastl::vector<UUID>> hardReferences = m_bigFile.GetAssetsHardReferences(uuids);
ASSERT_EQ(hardReferences.size(), 2);
for (const eastl::vector<UUID>& entry: hardReferences)
{
EXPECT_THAT(entry, ::testing::ElementsAre(referencedAssetX.GetHeader().uuid));
}
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetTime_ShouldReturnNulloptForNonExistentUUID)
{
const UUID uuid;
EXPECT_FALSE(m_bigFile.GetAssetTime(uuid).has_value());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetTime_ShouldReturnCreationAndModificationTimeOnFirstSave)
{
AssetA asset;
asset.SetName("hello");
const std::uint64_t beforeSave = Time::Now().GetEpoch();
SaveAsset(asset);
const eastl::optional<BigFile::AssetTime> assetTime = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(assetTime.has_value());
constexpr std::uint64_t TOLERANCE_MICROSECONDS = 1'000'000;
const std::uint64_t creationEpoch = assetTime->m_creationTime.GetEpoch();
const std::uint64_t delta = creationEpoch > beforeSave ? creationEpoch - beforeSave : beforeSave - creationEpoch;
EXPECT_LE(delta, TOLERANCE_MICROSECONDS);
EXPECT_EQ(assetTime->m_creationTime, assetTime->m_modificationTime);
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffer_ShouldUpdateModificationTimeButNotCreationTimeOnSecondSave)
{
AssetA asset;
asset.SetName("hello");
SaveAsset(asset);
const eastl::optional<BigFile::AssetTime> firstAssetTime = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(firstAssetTime.has_value());
std::this_thread::sleep_for(std::chrono::milliseconds(20));
asset.GetAsset().health = 42;
SaveAsset(asset);
const eastl::optional<BigFile::AssetTime> secondAssetTime = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(secondAssetTime.has_value());
EXPECT_EQ(secondAssetTime->m_creationTime, firstAssetTime->m_creationTime);
EXPECT_GT(secondAssetTime->m_modificationTime.GetEpoch(), firstAssetTime->m_modificationTime.GetEpoch());
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffers_ShouldUpdateModificationTimeButNotCreationTimeOnSecondSave)
{
AssetA asset;
asset.SetName("hello");
SaveAssets({&asset});
const eastl::optional<BigFile::AssetTime> firstAssetTime = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(firstAssetTime.has_value());
std::this_thread::sleep_for(std::chrono::milliseconds(20));
asset.GetAsset().health = 42;
SaveAssets({&asset});
const eastl::optional<BigFile::AssetTime> secondAssetTime = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(secondAssetTime.has_value());
EXPECT_EQ(secondAssetTime->m_creationTime, firstAssetTime->m_creationTime);
EXPECT_GT(secondAssetTime->m_modificationTime.GetEpoch(), firstAssetTime->m_modificationTime.GetEpoch());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetsTime_ShouldReturnNulloptForUUIDsThatDoNotExist)
{
AssetA asset;
asset.SetName("hello");
SaveAsset(asset);
const UUID missingUUID;
const eastl::array<UUID, 2> uuids {asset.GetHeader().uuid, missingUUID};
const eastl::vector<eastl::optional<BigFile::AssetTime>> times = m_bigFile.GetAssetsTime(uuids);
ASSERT_EQ(times.size(), 2);
EXPECT_TRUE(times[0].has_value());
EXPECT_FALSE(times[1].has_value());
}
/****************************************************************************************/
TEST_F(BigFileFixture, GetAssetsTime_ShouldDuplicateResultsForADuplicateUUID)
{
AssetA asset;
asset.SetName("hello");
SaveAsset(asset);
const eastl::optional<BigFile::AssetTime> expected = m_bigFile.GetAssetTime(asset.GetHeader().uuid);
ASSERT_TRUE(expected.has_value());
const eastl::array<UUID, 2> uuids {asset.GetHeader().uuid, asset.GetHeader().uuid};
const eastl::vector<eastl::optional<BigFile::AssetTime>> times = m_bigFile.GetAssetsTime(uuids);
ASSERT_EQ(times.size(), 2);
ASSERT_TRUE(times[0].has_value());
ASSERT_TRUE(times[1].has_value());
EXPECT_EQ(*times[0], *expected);
EXPECT_EQ(*times[1], *expected);
}
/****************************************************************************************/
TEST_F(BigFileFixture, Clear_ShouldRemoveAssetTime)
{
AssetA asset;
asset.SetName("hello");
SaveAsset(asset);
ASSERT_TRUE(m_bigFile.GetAssetTime(asset.GetHeader().uuid).has_value());
m_bigFile.Clear();
EXPECT_FALSE(m_bigFile.GetAssetTime(asset.GetHeader().uuid).has_value());
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffer_ShouldSaveTheAsset)
{
AssetA asset;
AssetA referencedAsset;
referencedAsset.SetName("referenced");
AssetB asset;
asset.SetName("hello");
asset.GetAsset().health = 42;
asset.GetAsset().mana = 7;
const eastl::vector<std::byte> flatbuffer = asset.Pack();
const AssetMetadata metadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name};
m_bigFile.SaveAssetFlatbuffer(metadata, flatbuffer);
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAsset.GetHeader().uuid};
SaveAsset(asset);
EXPECT_TRUE(m_bigFile.Has(asset.GetHeader().uuid));
const eastl::vector<UUID> hardReferences = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
ASSERT_EQ(hardReferences.size(), 1);
EXPECT_EQ(hardReferences[0], referencedAsset.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffer_ShouldUpdateTheDataWhenCalledTwice)
{
const UUID uuid;
AssetA referencedAssetX;
referencedAssetX.SetName("referencedX");
AssetA asset;
asset.GetAsset().health = 1;
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = uuid,
.m_name = "hello",
.m_typeID = AssetA::GetTypeID(),
.m_typeName = eastl::string {AssetA::GetTypeName()}},
asset.Pack());
AssetA referencedAssetY;
referencedAssetY.SetName("referencedY");
const eastl::optional<AssetMetadata> savedMetadata1 = m_bigFile.GetAssetMetadata(uuid);
ASSERT_TRUE(savedMetadata1.has_value());
EXPECT_EQ(savedMetadata1->m_name, "hello");
EXPECT_EQ(savedMetadata1->m_typeID, AssetA::GetTypeID());
EXPECT_EQ(savedMetadata1->m_typeName, eastl::string {AssetA::GetTypeName()});
EXPECT_EQ(savedMetadata1->m_modificationTime, savedMetadata1->m_creationTime);
AssetB asset;
asset.GetAsset().count = 1;
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetX.GetHeader().uuid};
SaveAsset(asset);
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer1 = m_bigFile.GetAssetFlatbuffer(uuid);
ASSERT_TRUE(savedFlatbuffer1.has_value());
const eastl::vector<std::byte> savedFlatbuffer1 = m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid);
ASSERT_FALSE(savedFlatbuffer1.empty());
AssetA savedAsset1;
savedAsset1.Unpack(*savedFlatbuffer1);
EXPECT_EQ(savedAsset1.GetAsset().health, 1);
AssetB savedAsset1;
savedAsset1.Unpack(savedFlatbuffer1);
EXPECT_EQ(savedAsset1.GetAsset().count, 1);
const eastl::vector<UUID> hardReferences1 = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
ASSERT_EQ(hardReferences1.size(), 1);
EXPECT_EQ(hardReferences1[0], referencedAssetX.GetHeader().uuid);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
asset.GetAsset().health = 2;
m_bigFile.SaveAssetFlatbuffer(AssetMetadata {.m_uuid = uuid,
.m_name = "world",
.m_typeID = AssetA::GetTypeID(),
.m_typeName = eastl::string {AssetA::GetTypeName()}},
asset.Pack());
asset.GetAsset().count = 2;
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetY.GetHeader().uuid};
SaveAsset(asset);
const eastl::optional<AssetMetadata> savedMetadata2 = m_bigFile.GetAssetMetadata(uuid);
ASSERT_TRUE(savedMetadata2.has_value());
EXPECT_EQ(savedMetadata2->m_name, "world");
EXPECT_EQ(savedMetadata2->m_typeID, AssetA::GetTypeID());
EXPECT_EQ(savedMetadata2->m_typeName, eastl::string {AssetA::GetTypeName()});
EXPECT_GT(savedMetadata2->m_modificationTime, savedMetadata2->m_creationTime);
const eastl::vector<std::byte> savedFlatbuffer2 = m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid);
ASSERT_FALSE(savedFlatbuffer2.empty());
EXPECT_LT(savedMetadata1->m_modificationTime, savedMetadata2->m_modificationTime);
AssetB savedAsset2;
savedAsset2.Unpack(savedFlatbuffer2);
EXPECT_EQ(savedAsset2.GetAsset().count, 2);
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer2 = m_bigFile.GetAssetFlatbuffer(uuid);
ASSERT_TRUE(savedFlatbuffer2.has_value());
AssetA savedAsset2;
savedAsset2.Unpack(*savedFlatbuffer2);
EXPECT_EQ(savedAsset2.GetAsset().health, 2);
const eastl::vector<UUID> hardReferences2 = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
ASSERT_EQ(hardReferences2.size(), 1);
EXPECT_EQ(hardReferences2[0], referencedAssetY.GetHeader().uuid);
EXPECT_NE(hardReferences2[0], referencedAssetX.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffers_ShouldSaveTheAssets)
{
AssetA assetA;
AssetA referencedAssetA;
referencedAssetA.SetName("referencedA");
AssetA referencedAssetB;
referencedAssetB.SetName("referencedB");
AssetB assetA;
assetA.SetName("hello");
assetA.GetAsset().health = 42;
assetA.GetAsset().count = 42;
assetA.GetAsset().ref_a = HardReference<AssetA> {referencedAssetA.GetHeader().uuid};
AssetA assetB;
AssetB assetB;
assetB.SetName("world");
assetB.GetAsset().health = 7;
assetB.GetAsset().count = 7;
assetB.GetAsset().ref_a = HardReference<AssetA> {referencedAssetB.GetHeader().uuid};
const eastl::vector<AssetMetadata> metadatas {AssetMetadata {.m_uuid = assetA.GetHeader().uuid,
.m_name = assetA.GetHeader().name,
.m_typeID = assetA.GetHeader().type_id,
.m_typeName = assetA.GetHeader().type_name},
AssetMetadata {.m_uuid = assetB.GetHeader().uuid,
.m_name = assetB.GetHeader().name,
.m_typeID = assetB.GetHeader().type_id,
.m_typeName = assetB.GetHeader().type_name}};
const eastl::vector<eastl::vector<std::byte>> flatbuffers {assetA.Pack(), assetB.Pack()};
m_bigFile.SaveAssetFlatbuffers(metadatas, flatbuffers);
SaveAssets({&assetA, &assetB});
EXPECT_TRUE(m_bigFile.Has(assetA.GetHeader().uuid));
EXPECT_TRUE(m_bigFile.Has(assetB.GetHeader().uuid));
const eastl::vector<UUID> hardReferencesA = m_bigFile.GetAssetHardReferences(assetA.GetHeader().uuid);
ASSERT_EQ(hardReferencesA.size(), 1);
EXPECT_EQ(hardReferencesA[0], referencedAssetA.GetHeader().uuid);
const eastl::vector<UUID> hardReferencesB = m_bigFile.GetAssetHardReferences(assetB.GetHeader().uuid);
ASSERT_EQ(hardReferencesB.size(), 1);
EXPECT_EQ(hardReferencesB[0], referencedAssetB.GetHeader().uuid);
}
/****************************************************************************************/
TEST_F(BigFileFixture, SaveAssetFlatbuffers_ShouldUpdateTheDataWhenCalledTwice)
{
const UUID uuid;
AssetA referencedAssetX;
referencedAssetX.SetName("referencedX");
AssetA asset;
asset.GetAsset().health = 1;
const eastl::vector<AssetMetadata> metadatas1 {AssetMetadata {.m_uuid = uuid,
.m_name = "hello",
.m_typeID = AssetA::GetTypeID(),
.m_typeName = eastl::string {AssetA::GetTypeName()}}};
const eastl::vector<eastl::vector<std::byte>> flatbuffers1 {asset.Pack()};
m_bigFile.SaveAssetFlatbuffers(metadatas1, flatbuffers1);
AssetA referencedAssetY;
referencedAssetY.SetName("referencedY");
const eastl::optional<AssetMetadata> savedMetadata1 = m_bigFile.GetAssetMetadata(uuid);
ASSERT_TRUE(savedMetadata1.has_value());
EXPECT_EQ(savedMetadata1->m_name, "hello");
EXPECT_EQ(savedMetadata1->m_modificationTime, savedMetadata1->m_creationTime);
AssetB asset;
asset.GetAsset().count = 1;
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetX.GetHeader().uuid};
SaveAssets({&asset});
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer1 = m_bigFile.GetAssetFlatbuffer(uuid);
ASSERT_TRUE(savedFlatbuffer1.has_value());
eastl::vector<std::byte> savedFlatbuffer1 = m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid);
ASSERT_FALSE(savedFlatbuffer1.empty());
AssetA savedAsset1;
savedAsset1.Unpack(*savedFlatbuffer1);
EXPECT_EQ(savedAsset1.GetAsset().health, 1);
AssetB savedAsset1;
savedAsset1.Unpack(savedFlatbuffer1);
EXPECT_EQ(savedAsset1.GetAsset().count, 1);
const eastl::vector<UUID> hardReferences1 = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
ASSERT_EQ(hardReferences1.size(), 1);
EXPECT_EQ(hardReferences1[0], referencedAssetX.GetHeader().uuid);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
asset.GetAsset().health = 2;
const eastl::vector<AssetMetadata> metadatas2 {AssetMetadata {.m_uuid = uuid,
.m_name = "world",
.m_typeID = AssetA::GetTypeID(),
.m_typeName = eastl::string {AssetA::GetTypeName()}}};
const eastl::vector<eastl::vector<std::byte>> flatbuffers2 {asset.Pack()};
m_bigFile.SaveAssetFlatbuffers(metadatas2, flatbuffers2);
asset.GetAsset().count = 2;
asset.GetAsset().ref_a = HardReference<AssetA> {referencedAssetY.GetHeader().uuid};
SaveAssets({&asset});
const eastl::optional<AssetMetadata> savedMetadata2 = m_bigFile.GetAssetMetadata(uuid);
ASSERT_TRUE(savedMetadata2.has_value());
EXPECT_EQ(savedMetadata2->m_name, "world");
EXPECT_GT(savedMetadata2->m_modificationTime, savedMetadata2->m_creationTime);
const eastl::vector<std::byte> savedFlatbuffer2 = m_bigFile.GetAssetFlatbuffer(asset.GetHeader().uuid);
ASSERT_FALSE(savedFlatbuffer2.empty());
EXPECT_LT(savedMetadata1->m_modificationTime, savedMetadata2->m_modificationTime);
AssetB savedAsset2;
savedAsset2.Unpack(savedFlatbuffer2);
EXPECT_EQ(savedAsset2.GetAsset().count, 2);
const eastl::optional<eastl::vector<std::byte>> savedFlatbuffer2 = m_bigFile.GetAssetFlatbuffer(uuid);
ASSERT_TRUE(savedFlatbuffer2.has_value());
AssetA savedAsset2;
savedAsset2.Unpack(*savedFlatbuffer2);
EXPECT_EQ(savedAsset2.GetAsset().health, 2);
const eastl::vector<UUID> hardReferences2 = m_bigFile.GetAssetHardReferences(asset.GetHeader().uuid);
ASSERT_EQ(hardReferences2.size(), 1);
EXPECT_EQ(hardReferences2[0], referencedAssetY.GetHeader().uuid);
}
/****************************************************************************************/
@@ -347,14 +548,7 @@ TEST_F(BigFileFixture, Has_ShouldReturnTrueIfTheAssetExists)
asset.SetName("hello");
asset.GetAsset().health = 42;
asset.GetAsset().mana = 7;
const eastl::vector<std::byte> flatbuffer = asset.Pack();
const AssetMetadata metadata {.m_uuid = asset.GetHeader().uuid,
.m_name = asset.GetHeader().name,
.m_typeID = asset.GetHeader().type_id,
.m_typeName = asset.GetHeader().type_name};
m_bigFile.SaveAssetFlatbuffer(metadata, flatbuffer);
SaveAsset(asset);
EXPECT_TRUE(m_bigFile.Has(asset.GetHeader().uuid));
}
+3 -1
View File
@@ -10,4 +10,6 @@ bigfoot_create_package_tests(
"${BigfootDependencies}"
"")
bigfoot_create_bigfile("Tests/Bigfoot")
bigfoot_create_bigfile("Tests/Bigfoot")
bigfoot_create_sasquatch_info()
@@ -6,7 +6,8 @@
*********************************************************************/
#include <Engine/DataAccessor/DataAccessor.hpp>
#include <gmock/gmock.h>
#include <EngineTests/DataAccessorMock.hpp>
#include <gtest/gtest.h>
namespace Bigfoot
@@ -14,34 +15,6 @@ namespace Bigfoot
class DataAccessorFixture: public ::testing::Test
{
protected:
class DataAccessorMock: public DataAccessor
{
public:
MOCK_METHOD(eastl::optional<eastl::vector<std::byte>>,
GetAssetFlatbuffer,
(const UUID& p_uuid),
(const, override));
MOCK_METHOD(eastl::vector<eastl::vector<std::byte>>,
GetAssetFlatbuffers,
(eastl::span<const UUID> p_uuids),
(const, override));
MOCK_METHOD(eastl::optional<AssetMetadata>, GetAssetMetadata, (const UUID& p_uuid), (const, override));
MOCK_METHOD(eastl::vector<AssetMetadata>,
GetAssetMetadatas,
(eastl::span<const UUID> p_uuids),
(const, override));
MOCK_METHOD(void,
SaveAssetFlatbuffer,
(const AssetMetadata& p_metaData, const eastl::span<const std::byte> p_flatbuffer),
(override));
MOCK_METHOD(void,
SaveAssetFlatbuffers,
(eastl::span<const AssetMetadata> p_metadatas,
eastl::span<const eastl::vector<std::byte>> p_flatbuffers),
(override));
MOCK_METHOD(bool, Has, (const UUID& p_uuid), (const, override));
};
void SetUp() override
{
m_original = g_dataAccessor;
@@ -53,7 +26,7 @@ class DataAccessorFixture: public ::testing::Test
}
DataAccessorMock m_accessor;
DataAccessor* m_original;
DataAccessor* m_original = nullptr;
};
/****************************************************************************************/
@@ -13,6 +13,8 @@ int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
GTEST_FLAG_SET(death_test_style, "threadsafe");
BIGFOOT_NOT_OPTIMIZED_ONLY(::Bigfoot::Singleton<::Bigfoot::Log>::Lifetime m_lifetime);
return RUN_ALL_TESTS();

Some files were not shown because too many files have changed in this diff Show More