From 209f8340165a567b8a72bbb4501cc986d5a4afee Mon Sep 17 00:00:00 2001
From: Death Killer <884052+deathkiller@users.noreply.github.com>
Date: Sun, 5 Nov 2023 01:30:43 +0100
Subject: [PATCH] Replaced `FNV1a`/`fasthash` with `CityHash`, reworked
`BinaryShaderCache`, fixed `RenderBatcher`, use `static_cast`
---
Sources/Jazz2.vcxproj | 1 +
Sources/Jazz2.vcxproj.filters | 12 +
Sources/Jazz2/Multiplayer/NetworkManager.cpp | 4 +-
.../Jazz2/Scripting/JJ2PlusDefinitions.cpp | 70 ++--
Sources/Jazz2/Scripting/LevelScriptLoader.cpp | 42 +-
.../Jazz2/Scripting/ScriptActorWrapper.cpp | 4 +-
Sources/Jazz2/Scripting/ScriptLoader.cpp | 4 +-
.../Jazz2/Scripting/ScriptPlayerWrapper.cpp | 2 +-
Sources/Jazz2/UI/DiscordRpcClient.cpp | 2 +-
Sources/Jazz2/UI/Menu/ImportSection.cpp | 4 +-
Sources/Jazz2/UI/Menu/RefreshCacheSection.cpp | 13 +-
Sources/Jazz2/UI/RgbLights.cpp | 6 +-
Sources/Shared/Base/Unaligned.h | 62 +++
Sources/nCine/Audio/AudioReaderMpt.cpp | 6 +-
Sources/nCine/Backends/GlfwInputManager.cpp | 2 +-
Sources/nCine/Base/Clock.cpp | 5 +-
Sources/nCine/Base/HashFunctions.cpp | 368 ++++++++++++++++++
Sources/nCine/Base/HashFunctions.h | 80 +++-
Sources/nCine/Base/HashMap.h | 8 +-
Sources/nCine/Graphics/BinaryShaderCache.cpp | 79 ++--
Sources/nCine/Graphics/BinaryShaderCache.h | 2 +-
Sources/nCine/Graphics/IGfxDevice.cpp | 4 +-
Sources/nCine/Graphics/RenderBatcher.cpp | 4 +-
Sources/nCine/Graphics/RenderCommand.cpp | 2 +-
Sources/nCine/MainApplication.cpp | 2 +-
cmake/ncine_headers.cmake | 1 +
26 files changed, 634 insertions(+), 155 deletions(-)
create mode 100644 Sources/Shared/Base/Unaligned.h
diff --git a/Sources/Jazz2.vcxproj b/Sources/Jazz2.vcxproj
index fa08e09e..37c8a5c2 100644
--- a/Sources/Jazz2.vcxproj
+++ b/Sources/Jazz2.vcxproj
@@ -228,6 +228,7 @@
+
diff --git a/Sources/Jazz2.vcxproj.filters b/Sources/Jazz2.vcxproj.filters
index e7ba2d21..adf2bf91 100644
--- a/Sources/Jazz2.vcxproj.filters
+++ b/Sources/Jazz2.vcxproj.filters
@@ -211,6 +211,9 @@
{8e8a53f2-8324-4eaa-ab44-82141d569c85}
+
+ {0fd6dab6-c92b-45ba-a620-98604d239e84}
+
@@ -1383,6 +1386,12 @@
Header Files\Jazz2\Multiplayer
+
+ Header Files
+
+
+ Header Files\Shared\Base
+
@@ -2291,6 +2300,9 @@
Source Files\Jazz2
+
+ Source Files
+
diff --git a/Sources/Jazz2/Multiplayer/NetworkManager.cpp b/Sources/Jazz2/Multiplayer/NetworkManager.cpp
index 20766700..83364126 100644
--- a/Sources/Jazz2/Multiplayer/NetworkManager.cpp
+++ b/Sources/Jazz2/Multiplayer/NetworkManager.cpp
@@ -174,7 +174,7 @@ namespace Jazz2::Multiplayer
void NetworkManager::OnClientThread(void* param)
{
- NetworkManager* _this = reinterpret_cast(param);
+ NetworkManager* _this = static_cast(param);
INetworkHandler* handler = _this->_handler;
ENetHost* host = _this->_host;
@@ -252,7 +252,7 @@ namespace Jazz2::Multiplayer
void NetworkManager::OnServerThread(void* param)
{
- NetworkManager* _this = reinterpret_cast(param);
+ NetworkManager* _this = static_cast(param);
INetworkHandler* handler = _this->_handler;
ENetHost* host = _this->_host;
diff --git a/Sources/Jazz2/Scripting/JJ2PlusDefinitions.cpp b/Sources/Jazz2/Scripting/JJ2PlusDefinitions.cpp
index 74bd8b44..84d5864e 100644
--- a/Sources/Jazz2/Scripting/JJ2PlusDefinitions.cpp
+++ b/Sources/Jazz2/Scripting/JJ2PlusDefinitions.cpp
@@ -167,7 +167,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjSTREAM));
return new(mem) jjSTREAM();
@@ -176,7 +176,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjSTREAM));
return new(mem) jjSTREAM();
@@ -438,7 +438,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjANIMFRAME));
return new(mem) jjANIMFRAME();
@@ -498,7 +498,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjANIMATION));
return new(mem) jjANIMATION(index);
@@ -542,7 +542,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjANIMSET));
return new(mem) jjANIMSET(index);
@@ -839,7 +839,7 @@ namespace Jazz2::Scripting
{
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjOBJ));
return new(mem) jjOBJ();
@@ -849,7 +849,7 @@ namespace Jazz2::Scripting
{
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjOBJ));
return new(mem) jjOBJ();
@@ -1415,14 +1415,14 @@ namespace Jazz2::Scripting
int32_t get_jjPlayerCount() {
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPLAYER));
return owner->GetPlayers().size();
}
int32_t get_jjLocalPlayerCount() {
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPLAYER));
return owner->GetPlayers().size();
@@ -1432,7 +1432,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPLAYER));
return new(mem) jjPLAYER(owner, 0);
@@ -1441,7 +1441,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPLAYER));
return new(mem) jjPLAYER(owner, index);
@@ -1450,7 +1450,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPLAYER));
return new(mem) jjPLAYER(owner, index);
@@ -1467,7 +1467,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1476,7 +1476,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1485,7 +1485,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1494,7 +1494,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1503,7 +1503,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1512,7 +1512,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1521,7 +1521,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjPIXELMAP));
return new(mem) jjPIXELMAP();
@@ -1578,7 +1578,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjMASKMAP));
return new(mem) jjMASKMAP();
@@ -1587,7 +1587,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjMASKMAP));
return new(mem) jjMASKMAP();
@@ -1664,7 +1664,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(jjLAYER));
return new(mem) jjLAYER();
@@ -1776,7 +1776,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_musicCurrentPath;
}
String get_jjTilesetFileName() {
@@ -1787,14 +1787,14 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->GetLevelText(index);
}
void LevelScriptLoader::set_jjHelpStrings(uint32_t index, const String& text) {
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->OverrideLevelText(index, text);
}
@@ -1807,7 +1807,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::jjAlert(const String& text, bool sendToAll, uint32_t size)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->ShowLevelText(text);
}
void jjPrint(const String& text, bool timestamp) {
@@ -1993,14 +1993,14 @@ namespace Jazz2::Scripting
//noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->GetTrigger(id);
}
bool LevelScriptLoader::set_jjTriggers(uint8_t id, bool value) {
//noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->SetTrigger(id, value);
return value;
}
@@ -2008,7 +2008,7 @@ namespace Jazz2::Scripting
//noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->SetTrigger(id, !_this->_levelHandler->GetTrigger(id));
return _this->_levelHandler->GetTrigger(id);
}
@@ -2070,7 +2070,7 @@ namespace Jazz2::Scripting
}
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->BeginLevelChange(exitType, { });
}
@@ -2092,7 +2092,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->BeginPlayMusic(filename, !temporary, forceReload);
return false;
@@ -2101,7 +2101,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
if (_this->_levelHandler->_music != nullptr) {
_this->_levelHandler->_music->stop();
}
@@ -2110,7 +2110,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
if (_this->_levelHandler->_music != nullptr) {
_this->_levelHandler->_music->play();
}
@@ -2119,7 +2119,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
if (_this->_levelHandler->_music != nullptr) {
_this->_levelHandler->_music->stop();
}
@@ -2128,7 +2128,7 @@ namespace Jazz2::Scripting
noop();
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
if (_this->_levelHandler->_music != nullptr && _this->_levelHandler->_music->isPaused()) {
_this->_levelHandler->_music->play();
}
diff --git a/Sources/Jazz2/Scripting/LevelScriptLoader.cpp b/Sources/Jazz2/Scripting/LevelScriptLoader.cpp
index b0f67f80..750049d2 100644
--- a/Sources/Jazz2/Scripting/LevelScriptLoader.cpp
+++ b/Sources/Jazz2/Scripting/LevelScriptLoader.cpp
@@ -3292,10 +3292,10 @@ namespace Jazz2::Scripting
return nullptr;
}
- asIScriptObject* obj = reinterpret_cast(_engine->CreateScriptObject(typeInfo));
+ asIScriptObject* obj = static_cast(_engine->CreateScriptObject(typeInfo));
// Get the pointer to the C++ side of the ActorBase class
- ScriptActorWrapper* obj2 = *reinterpret_cast(obj->GetAddressOfProperty(0));
+ ScriptActorWrapper* obj2 = *static_cast(obj->GetAddressOfProperty(0));
// Increase the reference count to the C++ object, as this is what will be used to control the life time of the object from the application side
obj2->AddRef();
@@ -3314,63 +3314,63 @@ namespace Jazz2::Scripting
uint8_t LevelScriptLoader::asGetDifficulty()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return (uint8_t)_this->_levelHandler->_difficulty;
}
bool LevelScriptLoader::asIsReforged()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return (uint8_t)_this->_levelHandler->_isReforged;
}
int LevelScriptLoader::asGetLevelWidth()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_tileMap->GetLevelBounds().X;
}
int LevelScriptLoader::asGetLevelHeight()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_tileMap->GetLevelBounds().Y;
}
float LevelScriptLoader::asGetElapsedFrames()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_elapsedFrames;
}
float LevelScriptLoader::asGetAmbientLight()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_ambientLightTarget;
}
void LevelScriptLoader::asSetAmbientLight(float value)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->_ambientLightTarget = value;
}
float LevelScriptLoader::asGetWaterLevel()
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
return _this->_levelHandler->_waterLevel;
}
void LevelScriptLoader::asSetWaterLevel(float value)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->_waterLevel = value;
}
@@ -3382,7 +3382,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asRegisterSpawnable(int eventType, const String& typeName)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
asITypeInfo* typeInfo = _this->_module->GetTypeInfoByName(typeName.data());
if (typeInfo == nullptr) {
@@ -3403,8 +3403,8 @@ namespace Jazz2::Scripting
if (_this != nullptr) {
auto it = _this->_eventTypeToTypeInfo.find((int)details.Type);
if (it != _this->_eventTypeToTypeInfo.end()) {
- asIScriptObject* obj = reinterpret_cast(_this->_engine->CreateScriptObject(it->second));
- ScriptActorWrapper* obj2 = *reinterpret_cast(obj->GetAddressOfProperty(0));
+ asIScriptObject* obj = static_cast(_this->_engine->CreateScriptObject(it->second));
+ ScriptActorWrapper* obj2 = *static_cast(obj->GetAddressOfProperty(0));
obj2->AddRef();
obj->Release();
obj2->OnActivated(details);
@@ -3418,7 +3418,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asSpawnEvent(int eventType, int x, int y)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
uint8_t spawnParams[Events::EventSpawner::SpawnParamsSize] { };
auto actor = _this->_levelHandler->EventSpawner()->SpawnEvent((EventType)eventType, spawnParams, Actors::ActorState::None, Vector3i(x, y, ILevelHandler::MainPlaneZ));
@@ -3430,7 +3430,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asSpawnEventParams(int eventType, int x, int y, const CScriptArray& eventParams)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
uint8_t spawnParams[Events::EventSpawner::SpawnParamsSize] { };
int size = eventParams.GetSize();
@@ -3445,7 +3445,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asSpawnType(const String& typeName, int x, int y)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
auto actor = _this->CreateActorInstance(typeName);
if (actor == nullptr) {
@@ -3464,7 +3464,7 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asSpawnTypeParams(const String& typeName, int x, int y, const CScriptArray& eventParams)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
auto actor = _this->CreateActorInstance(typeName);
if (actor == nullptr) {
@@ -3486,21 +3486,21 @@ namespace Jazz2::Scripting
void LevelScriptLoader::asChangeLevel(int exitType, const String& path)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->BeginLevelChange((ExitType)exitType, path);
}
void LevelScriptLoader::asShowLevelText(const String& text)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->ShowLevelText(text);
}
void LevelScriptLoader::asSetWeather(uint8_t weatherType, uint8_t intensity)
{
auto ctx = asGetActiveContext();
- auto _this = reinterpret_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
+ auto _this = static_cast(ctx->GetEngine()->GetUserData(EngineToOwner));
_this->_levelHandler->SetWeather((WeatherType)weatherType, intensity);
}
}
diff --git a/Sources/Jazz2/Scripting/ScriptActorWrapper.cpp b/Sources/Jazz2/Scripting/ScriptActorWrapper.cpp
index 51a42c25..ee01b491 100644
--- a/Sources/Jazz2/Scripting/ScriptActorWrapper.cpp
+++ b/Sources/Jazz2/Scripting/ScriptActorWrapper.cpp
@@ -179,7 +179,7 @@ shared abstract class CollectibleBase : )" AsClassName R"(
ScriptActorWrapper* ScriptActorWrapper::Factory(int actorType)
{
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
// Get the function that is calling the factory, so we can be certain it is the our internal script class
asIScriptFunction* func = ctx->GetFunction(0);
@@ -188,7 +188,7 @@ shared abstract class CollectibleBase : )" AsClassName R"(
return nullptr;
}
- asIScriptObject* obj = reinterpret_cast(ctx->GetThisPointer());
+ asIScriptObject* obj = static_cast(ctx->GetThisPointer());
switch (actorType) {
default:
case 0: {
diff --git a/Sources/Jazz2/Scripting/ScriptLoader.cpp b/Sources/Jazz2/Scripting/ScriptLoader.cpp
index 79f68450..9ab2df89 100644
--- a/Sources/Jazz2/Scripting/ScriptLoader.cpp
+++ b/Sources/Jazz2/Scripting/ScriptLoader.cpp
@@ -920,7 +920,7 @@ namespace Jazz2::Scripting
asIScriptContext* ScriptLoader::RequestContextCallback(asIScriptEngine* engine, void* param)
{
// Check if there is a free context available in the pool
- auto _this = reinterpret_cast(param);
+ auto _this = static_cast(param);
if (!_this->_contextPool.empty()) {
return _this->_contextPool.pop_back_val();
} else {
@@ -937,7 +937,7 @@ namespace Jazz2::Scripting
ctx->Unprepare();
// Place the context into the pool for when it will be needed again
- auto _this = reinterpret_cast(param);
+ auto _this = static_cast(param);
_this->_contextPool.push_back(ctx);
}
diff --git a/Sources/Jazz2/Scripting/ScriptPlayerWrapper.cpp b/Sources/Jazz2/Scripting/ScriptPlayerWrapper.cpp
index 1f8f5c2b..2c7e4a69 100644
--- a/Sources/Jazz2/Scripting/ScriptPlayerWrapper.cpp
+++ b/Sources/Jazz2/Scripting/ScriptPlayerWrapper.cpp
@@ -74,7 +74,7 @@ namespace Jazz2::Scripting
ScriptPlayerWrapper* ScriptPlayerWrapper::Factory(int playerIndex)
{
auto ctx = asGetActiveContext();
- auto owner = reinterpret_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
+ auto owner = static_cast(ctx->GetEngine()->GetUserData(ScriptLoader::EngineToOwner));
void* mem = asAllocMem(sizeof(ScriptPlayerWrapper));
return new(mem) ScriptPlayerWrapper(owner, playerIndex);
diff --git a/Sources/Jazz2/UI/DiscordRpcClient.cpp b/Sources/Jazz2/UI/DiscordRpcClient.cpp
index c55f22a2..e1c80481 100644
--- a/Sources/Jazz2/UI/DiscordRpcClient.cpp
+++ b/Sources/Jazz2/UI/DiscordRpcClient.cpp
@@ -260,7 +260,7 @@ namespace Jazz2::UI
void DiscordRpcClient::OnBackgroundThread(void* args)
{
- DiscordRpcClient* client = reinterpret_cast(args);
+ DiscordRpcClient* client = static_cast(args);
// Handshake
char buffer[2048];
diff --git a/Sources/Jazz2/UI/Menu/ImportSection.cpp b/Sources/Jazz2/UI/Menu/ImportSection.cpp
index c8bab67a..b0ca3697 100644
--- a/Sources/Jazz2/UI/Menu/ImportSection.cpp
+++ b/Sources/Jazz2/UI/Menu/ImportSection.cpp
@@ -112,7 +112,7 @@ namespace Jazz2::UI::Menu
void ImportSection::FileDataCallback(void* context, std::unique_ptr data, size_t length, const StringView& name)
{
- auto* _this = reinterpret_cast(context);
+ auto* _this = static_cast(context);
_this->_fileCount--;
int32_t offset = 180; // Skip header
@@ -133,7 +133,7 @@ namespace Jazz2::UI::Menu
void ImportSection::FileCountCallback(void* context, int32_t fileCount)
{
- auto* _this = reinterpret_cast(context);
+ auto* _this = static_cast(context);
_this->_fileCount = fileCount;
if (fileCount <= 0) {
_this->_state = State::NothingSelected;
diff --git a/Sources/Jazz2/UI/Menu/RefreshCacheSection.cpp b/Sources/Jazz2/UI/Menu/RefreshCacheSection.cpp
index baf99442..806a34f1 100644
--- a/Sources/Jazz2/UI/Menu/RefreshCacheSection.cpp
+++ b/Sources/Jazz2/UI/Menu/RefreshCacheSection.cpp
@@ -7,8 +7,11 @@
#include "../../PreferencesCache.h"
#include "../../../nCine/Base/Algorithms.h"
+#include "../../../nCine/Graphics/BinaryShaderCache.h"
+#include "../../../nCine/Graphics/RenderResources.h"
using namespace Jazz2::UI::Menu::Resources;
+using namespace nCine;
namespace Jazz2::UI::Menu
{
@@ -23,16 +26,24 @@ namespace Jazz2::UI::Menu
#if defined(WITH_THREADS)
_thread.Run([](void* arg) {
- auto _this = reinterpret_cast(arg);
+ auto _this = static_cast(arg);
if (auto mainMenu = dynamic_cast(_this->_root)) {
mainMenu->_root->RefreshCacheLevels();
}
+
+ std::uint32_t filesRemoved = RenderResources::binaryShaderCache().prune();
+ LOGI("Pruning binary shader cache (removed %u files)...", filesRemoved);
+
_this->_done = true;
}, this);
#else
if (auto mainMenu = dynamic_cast(_root)) {
mainMenu->_root->RefreshCacheLevels();
}
+
+ std::uint32_t filesRemoved = RenderResources::binaryShaderCache().prune();
+ LOGI("Pruning binary shader cache (removed %u files)...", filesRemoved);
+
_done = true;
#endif
}
diff --git a/Sources/Jazz2/UI/RgbLights.cpp b/Sources/Jazz2/UI/RgbLights.cpp
index e4f0b742..555fd51a 100644
--- a/Sources/Jazz2/UI/RgbLights.cpp
+++ b/Sources/Jazz2/UI/RgbLights.cpp
@@ -242,7 +242,7 @@ namespace Jazz2::UI
if (result) {
LOGE("Response failed: %i", result);
} else {
- RgbLights* _this = reinterpret_cast(userData);
+ RgbLights* _this = static_cast(userData);
_this->_isConnected = true;
}
return EM_TRUE;
@@ -250,7 +250,7 @@ namespace Jazz2::UI
EM_BOOL RgbLights::emscriptenOnError(std::int32_t eventType, const EmscriptenWebSocketErrorEvent* websocketEvent, void* userData)
{
- RgbLights* _this = reinterpret_cast(userData);
+ RgbLights* _this = static_cast(userData);
_this->_isConnected = false;
if (_this->_ws != NULL) {
@@ -263,7 +263,7 @@ namespace Jazz2::UI
EM_BOOL RgbLights::emscriptenOnClose(std::int32_t eventType, const EmscriptenWebSocketCloseEvent* websocketEvent, void* userData)
{
- RgbLights* _this = reinterpret_cast(userData);
+ RgbLights* _this = static_cast(userData);
_this->_isConnected = false;
if (_this->_ws != NULL) {
diff --git a/Sources/Shared/Base/Unaligned.h b/Sources/Shared/Base/Unaligned.h
new file mode 100644
index 00000000..1c5e8986
--- /dev/null
+++ b/Sources/Shared/Base/Unaligned.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "../Common.h"
+
+#include
+
+namespace Death
+{
+ /**
+ @brief Provides unaligned operations for pointers
+ */
+ class Unaligned
+ {
+ public:
+ static std::uint16_t Load16(const void* p);
+ static std::uint32_t Load32(const void* p);
+ static std::uint64_t Load64(const void* p);
+
+ static void Store16(void* p, std::uint16_t v);
+ static void Store32(void* p, std::uint32_t v);
+ static void Store64(void* p, std::uint64_t v);
+
+ private:
+ Unaligned() = delete;
+ };
+
+ DEATH_ALWAYS_INLINE std::uint16_t Unaligned::Load16(const void* p)
+ {
+ std::uint16_t v;
+ std::memcpy(&v, p, sizeof(v));
+ return v;
+ }
+
+ DEATH_ALWAYS_INLINE std::uint32_t Unaligned::Load32(const void* p)
+ {
+ std::uint32_t v;
+ std::memcpy(&v, p, sizeof(v));
+ return v;
+ }
+
+ DEATH_ALWAYS_INLINE std::uint64_t Unaligned::Load64(const void* p)
+ {
+ std::uint64_t v;
+ std::memcpy(&v, p, sizeof(v));
+ return v;
+ }
+
+ DEATH_ALWAYS_INLINE void Unaligned::Store16(void* p, std::uint16_t v)
+ {
+ std::memcpy(p, &v, sizeof(v));
+ }
+
+ DEATH_ALWAYS_INLINE void Unaligned::Store32(void* p, std::uint32_t v)
+ {
+ std::memcpy(p, &v, sizeof(v));
+ }
+
+ DEATH_ALWAYS_INLINE void Unaligned::Store64(void* p, std::uint64_t v)
+ {
+ std::memcpy(p, &v, sizeof(v));
+ }
+}
\ No newline at end of file
diff --git a/Sources/nCine/Audio/AudioReaderMpt.cpp b/Sources/nCine/Audio/AudioReaderMpt.cpp
index e70afcf3..54831aea 100644
--- a/Sources/nCine/Audio/AudioReaderMpt.cpp
+++ b/Sources/nCine/Audio/AudioReaderMpt.cpp
@@ -144,7 +144,7 @@ namespace nCine
size_t AudioReaderMpt::stream_read_func(void* stream, void* dst, size_t bytes)
{
- AudioReaderMpt* _this = reinterpret_cast(stream);
+ AudioReaderMpt* _this = static_cast(stream);
return _this->_fileHandle->Read(dst, (unsigned long)bytes);
}
@@ -158,14 +158,14 @@ namespace nCine
case OPENMPT_STREAM_SEEK_END: origin = SeekOrigin::End; break;
}
- AudioReaderMpt* _this = reinterpret_cast(stream);
+ AudioReaderMpt* _this = static_cast(stream);
_this->_fileHandle->Seek((int32_t)offset, origin);
return 0;
}
int64_t AudioReaderMpt::stream_tell_func(void* stream)
{
- AudioReaderMpt* _this = reinterpret_cast(stream);
+ AudioReaderMpt* _this = static_cast(stream);
return _this->_fileHandle->GetPosition();
}
diff --git a/Sources/nCine/Backends/GlfwInputManager.cpp b/Sources/nCine/Backends/GlfwInputManager.cpp
index 18086812..75845d02 100644
--- a/Sources/nCine/Backends/GlfwInputManager.cpp
+++ b/Sources/nCine/Backends/GlfwInputManager.cpp
@@ -416,7 +416,7 @@ namespace nCine
#ifdef DEATH_TARGET_EMSCRIPTEN
EM_BOOL GlfwInputManager::emscriptenHandleTouch(int eventType, const EmscriptenTouchEvent* event, void* userData)
{
- GlfwInputManager* inputManager = reinterpret_cast(userData);
+ GlfwInputManager* inputManager = static_cast(userData);
double cssWidth = 0.0;
double cssHeight = 0.0;
diff --git a/Sources/nCine/Base/Clock.cpp b/Sources/nCine/Base/Clock.cpp
index 6d4ba822..43b88df7 100644
--- a/Sources/nCine/Base/Clock.cpp
+++ b/Sources/nCine/Base/Clock.cpp
@@ -27,7 +27,9 @@ namespace nCine
: frequency_(0UL), baseCount_(0ULL)
{
#if defined(DEATH_TARGET_WINDOWS)
- if (::QueryPerformanceFrequency(reinterpret_cast(&frequency_))) {
+ LARGE_INTEGER li;
+ if (::QueryPerformanceFrequency(&li)) {
+ frequency_ = li.LowPart;
hasPerfCounter_ = true;
} else {
frequency_ = 1000L;
@@ -38,7 +40,6 @@ namespace nCine
# else
mach_timebase_info_data_t info;
mach_timebase_info(&info);
-
frequency_ = (info.denom * 1.0e9L) / info.numer;
# endif
#else
diff --git a/Sources/nCine/Base/HashFunctions.cpp b/Sources/nCine/Base/HashFunctions.cpp
index dddbbca0..db09b017 100644
--- a/Sources/nCine/Base/HashFunctions.cpp
+++ b/Sources/nCine/Base/HashFunctions.cpp
@@ -1,5 +1,11 @@
#include "HashFunctions.h"
+#include
+
+#if defined(DEATH_TARGET_APPLE)
+# ínclude
+#endif
+
namespace nCine
{
// Compression function for Merkle-Damgard construction.
@@ -50,4 +56,366 @@ namespace nCine
uint64_t h = fasthash64(buf, len, seed);
return static_cast(h - (h >> 32));
}
+
+ // CityHash
+ inline std::uint32_t ByteSwap32(std::uint32_t value)
+ {
+#if defined(DEATH_TARGET_MSVC)
+ return _byteswap_ulong(value);
+#elif defined(DEATH_TARGET_APPLE)
+ return OSSwapInt32(value);
+#elif __has_builtin(__builtin_bswap32) || defined(DEATH_TARGET_GCC)
+ return __builtin_bswap32(value);
+#else
+ return (((value & std::uint32_t{0xFF}) << 24) |
+ ((value & std::uint32_t{0xFF00}) << 8) |
+ ((value & std::uint32_t{0xFF0000}) >> 8) |
+ ((value & std::uint32_t{0xFF000000}) >> 24));
+#endif
+ }
+
+ inline std::uint64_t ByteSwap64(std::uint64_t value)
+ {
+#if defined(DEATH_TARGET_MSVC)
+ return _byteswap_uint64(value);
+#elif defined(DEATH_TARGET_APPLE)
+ return OSSwapInt64(value);
+#elif __has_builtin(__builtin_bswap64) || defined(DEATH_TARGET_GCC)
+ return __builtin_bswap64(value);
+#else
+ return (((value & std::uint64_t{0xFF}) << 56) |
+ ((value & std::uint64_t{0xFF00}) << 40) |
+ ((value & std::uint64_t{0xFF0000}) << 24) |
+ ((value & std::uint64_t{0xFF000000}) << 8) |
+ ((value & std::uint64_t{0xFF00000000}) >> 8) |
+ ((value & std::uint64_t{0xFF0000000000}) >> 24) |
+ ((value & std::uint64_t{0xFF000000000000}) >> 40) |
+ ((value & std::uint64_t{0xFF00000000000000}) >> 56));
+#endif
+ }
+
+#if defined(DEATH_TARGET_BIG_ENDIAN)
+# define uint32_in_expected_order(x) (ByteSwap32(x))
+# define uint64_in_expected_order(x) (ByteSwap64(x))
+#else
+# define uint32_in_expected_order(x) (x)
+# define uint64_in_expected_order(x) (x)
+#endif
+
+ static std::uint64_t Fetch64(const char* p)
+ {
+ using Death::Unaligned;
+ return uint64_in_expected_order(Unaligned::Load64(p));
+ }
+
+ static std::uint32_t Fetch32(const char* p)
+ {
+ using Death::Unaligned;
+ return uint32_in_expected_order(Unaligned::Load32(p));
+ }
+
+ // Some primes between 2^63 and 2^64 for various uses.
+ static const std::uint64_t k0 = 0xc3a5c85c97cb3127ULL;
+ static const std::uint64_t k1 = 0xb492b66fbe98f273ULL;
+ static const std::uint64_t k2 = 0x9ae16a3b2f90404fULL;
+
+ // Magic numbers for 32-bit hashing. Copied from Murmur3.
+ static const std::uint32_t c1 = 0xcc9e2d51;
+ static const std::uint32_t c2 = 0x1b873593;
+
+ // A 32-bit to 32-bit integer hash copied from Murmur3.
+ static std::uint32_t fmix(std::uint32_t h)
+ {
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+ }
+
+ static std::uint32_t Rotate32(std::uint32_t val, std::int32_t shift)
+ {
+ // Avoid shifting by 32: doing so yields an undefined result.
+ return (shift == 0 ? val : ((val >> shift) | (val << (32 - shift))));
+ }
+
+#undef PERMUTE3
+#define PERMUTE3(a, b, c) \
+ do { \
+ std::swap(a, b); \
+ std::swap(a, c); \
+ } while (0)
+
+ static std::uint32_t Mur(std::uint32_t a, std::uint32_t h)
+ {
+ // Helper from Murmur3 for combining two 32-bit values.
+ a *= c1;
+ a = Rotate32(a, 17);
+ a *= c2;
+ h ^= a;
+ h = Rotate32(h, 19);
+ return h * 5 + 0xe6546b64;
+ }
+
+ static std::uint32_t Hash32Len13to24(const char* s, std::size_t len)
+ {
+ std::uint32_t a = Fetch32(s - 4 + (len >> 1));
+ std::uint32_t b = Fetch32(s + 4);
+ std::uint32_t c = Fetch32(s + len - 8);
+ std::uint32_t d = Fetch32(s + (len >> 1));
+ std::uint32_t e = Fetch32(s);
+ std::uint32_t f = Fetch32(s + len - 4);
+ std::uint32_t h = static_cast(len);
+ return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
+ }
+
+ static std::uint32_t Hash32Len0to4(const char* s, std::size_t len)
+ {
+ std::uint32_t b = 0;
+ std::uint32_t c = 9;
+ for (std::size_t i = 0; i < len; i++) {
+ signed char v = static_cast(s[i]);
+ b = b * c1 + static_cast(v);
+ c ^= b;
+ }
+ return fmix(Mur(b, Mur(static_cast(len), c)));
+ }
+
+ static std::uint32_t Hash32Len5to12(const char* s, std::size_t len)
+ {
+ std::uint32_t a = static_cast(len), b = a * 5, c = 9, d = b;
+ a += Fetch32(s);
+ b += Fetch32(s + len - 4);
+ c += Fetch32(s + ((len >> 1) & 4));
+ return fmix(Mur(c, Mur(b, Mur(a, d))));
+ }
+
+ std::uint32_t CityHash32(const char* s, std::size_t len)
+ {
+ if (len <= 24) {
+ return len <= 12
+ ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len))
+ : Hash32Len13to24(s, len);
+ }
+
+ // len > 24
+ std::uint32_t h = static_cast(len), g = c1 * h, f = g;
+
+ std::uint32_t a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
+ std::uint32_t a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
+ std::uint32_t a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
+ std::uint32_t a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
+ std::uint32_t a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
+ h ^= a0;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ h ^= a2;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a1;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ g ^= a3;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ f += a4;
+ f = Rotate32(f, 19);
+ f = f * 5 + 0xe6546b64;
+ std::size_t iters = (len - 1) / 20;
+ do {
+ std::uint32_t b0 = Rotate32(Fetch32(s) * c1, 17) * c2;
+ std::uint32_t b1 = Fetch32(s + 4);
+ std::uint32_t b2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
+ std::uint32_t b3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
+ std::uint32_t b4 = Fetch32(s + 16);
+ h ^= b0;
+ h = Rotate32(h, 18);
+ h = h * 5 + 0xe6546b64;
+ f += b1;
+ f = Rotate32(f, 19);
+ f = f * c1;
+ g += b2;
+ g = Rotate32(g, 18);
+ g = g * 5 + 0xe6546b64;
+ h ^= b3 + b1;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= b4;
+ g = ByteSwap32(g) * 5;
+ h += b4 * 5;
+ h = ByteSwap32(h);
+ f += b0;
+ PERMUTE3(f, h, g);
+ s += 20;
+ } while (--iters != 0);
+ g = Rotate32(g, 11) * c1;
+ g = Rotate32(g, 17) * c1;
+ f = Rotate32(f, 11) * c1;
+ f = Rotate32(f, 17) * c1;
+ h = Rotate32(h + g, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ h = Rotate32(h + f, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ return h;
+ }
+
+ // Bitwise right rotate. Normally this will compile to a single
+ // instruction, especially if the shift is a manifest constant.
+ static std::uint64_t Rotate(std::uint64_t val, std::int32_t shift)
+ {
+ // Avoid shifting by 64: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
+ }
+
+ static std::uint64_t ShiftMix(std::uint64_t val)
+ {
+ return val ^ (val >> 47);
+ }
+
+ static std::uint64_t HashLen16(std::uint64_t u, std::uint64_t v, std::uint64_t mul)
+ {
+ // Murmur-inspired hashing.
+ std::uint64_t a = (u ^ v) * mul;
+ a ^= (a >> 47);
+ std::uint64_t b = (v ^ a) * mul;
+ b ^= (b >> 47);
+ b *= mul;
+ return b;
+ }
+
+ static std::uint64_t HashLen16(std::uint64_t u, std::uint64_t v)
+ {
+ const std::uint64_t kMul = 0x9ddfea08eb382d69ULL;
+ return HashLen16(u, v, kMul);
+ }
+
+ static std::uint64_t HashLen0to16(const char* s, std::size_t len)
+ {
+ if (len >= 8) {
+ std::uint64_t mul = k2 + len * 2;
+ std::uint64_t a = Fetch64(s) + k2;
+ std::uint64_t b = Fetch64(s + len - 8);
+ std::uint64_t c = Rotate(b, 37) * mul + a;
+ std::uint64_t d = (Rotate(a, 25) + b) * mul;
+ return HashLen16(c, d, mul);
+ }
+ if (len >= 4) {
+ std::uint64_t mul = k2 + len * 2;
+ std::uint64_t a = Fetch32(s);
+ return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
+ }
+ if (len > 0) {
+ std::uint8_t a = static_cast(s[0]);
+ std::uint8_t b = static_cast(s[len >> 1]);
+ std::uint8_t c = static_cast(s[len - 1]);
+ std::uint32_t y = static_cast(a) + (static_cast(b) << 8);
+ std::uint32_t z = static_cast(len) + (static_cast(c) << 2);
+ return ShiftMix(y * k2 ^ z * k0) * k2;
+ }
+ return k2;
+ }
+
+ // This probably works well for 16-byte strings as well, but it may be overkill in that case.
+ static std::uint64_t HashLen17to32(const char* s, std::size_t len)
+ {
+ std::uint64_t mul = k2 + len * 2;
+ std::uint64_t a = Fetch64(s) * k1;
+ std::uint64_t b = Fetch64(s + 8);
+ std::uint64_t c = Fetch64(s + len - 8) * mul;
+ std::uint64_t d = Fetch64(s + len - 16) * k2;
+ return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
+ }
+
+ // Return a 16-byte hash for 48 bytes. Quick and dirty.
+ // Callers do best to use "random-looking" values for a and b.
+ static std::pair WeakHashLen32WithSeeds(std::uint64_t w, std::uint64_t x, std::uint64_t y, std::uint64_t z, std::uint64_t a, std::uint64_t b)
+ {
+ a += w;
+ b = Rotate(b + a + z, 21);
+ std::uint64_t c = a;
+ a += x;
+ a += y;
+ b += Rotate(a, 44);
+ return std::make_pair(a + z, b + c);
+ }
+
+ // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
+ static std::pair WeakHashLen32WithSeeds(const char* s, std::uint64_t a, std::uint64_t b)
+ {
+ return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, b);
+ }
+
+ // Return an 8-byte hash for 33 to 64 bytes.
+ static std::uint64_t HashLen33to64(const char* s, std::size_t len)
+ {
+ std::uint64_t mul = k2 + len * 2;
+ std::uint64_t a = Fetch64(s) * k2;
+ std::uint64_t b = Fetch64(s + 8);
+ std::uint64_t c = Fetch64(s + len - 24);
+ std::uint64_t d = Fetch64(s + len - 32);
+ std::uint64_t e = Fetch64(s + 16) * k2;
+ std::uint64_t f = Fetch64(s + 24) * 9;
+ std::uint64_t g = Fetch64(s + len - 8);
+ std::uint64_t h = Fetch64(s + len - 16) * mul;
+ std::uint64_t u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
+ std::uint64_t v = ((a + g) ^ d) + f + 1;
+ std::uint64_t w = ByteSwap64((u + v) * mul) + h;
+ std::uint64_t x = Rotate(e + f, 42) + c;
+ std::uint64_t y = (ByteSwap64((v + w) * mul) + g) * mul;
+ std::uint64_t z = e + f + c;
+ a = ByteSwap64((x + z) * mul + y) + b;
+ b = ShiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+ }
+
+ std::uint64_t CityHash64(const char* s, std::size_t len)
+ {
+ if (len <= 32) {
+ if (len <= 16) {
+ return HashLen0to16(s, len);
+ } else {
+ return HashLen17to32(s, len);
+ }
+ } else if (len <= 64) {
+ return HashLen33to64(s, len);
+ }
+
+ // For strings over 64 bytes we hash the end first, and then as we
+ // loop we keep 56 bytes of state: v, w, x, y, and z.
+ std::uint64_t x = Fetch64(s + len - 40);
+ std::uint64_t y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
+ std::uint64_t z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
+ std::pair v = WeakHashLen32WithSeeds(s + len - 64, len, z);
+ std::pair w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
+ x = x * k1 + Fetch64(s);
+
+ // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+ len = (len - 1) & ~static_cast(63);
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 64;
+ } while (len != 0);
+ return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, HashLen16(v.second, w.second) + x);
+ }
+
+ std::uint64_t CityHash64WithSeeds(const char* s, std::size_t len, std::uint64_t seed0, uint64_t seed1)
+ {
+ return HashLen16(CityHash64(s, len) - seed0, seed1);
+ }
+
+ std::uint64_t CityHash64WithSeed(const char* s, std::size_t len, std::uint64_t seed)
+ {
+ return CityHash64WithSeeds(s, len, k2, seed);
+ }
}
\ No newline at end of file
diff --git a/Sources/nCine/Base/HashFunctions.h b/Sources/nCine/Base/HashFunctions.h
index 263a1fce..b4999b56 100644
--- a/Sources/nCine/Base/HashFunctions.h
+++ b/Sources/nCine/Base/HashFunctions.h
@@ -11,10 +11,11 @@ using namespace Death::Containers;
namespace nCine
{
using hash_t = uint32_t;
+ using hash64_t = uint64_t;
const hash_t NullHash = static_cast(~0);
/// Hash function returning always the first hashmap bucket, for debug purposes
- template
+ template
class FixedHashFunc
{
public:
@@ -24,7 +25,7 @@ namespace nCine
};
/// Hash function returning the modulo of the key, for debug purposes
- template
+ template
class ModuloHashFunc
{
public:
@@ -35,7 +36,7 @@ namespace nCine
/// Hash function returning the key unchanged
/*! The key type should be convertible to `hash_t.` */
- template
+ template
class IdentityHashFunc
{
public:
@@ -45,7 +46,7 @@ namespace nCine
};
/// Shift-Add-XOR hash function
- template
+ template
class SaxHashFunc
{
public:
@@ -64,7 +65,7 @@ namespace nCine
/*!
* \note Specialized version of the function for C-style strings
*/
- template <>
+ template<>
class SaxHashFunc
{
public:
@@ -83,7 +84,7 @@ namespace nCine
/*!
* \note Specialized version of the function for String objects
*/
- template <>
+ template<>
class SaxHashFunc
{
public:
@@ -102,7 +103,7 @@ namespace nCine
/*!
* For more information: http://en.wikipedia.org/wiki/Jenkins_hash_function
*/
- template
+ template
class JenkinsHashFunc
{
public:
@@ -129,7 +130,7 @@ namespace nCine
*
* For more information: http://en.wikipedia.org/wiki/Jenkins_hash_function
*/
- template <>
+ template<>
class JenkinsHashFunc
{
public:
@@ -156,7 +157,7 @@ namespace nCine
*
* For more information: http://en.wikipedia.org/wiki/Jenkins_hash_function
*/
- template <>
+ template<>
class JenkinsHashFunc
{
public:
@@ -181,7 +182,7 @@ namespace nCine
/*!
* For more information: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
*/
- template
+ template
class FNV1aHashFunc
{
public:
@@ -210,7 +211,7 @@ namespace nCine
*
* For more information: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
*/
- template <>
+ template<>
class FNV1aHashFunc
{
public:
@@ -240,7 +241,7 @@ namespace nCine
*
* For more information: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
*/
- template <>
+ template<>
class FNV1aHashFunc
{
public:
@@ -281,7 +282,7 @@ namespace nCine
/*!
* For more information: https://github.com/ztanml/fast-hash
*/
- template
+ template
class FastHashFunc
{
public:
@@ -303,7 +304,7 @@ namespace nCine
*
* For more information: https://github.com/ztanml/fast-hash
*/
- template <>
+ template<>
class FastHashFunc
{
public:
@@ -322,7 +323,7 @@ namespace nCine
*
* For more information: https://github.com/ztanml/fast-hash
*/
- template <>
+ template<>
class FastHashFunc
{
public:
@@ -334,4 +335,53 @@ namespace nCine
private:
static const uint32_t Seed = 0x811C9DC5;
};
+
+ /// CityHash
+ std::uint64_t CityHash64(const char* s, std::size_t len);
+
+ std::uint64_t CityHash64WithSeed(const char* s, std::size_t len, std::uint64_t seed);
+
+ std::uint64_t CityHash64WithSeeds(const char* s, std::size_t len, std::uint64_t seed0, std::uint64_t seed1);
+
+ std::uint32_t CityHash32(const char* s, std::size_t len);
+
+ template
+ class CityHash32Func
+ {
+ public:
+ hash_t operator()(const K& key) const
+ {
+ return CityHash32(reinterpret_cast(&key), sizeof(K));
+ }
+ };
+
+ template<>
+ class CityHash32Func
+ {
+ public:
+ hash_t operator()(const String& string) const
+ {
+ return CityHash32(string.data(), string.size());
+ }
+ };
+
+ template
+ class CityHash64Func
+ {
+ public:
+ hash64_t operator()(const K& key) const
+ {
+ return CityHash64(reinterpret_cast(&key), sizeof(K));
+ }
+ };
+
+ template<>
+ class CityHash64Func
+ {
+ public:
+ hash64_t operator()(const String& string) const
+ {
+ return CityHash64(string.data(), string.size());
+ }
+ };
}
\ No newline at end of file
diff --git a/Sources/nCine/Base/HashMap.h b/Sources/nCine/Base/HashMap.h
index eee06285..7c9c1df3 100644
--- a/Sources/nCine/Base/HashMap.h
+++ b/Sources/nCine/Base/HashMap.h
@@ -5,9 +5,13 @@
namespace nCine
{
- // Use flat_hash_map from Parallel Hashmap library
+ // Use `flat_hash_map` from Parallel Hashmap library
template ,
+#if defined(DEATH_TARGET_32BIT)
+ class Hash = CityHash32Func,
+#else
+ class Hash = CityHash64Func,
+#endif
class Eq = phmap::priv::hash_default_eq>
using HashMap = phmap::flat_hash_map;
}
diff --git a/Sources/nCine/Graphics/BinaryShaderCache.cpp b/Sources/nCine/Graphics/BinaryShaderCache.cpp
index ce7b6447..25cb2548 100644
--- a/Sources/nCine/Graphics/BinaryShaderCache.cpp
+++ b/Sources/nCine/Graphics/BinaryShaderCache.cpp
@@ -15,8 +15,6 @@ namespace nCine
{
namespace
{
- constexpr uint64_t HashSeed = 0x01000193811C9DC5;
-
std::int32_t bufferSize = 0;
std::unique_ptr bufferPtr;
}
@@ -56,17 +54,12 @@ namespace nCine
const IGfxCapabilities::GlInfoStrings& infoStrings = gfxCaps.glInfoStrings();
- // For a stable hash, the OpenGL strings need to be copied so that padding bytes can be set to zero
- char platformString[512];
- std::memset(platformString, 0, sizeof(platformString));
- int platformStringLength = copyStringFirst(platformString, infoStrings.renderer);
- platformHash_ += fasthash64(platformString, platformStringLength, HashSeed);
-
- std::memset(platformString, 0, sizeof(platformString));
- platformStringLength = copyStringFirst(platformString, infoStrings.glVersion);
- platformHash_ += fasthash64(platformString, platformStringLength, HashSeed);
+ platformHash_ += CityHash64(infoStrings.renderer, strlen(infoStrings.renderer));
+ platformHash_ += CityHash64(infoStrings.glVersion, strlen(infoStrings.glVersion));
- path_ = path;
+ char platformHashString[24];
+ std::int32_t platformHashLength = formatString(platformHashString, sizeof(platformHashString), "%016llx", platformHash_);
+ path_ = fs::CombinePath(path, { platformHashString, (std::size_t)platformHashLength });
fs::CreateDirectories(path_);
bufferSize = 64 * 1024;
@@ -78,20 +71,15 @@ namespace nCine
String BinaryShaderCache::getCachedShaderPath(const char* shaderName)
{
- if (!isAvailable_ || shaderName == nullptr) {
- return { };
- }
-
- std::size_t shaderNameLength = strlen(shaderName);
- if (shaderNameLength == 0) {
+ if (!isAvailable_ || shaderName == nullptr || shaderName[0] == '\0') {
return { };
}
- std::uint64_t shaderNameHash = fasthash64(shaderName, shaderNameLength, 0x01000193811C9DC5);
+ std::uint64_t shaderNameHash = CityHash64(shaderName, strlen(shaderName));
- char outputBuffer[48];
- formatString(outputBuffer, sizeof(outputBuffer), "%016llx%016llx.shader", shaderNameHash, platformHash_);
- return fs::CombinePath(path_, outputBuffer);
+ char filename[32];
+ std::int32_t filenameLength = formatString(filename, sizeof(filename), "%016llx.shader", shaderNameHash);
+ return fs::CombinePath(path_, { filename, (std::size_t)filenameLength });
}
bool BinaryShaderCache::loadFromCache(const char* shaderName, std::uint64_t shaderVersion, GLShaderProgram* program, GLShaderProgram::Introspection introspection)
@@ -179,28 +167,18 @@ namespace nCine
std::uint32_t BinaryShaderCache::prune()
{
+ auto platformHashString = fs::GetFileName(path_);
+
std::uint32_t filesRemoved = 0;
- fs::Directory dir(path_);
+ fs::Directory dir(fs::GetDirectoryName(path_));
while (const StringView shaderPath = dir.GetNext()) {
- if (fs::GetExtension(shaderPath) != "shader"_s) {
- continue;
- }
-
- StringView filename = fs::GetFileNameWithoutExtension(shaderPath);
-
- bool shouldRemove;
- if (filename.size() != 32) {
- shouldRemove = true;
- } else {
- char componentString[17];
- std::memcpy(componentString, &filename[16], 16);
- componentString[16] = '\0';
-
- std::uint64_t platformHash = strtoull(componentString, nullptr, 16);
- shouldRemove = (platformHash != platformHash_);
- }
-
- if (shouldRemove) {
+ if (fs::DirectoryExists(shaderPath)) {
+ StringView filename = fs::GetFileName(shaderPath);
+ if (filename != platformHashString) {
+ fs::RemoveDirectoryRecursive(shaderPath);
+ filesRemoved++;
+ }
+ } else if (fs::GetExtension(shaderPath) == "shader"_s) {
fs::RemoveFile(shaderPath);
filesRemoved++;
}
@@ -209,20 +187,11 @@ namespace nCine
return filesRemoved;
}
- std::uint32_t BinaryShaderCache::clear()
+ bool BinaryShaderCache::clear()
{
- std::uint32_t filesRemoved = 0;
- fs::Directory dir(path_);
- while (const StringView shaderPath = dir.GetNext()) {
- if (fs::GetExtension(shaderPath) != "shader"_s) {
- continue;
- }
-
- fs::RemoveFile(shaderPath);
- filesRemoved++;
- }
-
- return filesRemoved;
+ bool success = fs::RemoveDirectoryRecursive(path_);
+ fs::CreateDirectories(path_);
+ return success;
}
bool BinaryShaderCache::setPath(const StringView& path)
diff --git a/Sources/nCine/Graphics/BinaryShaderCache.h b/Sources/nCine/Graphics/BinaryShaderCache.h
index f22bb84d..77c13bcc 100644
--- a/Sources/nCine/Graphics/BinaryShaderCache.h
+++ b/Sources/nCine/Graphics/BinaryShaderCache.h
@@ -37,7 +37,7 @@ namespace nCine
/// Deletes all binary shaders that not belong to this platform from the cache directory
std::uint32_t prune();
/// Deletes all binary shaders from the cache directory
- std::uint32_t clear();
+ bool clear();
/// Returns the current cache directory for binary shaders
inline const StringView path() {
diff --git a/Sources/nCine/Graphics/IGfxDevice.cpp b/Sources/nCine/Graphics/IGfxDevice.cpp
index 664a7fd2..f4579c6d 100644
--- a/Sources/nCine/Graphics/IGfxDevice.cpp
+++ b/Sources/nCine/Graphics/IGfxDevice.cpp
@@ -28,7 +28,7 @@ namespace nCine
# endif
if (event->windowInnerWidth > 0 && event->windowInnerHeight > 0) {
float pixelRatio = emscripten_get_device_pixel_ratio();
- IGfxDevice* gfxDevice = reinterpret_cast(userData);
+ IGfxDevice* gfxDevice = static_cast(userData);
gfxDevice->setResolutionInternal(static_cast(event->windowInnerWidth * pixelRatio), static_cast(event->windowInnerHeight * pixelRatio));
}
return 1;
@@ -43,7 +43,7 @@ namespace nCine
float pixelRatio2 = emscripten_get_device_pixel_ratio();
LOGI("Canvas was resized to %ix%i (canvas size is %ix%i; ratio is %f)", (int)(event->elementWidth * pixelRatio2), (int)(event->elementHeight * pixelRatio2), (int)cssWidth, (int)cssHeight, pixelRatio2);
# endif
- IGfxDevice* gfxDevice = reinterpret_cast(userData);
+ IGfxDevice* gfxDevice = static_cast(userData);
gfxDevice->isFullscreen_ = event->isFullscreen;
if (event->elementWidth > 0 && event->elementHeight > 0) {
diff --git a/Sources/nCine/Graphics/RenderBatcher.cpp b/Sources/nCine/Graphics/RenderBatcher.cpp
index c17d87d6..f4f6da40 100644
--- a/Sources/nCine/Graphics/RenderBatcher.cpp
+++ b/Sources/nCine/Graphics/RenderBatcher.cpp
@@ -170,9 +170,9 @@ namespace nCine
batchingWithIndices = true;
}
- // Don't request more bytes than a UBO can hold
+ // Don't request more bytes than an instances block can hold (also protects against big `RenderingSettings::maxBatchSize` values)
const unsigned long currentSize = nonBlockUniformsSize + nonInstancesBlocksSize + instancesBlockSize;
- if (currentSize + singleInstanceBlockSize > UboMaxSize) {
+ if (currentSize + singleInstanceBlockSize > instancesBlock->size()) {
break;
}
diff --git a/Sources/nCine/Graphics/RenderCommand.cpp b/Sources/nCine/Graphics/RenderCommand.cpp
index 3e577307..d51de153 100644
--- a/Sources/nCine/Graphics/RenderCommand.cpp
+++ b/Sources/nCine/Graphics/RenderCommand.cpp
@@ -23,7 +23,7 @@ namespace nCine
{
const uint64_t upper = static_cast(layerSortKey()) << 32;
const uint32_t lower = material_.sortKey();
- materialSortKey_ = upper + lower;
+ materialSortKey_ = upper | lower;
}
void RenderCommand::issue()
diff --git a/Sources/nCine/MainApplication.cpp b/Sources/nCine/MainApplication.cpp
index 29fd4c55..e2669d06 100644
--- a/Sources/nCine/MainApplication.cpp
+++ b/Sources/nCine/MainApplication.cpp
@@ -447,7 +447,7 @@ namespace nCine
#if defined(DEATH_TARGET_EMSCRIPTEN)
void MainApplication::emscriptenStep()
{
- reinterpret_cast(theApplication()).run();
+ static_cast(theApplication()).run();
}
#endif
}
diff --git a/cmake/ncine_headers.cmake b/cmake/ncine_headers.cmake
index ba4b7401..a5a6bd56 100644
--- a/cmake/ncine_headers.cmake
+++ b/cmake/ncine_headers.cmake
@@ -11,6 +11,7 @@ set(HEADERS
${NCINE_SOURCE_DIR}/Shared/IntrinsicsSse4.h
${NCINE_SOURCE_DIR}/Shared/IntrinsicsSsse3.h
${NCINE_SOURCE_DIR}/Shared/Utf8.h
+ ${NCINE_SOURCE_DIR}/Shared/Base/Unaligned.h
${NCINE_SOURCE_DIR}/Shared/Containers/Array.h
${NCINE_SOURCE_DIR}/Shared/Containers/ArrayView.h
${NCINE_SOURCE_DIR}/Shared/Containers/DateTime.h