From a2fe2e88abbe0c855772dc8a84bd2e1a8f283093 Mon Sep 17 00:00:00 2001 From: mo Date: Fri, 17 Jul 2026 20:55:49 +0100 Subject: [PATCH] fix: IW3 ensure `loadForRenderer` is set for shaders (#907) * fix: IW3 restore renderer loading for disk shaders * fix: IW3 ensure `loadForRenderer` is set for shaders * fix: use modtools linker logic for iw3 loadForRenderer shader values --------- Co-authored-by: Jan Laupetin --- .../Techset/TechsetCompiler.cpp.template | 31 +++++++++++++++++++ .../IW3/Techset/TechsetCompilerIW3Test.cpp | 19 ++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/ObjCompiling/Techset/TechsetCompiler.cpp.template b/src/ObjCompiling/Techset/TechsetCompiler.cpp.template index 25b26872..999516fb 100644 --- a/src/ObjCompiling/Techset/TechsetCompiler.cpp.template +++ b/src/ObjCompiling/Techset/TechsetCompiler.cpp.template @@ -69,6 +69,28 @@ namespace return static_cast(0); } +#if defined(FEATURE_IW3) + uint16_t GetLoadForRenderer(const std::string& techniqueSetName) + { + if (techniqueSetName.starts_with("sm2/")) + return 0; + return 1; + } + + void ApplyLoadForRendererToTechnique(const MaterialTechnique& technique, const uint16_t loadForRenderer) + { + for (auto passIndex = 0u; passIndex < technique.passCount; passIndex++) + { + auto* vertexShader = technique.passArray[passIndex].vertexShader; + if (vertexShader) + vertexShader->prog.loadDef.loadForRenderer = std::max(vertexShader->prog.loadDef.loadForRenderer, loadForRenderer); + auto* pixelShader = technique.passArray[passIndex].pixelShader; + if (pixelShader) + pixelShader->prog.loadDef.loadForRenderer = std::max(pixelShader->prog.loadDef.loadForRenderer, loadForRenderer); + } + } +#endif + #if defined(FEATURE_T6) MaterialType GetMaterialType(const std::string& name) { @@ -116,6 +138,9 @@ namespace return failure ? AssetCreationResult::Failure() : AssetCreationResult::NoAction(); auto* techset = ConvertTechniqueSet(*commonTechset, m_memory); +#if defined(FEATURE_IW3) + const auto loadForRenderer = GetLoadForRenderer(assetName); +#endif #if defined(FEATURE_T6) const auto materialType = GetMaterialType(assetName); #endif @@ -132,6 +157,12 @@ namespace techset->techniques[techniqueIndex] = technique->Asset(); +#if defined(FEATURE_IW3) + // IW3 supports both sm2 and sm3. + // The shader's loadForRenderer property is set to 0 for sm2 and 1 for sm3. + // Which shader model a techset is for is entirely determined by its name according to modtools. + ApplyLoadForRendererToTechnique(*techset->techniques[techniqueIndex], loadForRenderer); +#endif #if defined(FEATURE_T6) // Another techset may override this for the technique // but the game determines the material type by techset name. diff --git a/test/ObjCompilingTests/Game/IW3/Techset/TechsetCompilerIW3Test.cpp b/test/ObjCompilingTests/Game/IW3/Techset/TechsetCompilerIW3Test.cpp index 642da0e5..773486a5 100644 --- a/test/ObjCompilingTests/Game/IW3/Techset/TechsetCompilerIW3Test.cpp +++ b/test/ObjCompilingTests/Game/IW3/Techset/TechsetCompilerIW3Test.cpp @@ -19,6 +19,9 @@ namespace { auto* technique = memory.Alloc(); technique->name = memory.Dup(name.c_str()); + technique->passCount = 1; + technique->passArray[0].vertexShader = memory.Alloc(); + technique->passArray[0].pixelShader = memory.Alloc(); context.AddSubAsset(name, technique); @@ -79,7 +82,13 @@ TEST_CASE("TechsetCompilerIW3", "[techset][iw3][compiler]") SECTION("Can parse simple techset") { - searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET( + const auto [techsetName, expectedLoadForRendererValue] = GENERATE(Catch::Generators::table({ + {"simple", 1}, + {"sm2/simple", 0}, + })); + CAPTURE(techsetName); + + searchPath.AddFileData(techset::GetFileNameForTechsetName(techsetName), R"TECHSET( "depth prepass": example_zprepass; @@ -90,11 +99,11 @@ TEST_CASE("TechsetCompilerIW3", "[techset][iw3][compiler]") auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory); auto* exampleLit = GivenTechnique("example_lit_spot", context, memory); - const auto result = sut->CreateAsset("simple", context); + const auto result = sut->CreateAsset(techsetName, context); REQUIRE(result.HasBeenSuccessful()); const auto* techset = static_cast(result.GetAssetInfo()->m_ptr); - CHECK(techset->name == "simple"s); + CHECK(techset->name == techsetName); CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1); size_t techniqueCount = 0; @@ -107,6 +116,10 @@ TEST_CASE("TechsetCompilerIW3", "[techset][iw3][compiler]") CHECK(techniqueCount == 2); CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass); CHECK(techset->techniques[TECHNIQUE_LIT_SPOT] == exampleLit); + + // Make sure IW3 techset postprocessing sets correct values for loadForRenderer + CHECK(exampleZPrepass->passArray[0].vertexShader->prog.loadDef.loadForRenderer == expectedLoadForRendererValue); + CHECK(exampleZPrepass->passArray[0].pixelShader->prog.loadDef.loadForRenderer == expectedLoadForRendererValue); } SECTION("Can parse techset with same technique used multiple times")