From 7acc397714c9a3bedac847b68923fadc58e65699 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Mon, 6 Apr 2026 18:24:10 +0800 Subject: [PATCH] rendering: formalize legacy shader pass fallback --- .../Builtin/BuiltinPassMetadataUtils.h | 10 +++ .../Materials/RenderMaterialResolve.h | 67 +++++++++++++------ .../BuiltinDepthStylePassBaseResources.cpp | 29 ++++---- .../BuiltinForwardPipelineResources.cpp | 20 ++++-- .../unit/test_render_scene_extractor.cpp | 50 +++++++++++++- 5 files changed, 136 insertions(+), 40 deletions(-) diff --git a/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h b/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h index 5fb68578..26a1346a 100644 --- a/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h +++ b/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h @@ -79,6 +79,16 @@ inline bool ShaderPassHasExplicitBuiltinMetadata(const Resources::ShaderPass& sh return false; } +inline bool ShaderHasExplicitBuiltinMetadata(const Resources::Shader& shader) { + for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { + if (ShaderPassHasExplicitBuiltinMetadata(shaderPass)) { + return true; + } + } + + return false; +} + inline bool ShaderPassMatchesBuiltinPass( const Resources::ShaderPass& shaderPass, BuiltinMaterialPass pass) { diff --git a/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h b/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h index c5c030a1..fe15e071 100644 --- a/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h +++ b/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h @@ -409,43 +409,72 @@ inline bool IsTransparentRenderQueue(Core::int32 renderQueue) { return renderQueue >= static_cast(Resources::MaterialRenderQueue::Transparent); } -inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) { +inline bool HasLegacyMaterialBuiltinPassHints(const Resources::Material* material) { if (material == nullptr) { - return pass == BuiltinMaterialPass::ForwardLit; + return false; + } + + return !NormalizeBuiltinPassMetadataValue(material->GetShaderPass()).Empty() || + !NormalizeBuiltinPassMetadataValue(material->GetTag("LightMode")).Empty(); +} + +inline bool LegacyMaterialBuiltinPassHintsMatch( + const Resources::Material* material, + BuiltinMaterialPass pass) { + if (material == nullptr) { + return false; } const Containers::String shaderPass = material->GetShaderPass(); const Containers::String lightMode = material->GetTag("LightMode"); const bool hasMaterialShaderPass = !NormalizeBuiltinPassMetadataValue(shaderPass).Empty(); const bool hasMaterialLightMode = !NormalizeBuiltinPassMetadataValue(lightMode).Empty(); - if (hasMaterialShaderPass || hasMaterialLightMode) { - if (hasMaterialShaderPass && - !MatchesBuiltinPassName(shaderPass, pass)) { - return false; - } - if (hasMaterialLightMode && - !MatchesBuiltinPassName(lightMode, pass)) { - return false; - } + if (!hasMaterialShaderPass && !hasMaterialLightMode) { + return false; + } + + if (hasMaterialShaderPass && + !MatchesBuiltinPassName(shaderPass, pass)) { + return false; + } + + if (hasMaterialLightMode && + !MatchesBuiltinPassName(lightMode, pass)) { + return false; + } + + return true; +} + +inline bool CanUseLegacyMaterialPassFallback(const Resources::Material* material) { + if (material == nullptr) { return true; } + const Resources::Shader* shader = material->GetShader(); + return shader == nullptr || !ShaderHasExplicitBuiltinMetadata(*shader); +} + +inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) { + if (material == nullptr) { + return pass == BuiltinMaterialPass::ForwardLit; + } + const Resources::Shader* shader = material->GetShader(); if (shader != nullptr) { - bool shaderHasExplicitBuiltinMetadata = false; for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) { if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) { return true; } - - if (ShaderPassHasExplicitBuiltinMetadata(shaderPassEntry)) { - shaderHasExplicitBuiltinMetadata = true; - } } + } - if (shaderHasExplicitBuiltinMetadata) { - return false; - } + if (!CanUseLegacyMaterialPassFallback(material)) { + return false; + } + + if (HasLegacyMaterialBuiltinPassHints(material)) { + return LegacyMaterialBuiltinPassHintsMatch(material, pass); } return pass == BuiltinMaterialPass::ForwardLit; diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp index 68137821..f426f875 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp @@ -164,6 +164,8 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve return false; } + const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(*shader); + auto tryAcceptPass = [this, shader, &resolved](const Resources::ShaderPass& shaderPass) -> bool { BuiltinPassResourceBindingPlan bindingPlan = {}; @@ -178,19 +180,6 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve return true; }; - if (ownerMaterial != nullptr && !ownerMaterial->GetShaderPass().Empty()) { - const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetShaderPass()); - if (explicitPass != nullptr && - ShaderPassMatchesBuiltinPass(*explicitPass, m_passType) && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( - *shader, - explicitPass->name, - backend) && - tryAcceptPass(*explicitPass)) { - return true; - } - } - for (const Resources::ShaderPass& shaderPass : shader->GetPasses()) { if (!ShaderPassMatchesBuiltinPass(shaderPass, m_passType) || !::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(*shader, shaderPass.name, backend)) { @@ -202,6 +191,20 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve } } + if (!shaderHasExplicitBuiltinMetadata && + ownerMaterial != nullptr && + !ownerMaterial->GetShaderPass().Empty()) { + const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetShaderPass()); + if (explicitPass != nullptr && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( + *shader, + explicitPass->name, + backend) && + tryAcceptPass(*explicitPass)) { + return true; + } + } + return false; }; diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp index b679ad4a..97d55fd6 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp @@ -28,7 +28,18 @@ const Resources::ShaderPass* FindCompatibleSurfacePass( const Resources::Material* material, BuiltinMaterialPass pass, Resources::ShaderBackend backend) { - if (material != nullptr && !material->GetShaderPass().Empty()) { + const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(shader); + + for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { + if (ShaderPassMatchesBuiltinPass(shaderPass, pass) && + ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) { + return &shaderPass; + } + } + + if (!shaderHasExplicitBuiltinMetadata && + material != nullptr && + !material->GetShaderPass().Empty()) { const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetShaderPass()); if (explicitPass != nullptr && ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, explicitPass->name, backend)) { @@ -36,11 +47,8 @@ const Resources::ShaderPass* FindCompatibleSurfacePass( } } - for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { - if (ShaderPassMatchesBuiltinPass(shaderPass, pass) && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) { - return &shaderPass; - } + if (shaderHasExplicitBuiltinMetadata) { + return nullptr; } if (pass != BuiltinMaterialPass::ForwardLit) { diff --git a/tests/Rendering/unit/test_render_scene_extractor.cpp b/tests/Rendering/unit/test_render_scene_extractor.cpp index b4c02667..e12e8d43 100644 --- a/tests/Rendering/unit/test_render_scene_extractor.cpp +++ b/tests/Rendering/unit/test_render_scene_extractor.cpp @@ -459,7 +459,7 @@ TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasN delete mesh; } -TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardLitPassMetadata) { +TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsCanDriveBuiltinPassMatching) { Material forwardMaterial; forwardMaterial.SetShaderPass("ForwardLit"); forwardMaterial.SetTag("LightMode", "ForwardBase"); @@ -478,7 +478,7 @@ TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardLitPassMetadata) { EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::ForwardLit)); } -TEST(RenderMaterialUtility_Test, MatchesBuiltinUnlitDepthAndObjectIdPassMetadata) { +TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsSupportUnlitDepthAndObjectId) { Material unlitMaterial; unlitMaterial.SetShaderPass("Unlit"); EXPECT_TRUE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::Unlit)); @@ -525,6 +525,52 @@ TEST(RenderMaterialUtility_Test, ExplicitShaderPassMetadataDisablesImplicitForwa EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); } +TEST(RenderMaterialUtility_Test, ShaderMetadataOverridesConflictingLegacyMaterialPassHints) { + Material material; + auto* shader = new Shader(); + + ShaderPass forwardPass = {}; + forwardPass.name = "ForwardLit"; + shader->AddPass(forwardPass); + + material.SetShader(ResourceHandle(shader)); + material.SetShaderPass("ShadowCaster"); + material.SetTag("LightMode", "DepthOnly"); + + EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ShadowCaster)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::DepthOnly)); +} + +TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsRemainAvailableForShadersWithoutBuiltinMetadata) { + Material material; + auto* shader = new Shader(); + + ShaderPass defaultPass = {}; + defaultPass.name = "Default"; + shader->AddPass(defaultPass); + + material.SetShader(ResourceHandle(shader)); + material.SetShaderPass("ShadowCaster"); + + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ShadowCaster)); +} + +TEST(RenderMaterialUtility_Test, ShadersWithoutBuiltinMetadataKeepImplicitForwardFallback) { + Material material; + auto* shader = new Shader(); + + ShaderPass defaultPass = {}; + defaultPass.name = "Default"; + shader->AddPass(defaultPass); + + material.SetShader(ResourceHandle(shader)); + + EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); +} + TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromCanonicalNamesAndAliases) { Material canonicalMaterial; canonicalMaterial.SetFloat4("baseColor", Vector4(0.2f, 0.4f, 0.6f, 0.8f));