From 8d42188d80c8e58277791f4d5167184af14911f8 Mon Sep 17 00:00:00 2001 From: Pursche Date: Mon, 10 Aug 2026 04:16:08 +0200 Subject: [PATCH 1/4] Fix Linux/clang build and runtime issues Compile fixes for clang (MSVC-isms): - offsetof(Type, Type::member) -> offsetof(Type, member) in CulledRenderer, JoltDebugRenderer, ModelRenderer (standard offsetof takes a member designator; the qualified form is an MSVC extension) - LightRenderer.h: add missing RenderGraphBuilder forward declaration - UpdateUnitEntities.cpp: fix tracy include case (tracy/Tracy.hpp); only resolved on case-insensitive filesystems Runtime fixes: - Application.cpp: open PACT storage at Data/Pact (was data/pact, which only worked on case-insensitive filesystems; with FallbackToInit the game silently created and read an empty archive on Linux) - FactionUtil.cpp: Reaction perception override was unreachable without a local player set; return the override before the local-player early-out (matches the override-is-absolute semantics the tests expect) Shader fixes: - Model/Terrain Draw.vs.slang: SV_ClipDistance must be float or float[] (slang 2026.13 enforces this); use float[4] with element-wise writes Build system fixes: - ProjectUtil.lua: linkgroups + $ORIGIN rpath on Linux, fix executable extension map (no extension on Linux) - ShaderCookerStandalone.lua: cook shaders as a post-build step on non-Windows; premake's gmake backend generates no build target for Utility projects, so the Shaders project's prebuild never ran - Shaders.lua: adapt cooker invocation to the extension-map fix - Submodules/Engine: bump to LinuxBuildFix (Linux/clang build and runtime fixes) --- Premake/ProjectUtil.lua | 9 +++++++-- .../Game-Lib/Game-Lib/Application/Application.cpp | 2 +- .../Game-Lib/ECS/Systems/UpdateUnitEntities.cpp | 2 +- Source/Game-Lib/Game-Lib/ECS/Util/FactionUtil.cpp | 15 ++++++++------- .../Game-Lib/Rendering/CulledRenderer.cpp | 2 +- .../Rendering/Debug/JoltDebugRenderer.cpp | 4 ++-- .../Game-Lib/Rendering/Light/LightRenderer.h | 2 +- .../Game-Lib/Rendering/Model/ModelRenderer.cpp | 6 +++--- .../ShaderCookerStandalone.lua | 9 +++++++++ Source/Shaders/Shaders.lua | 2 +- Source/Shaders/Shaders/Model/Draw.vs.slang | 8 ++++++-- Source/Shaders/Shaders/Terrain/Draw.vs.slang | 13 ++++++++++--- Submodules/Engine | 2 +- 13 files changed, 51 insertions(+), 25 deletions(-) diff --git a/Premake/ProjectUtil.lua b/Premake/ProjectUtil.lua index 88b4190e..6e86feb8 100644 --- a/Premake/ProjectUtil.lua +++ b/Premake/ProjectUtil.lua @@ -44,8 +44,8 @@ end systemToExecutableExtensionMap = { - windows = "exe", - linux = "sh" + windows = ".exe", + linux = "" } systemToDynamicLibExtensionMap = @@ -237,6 +237,11 @@ Solution.Util.CreateProject = function(name, projectType, binDir, dependencies, characterset ("ASCII") editandcontinue "Off" + filter "system:linux" + linkgroups "On" + linkoptions { "-Wl,-rpath,'$$ORIGIN'" } + filter {} + filter "configurations:Debug" runtime "Debug" symbols "On" diff --git a/Source/Game-Lib/Game-Lib/Application/Application.cpp b/Source/Game-Lib/Game-Lib/Application/Application.cpp index 16f605f8..aa8b83e6 100644 --- a/Source/Game-Lib/Game-Lib/Application/Application.cpp +++ b/Source/Game-Lib/Game-Lib/Application/Application.cpp @@ -337,7 +337,7 @@ bool Application::Init(bool enableRenderDoc) ServiceLocator::SetEnttRegistries(&_registries); std::filesystem::path currentPath = std::filesystem::current_path(); - std::filesystem::path pactPath = currentPath / "data/pact"; + std::filesystem::path pactPath = currentPath / "Data/Pact"; std::filesystem::path customOverlayPath = pactPath / "data/custom"; std::filesystem::path stagingOverlayPath = pactPath / "data/staging"; diff --git a/Source/Game-Lib/Game-Lib/ECS/Systems/UpdateUnitEntities.cpp b/Source/Game-Lib/Game-Lib/ECS/Systems/UpdateUnitEntities.cpp index 0d04f94e..6539e67d 100644 --- a/Source/Game-Lib/Game-Lib/ECS/Systems/UpdateUnitEntities.cpp +++ b/Source/Game-Lib/Game-Lib/ECS/Systems/UpdateUnitEntities.cpp @@ -39,7 +39,7 @@ #include #include -#include +#include namespace ECS::Systems { diff --git a/Source/Game-Lib/Game-Lib/ECS/Util/FactionUtil.cpp b/Source/Game-Lib/Game-Lib/ECS/Util/FactionUtil.cpp index e95a4c1e..676da0d8 100644 --- a/Source/Game-Lib/Game-Lib/ECS/Util/FactionUtil.cpp +++ b/Source/Game-Lib/Game-Lib/ECS/Util/FactionUtil.cpp @@ -93,10 +93,16 @@ namespace ECS::Util::Faction Reaction Presentation(const State::FactionState& state, const Components::UnitFaction& unitFaction) { const FactionRuntimeData& runtime = *state.runtime; - if (state.localPlayerFaction == NONE_FACTION_INDEX || unitFaction.factionIndex == NONE_FACTION_INDEX) + if (unitFaction.factionIndex == NONE_FACTION_INDEX) return Reaction::Neutral; const State::PerceptionOverride* perception = FindPerception(state, unitFaction.factionIndex); + if (perception && HasPerceptionField(perception->activeFields, State::PerceptionOverrideFields::Reaction)) + return perception->effectiveReaction; + + if (state.localPlayerFaction == NONE_FACTION_INDEX) + return Reaction::Neutral; + Reaction reaction = runtime.GetRelation(unitFaction.factionIndex, state.localPlayerFaction); if (perception && HasPerceptionField(perception->activeFields, State::PerceptionOverrideFields::Standing)) @@ -113,12 +119,7 @@ namespace ECS::Util::Faction ? ReactionBounds::Unpack(unitFaction.playerReactionBounds) : ReactionBounds::Unpack(NEUTRAL_REACTION_BOUNDS); - reaction = bounds.Clamp(reaction); - - if (perception && HasPerceptionField(perception->activeFields, State::PerceptionOverrideFields::Reaction)) - reaction = perception->effectiveReaction; - - return reaction; + return bounds.Clamp(reaction); } bool HasAtWarFlag(const State::FactionState& state, FactionIndex targetFaction) diff --git a/Source/Game-Lib/Game-Lib/Rendering/CulledRenderer.cpp b/Source/Game-Lib/Game-Lib/Rendering/CulledRenderer.cpp index b8237fa1..1b2c7524 100644 --- a/Source/Game-Lib/Game-Lib/Rendering/CulledRenderer.cpp +++ b/Source/Game-Lib/Game-Lib/Rendering/CulledRenderer.cpp @@ -375,7 +375,7 @@ void CulledRenderer::RunInstancedCullingDispatch(CullingPassParams& params, bool cullConstants->numTotalInstances = numInstances; cullConstants->occlusionCull = params.occlusionCull; - u32 instanceCountOffset = params.cullingResources->IsIndexed() ? offsetof(Renderer::IndexedIndirectDraw, Renderer::IndexedIndirectDraw::instanceCount) : offsetof(Renderer::IndirectDraw, Renderer::IndirectDraw::instanceCount); + u32 instanceCountOffset = params.cullingResources->IsIndexed() ? offsetof(Renderer::IndexedIndirectDraw, instanceCount) : offsetof(Renderer::IndirectDraw, instanceCount); cullConstants->instanceCountOffset = instanceCountOffset; cullConstants->drawCallSize = params.cullingResources->IsIndexed() ? sizeof(Renderer::IndexedIndirectDraw) : sizeof(Renderer::IndirectDraw); diff --git a/Source/Game-Lib/Game-Lib/Rendering/Debug/JoltDebugRenderer.cpp b/Source/Game-Lib/Game-Lib/Rendering/Debug/JoltDebugRenderer.cpp index 77bd099a..ff961cd2 100644 --- a/Source/Game-Lib/Game-Lib/Rendering/Debug/JoltDebugRenderer.cpp +++ b/Source/Game-Lib/Game-Lib/Rendering/Debug/JoltDebugRenderer.cpp @@ -199,7 +199,7 @@ void JoltDebugRenderer::AddOccluderPass(Renderer::RenderGraph* renderGraph, Rend Draw(resources, frameIndex, graphResources, commandList, drawParams); }; - params.baseInstanceLookupOffset = offsetof(DrawCallData, DrawCallData::baseInstanceLookupOffset); + params.baseInstanceLookupOffset = offsetof(DrawCallData, baseInstanceLookupOffset); params.drawCallDataSize = sizeof(DrawCallData); params.enableDrawing = CVAR_JoltDebugDrawOccluders.Get(); @@ -272,7 +272,7 @@ void JoltDebugRenderer::AddOccluderPass(Renderer::RenderGraph* renderGraph, Rend Draw(resources, frameIndex, graphResources, commandList, drawParams); }; - params.baseInstanceLookupOffset = offsetof(DrawCallData, DrawCallData::baseInstanceLookupOffset); + params.baseInstanceLookupOffset = offsetof(DrawCallData, baseInstanceLookupOffset); params.drawCallDataSize = sizeof(DrawCallData); params.enableDrawing = CVAR_JoltDebugDrawOccluders.Get(); diff --git a/Source/Game-Lib/Game-Lib/Rendering/Light/LightRenderer.h b/Source/Game-Lib/Game-Lib/Rendering/Light/LightRenderer.h index 615813a9..3b6629b8 100644 --- a/Source/Game-Lib/Game-Lib/Rendering/Light/LightRenderer.h +++ b/Source/Game-Lib/Game-Lib/Rendering/Light/LightRenderer.h @@ -14,7 +14,7 @@ namespace Renderer { class Renderer; class RenderGraph; - + class RenderGraphBuilder; } class DebugRenderer; diff --git a/Source/Game-Lib/Game-Lib/Rendering/Model/ModelRenderer.cpp b/Source/Game-Lib/Game-Lib/Rendering/Model/ModelRenderer.cpp index 9d7be9d9..068c2846 100644 --- a/Source/Game-Lib/Game-Lib/Rendering/Model/ModelRenderer.cpp +++ b/Source/Game-Lib/Game-Lib/Rendering/Model/ModelRenderer.cpp @@ -834,7 +834,7 @@ void ModelRenderer::AddOccluderPass(Renderer::RenderGraph* renderGraph, RenderRe Draw(resources, frameIndex, graphResources, commandList, drawParams); }; - params.baseInstanceLookupOffset = offsetof(DrawCallData, DrawCallData::baseInstanceLookupOffset); + params.baseInstanceLookupOffset = offsetof(DrawCallData, baseInstanceLookupOffset); params.drawCallDataSize = sizeof(DrawCallData); params.enableDrawing = CVAR_ModelDrawOccluders.Get(); @@ -1050,7 +1050,7 @@ void ModelRenderer::AddGeometryPass(Renderer::RenderGraph* renderGraph, RenderRe Draw(resources, frameIndex, graphResources, commandList, drawParams); }; - params.baseInstanceLookupOffset = offsetof(DrawCallData, DrawCallData::baseInstanceLookupOffset); + params.baseInstanceLookupOffset = offsetof(DrawCallData, baseInstanceLookupOffset); params.drawCallDataSize = sizeof(DrawCallData); params.numShadowViews = numShadowViews; @@ -1308,7 +1308,7 @@ void ModelRenderer::AddSVSMGeometryPass(Renderer::RenderGraph* renderGraph, Rend Draw(resources, frameIndex, graphResources, commandList, drawParams); }; - params.baseInstanceLookupOffset = offsetof(DrawCallData, DrawCallData::baseInstanceLookupOffset); + params.baseInstanceLookupOffset = offsetof(DrawCallData, baseInstanceLookupOffset); params.drawCallDataSize = sizeof(DrawCallData); params.firstViewIndex = 1; diff --git a/Source/ShaderCookerStandalone/ShaderCookerStandalone.lua b/Source/ShaderCookerStandalone/ShaderCookerStandalone.lua index a9d31705..80ca48d7 100644 --- a/Source/ShaderCookerStandalone/ShaderCookerStandalone.lua +++ b/Source/ShaderCookerStandalone/ShaderCookerStandalone.lua @@ -14,6 +14,15 @@ Solution.Util.CreateConsoleApp(mod.Name, Solution.Projects.Current.BinDir, mod.D Solution.Util.SetIncludes(mod.Path) Solution.Util.SetDefines(defines) + if os.target() ~= "windows" then + -- premake's gmake backend generates no build target for Utility projects, so the + -- Shaders project's prebuild cook never runs there; cook after building the cooker instead + local shaderSourceDir = path.getabsolute(mod.Path .. "/../Shaders/Shaders") + local shaderOutputPath = (Solution.Projects.Current.BuildDir .. "/Data/Shaders") + postbuildmessage ("Compiling Shaders...") + postbuildcommands { "%{cfg.buildtarget.abspath} " .. shaderSourceDir .. " " .. shaderOutputPath } + end + vpaths { ["/*"] = { "*.lua", "*.cpp" } } diff --git a/Source/Shaders/Shaders.lua b/Source/Shaders/Shaders.lua index a995ed28..851203e0 100644 --- a/Source/Shaders/Shaders.lua +++ b/Source/Shaders/Shaders.lua @@ -16,7 +16,7 @@ Solution.Util.CreateProject(mod.Name, "Utility", Solution.Projects.Current.BinDi } Solution.Util.SetFiles(files) - local shaderCookerStandalonePath = (Solution.Projects.Current.BinDir .. "/%{cfg.buildcfg}/ShaderCookerStandalone.%{systemToExecutableExtensionMap[cfg.system]}") + local shaderCookerStandalonePath = (Solution.Projects.Current.BinDir .. "/%{cfg.buildcfg}/ShaderCookerStandalone%{systemToExecutableExtensionMap[cfg.system]}") local shaderOutputPath = (Solution.Projects.Current.BuildDir .. "/Data/Shaders") prebuildmessage ("Compiling Shaders...") diff --git a/Source/Shaders/Shaders/Model/Draw.vs.slang b/Source/Shaders/Shaders/Model/Draw.vs.slang index 0246f650..d661a485 100644 --- a/Source/Shaders/Shaders/Model/Draw.vs.slang +++ b/Source/Shaders/Shaders/Model/Draw.vs.slang @@ -39,7 +39,7 @@ struct VSOutput float4 uv01 : TEXCOORD1; #endif #if SVSM_PASS - float4 clipDistances : SV_ClipDistance; // Fixed-function culls fragments outside the draw's clip rect + float clipDistances[4] : SV_ClipDistance; // Fixed-function culls fragments outside the draw's clip rect #endif }; @@ -77,7 +77,11 @@ VSOutput main(VSInput input) output.position = mul(position, _cameras[_constants.viewIndex].worldToClip); #if SVSM_PASS - output.clipDistances = SVSMRectClipDistances(position.xyz, _constants.viewIndex - 1, _constants.svsmRectIndex, _cameras[0].eyePosition.xyz, _svsmData); + float4 clipDistances = SVSMRectClipDistances(position.xyz, _constants.viewIndex - 1, _constants.svsmRectIndex, _cameras[0].eyePosition.xyz, _svsmData); + output.clipDistances[0] = clipDistances.x; + output.clipDistances[1] = clipDistances.y; + output.clipDistances[2] = clipDistances.z; + output.clipDistances[3] = clipDistances.w; #endif #if !EDITOR_PASS diff --git a/Source/Shaders/Shaders/Terrain/Draw.vs.slang b/Source/Shaders/Shaders/Terrain/Draw.vs.slang index ff1978d1..ad36f1b4 100644 --- a/Source/Shaders/Shaders/Terrain/Draw.vs.slang +++ b/Source/Shaders/Shaders/Terrain/Draw.vs.slang @@ -36,7 +36,7 @@ struct VSOutput uint instanceID : TEXCOORD0; #endif #if SVSM_PASS - float4 clipDistances : SV_ClipDistance; // Fixed-function culls fragments outside the draw's clip rect + float clipDistances[4] : SV_ClipDistance; // Fixed-function culls fragments outside the draw's clip rect #endif }; @@ -53,7 +53,10 @@ VSOutput main(VSInput input) const float NaN = asfloat(0b01111111100000000000000000000000); output.position = float4(NaN, NaN, NaN, NaN); #if SVSM_PASS - output.clipDistances = float4(-1.0f, -1.0f, -1.0f, -1.0f); + output.clipDistances[0] = -1.0f; + output.clipDistances[1] = -1.0f; + output.clipDistances[2] = -1.0f; + output.clipDistances[3] = -1.0f; #endif return output; } @@ -70,7 +73,11 @@ VSOutput main(VSInput input) output.instanceID = instanceData.globalCellID; #endif #if SVSM_PASS - output.clipDistances = SVSMRectClipDistances(vertex.position, _constants.viewIndex - 1, _constants.svsmRectIndex, _cameras[0].eyePosition.xyz, _svsmData); + float4 clipDistances = SVSMRectClipDistances(vertex.position, _constants.viewIndex - 1, _constants.svsmRectIndex, _cameras[0].eyePosition.xyz, _svsmData); + output.clipDistances[0] = clipDistances.x; + output.clipDistances[1] = clipDistances.y; + output.clipDistances[2] = clipDistances.z; + output.clipDistances[3] = clipDistances.w; #endif return output; diff --git a/Submodules/Engine b/Submodules/Engine index 398a2719..5fc1e3c6 160000 --- a/Submodules/Engine +++ b/Submodules/Engine @@ -1 +1 @@ -Subproject commit 398a2719bc361c7a25b16b6d74b2673b7ecf349f +Subproject commit 5fc1e3c63db8e82e4a5e5fa9dd39fc462336802c From 4f1857ab29b859b4e0f13b87d34687378bbe2074 Mon Sep 17 00:00:00 2001 From: Pursche Date: Mon, 10 Aug 2026 04:24:04 +0200 Subject: [PATCH 2/4] Read automation test counters as numbers, not integers Luau integers in the Engine fork are a distinct 64-bit language type with strict host-side reads, and the interpreter does not support arithmetic on them (only native codegen does) - so script counters incremented with += are correctly plain Lua numbers. The test harness read those globals through the strict integer API, which yields 0 for non-integer-tagged values; read them as numbers and cast host-side instead. Also bumps Submodules/Engine to the LinuxBuildFix commit reverting the temporary double fallback in Zenith::ToInteger, restoring the intended strict integer read semantics. --- Source/Game-Tests/Game-Tests/ScriptingAutomationTests.cpp | 4 +++- Submodules/Engine | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Game-Tests/Game-Tests/ScriptingAutomationTests.cpp b/Source/Game-Tests/Game-Tests/ScriptingAutomationTests.cpp index c0ddee09..c00fd656 100644 --- a/Source/Game-Tests/Game-Tests/ScriptingAutomationTests.cpp +++ b/Source/Game-Tests/Game-Tests/ScriptingAutomationTests.cpp @@ -62,8 +62,10 @@ namespace i32 GetGlobalInteger(const char* name) { + // The counters are plain Lua numbers; Luau integers are a distinct type whose + // reads are strict, so read as number and cast host-side _zenith->GetGlobalKey(name); - const i32 value = _zenith->Get(-1); + const i32 value = static_cast(_zenith->Get(-1)); _zenith->Pop(); return value; } diff --git a/Submodules/Engine b/Submodules/Engine index 5fc1e3c6..d7b92900 160000 --- a/Submodules/Engine +++ b/Submodules/Engine @@ -1 +1 @@ -Subproject commit 5fc1e3c63db8e82e4a5e5fa9dd39fc462336802c +Subproject commit d7b929007983299f673341d14f5b7da8ea6bd4be From d49f2f25a0790db61cdea0e1dddce6674ea67d9a Mon Sep 17 00:00:00 2001 From: Pursche Date: Tue, 11 Aug 2026 01:44:53 +0200 Subject: [PATCH 3/4] Bump Engine to master (Linux build fixes merged upstream) --- Submodules/Engine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Submodules/Engine b/Submodules/Engine index d7b92900..2a8e5921 160000 --- a/Submodules/Engine +++ b/Submodules/Engine @@ -1 +1 @@ -Subproject commit d7b929007983299f673341d14f5b7da8ea6bd4be +Subproject commit 2a8e59210d68ebd3d4dc57c32a4aed47ca66429d From a031e8c26da713d0f24107401b96803621b9f123 Mon Sep 17 00:00:00 2001 From: Pursche Date: Tue, 11 Aug 2026 03:04:13 +0200 Subject: [PATCH 4/4] Bump Engine: ShaderCooker slang session leak fix --- Submodules/Engine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Submodules/Engine b/Submodules/Engine index 2a8e5921..f5163546 160000 --- a/Submodules/Engine +++ b/Submodules/Engine @@ -1 +1 @@ -Subproject commit 2a8e59210d68ebd3d4dc57c32a4aed47ca66429d +Subproject commit f51635463c15693abdd2d08da1bbf1e096d7b3c8