From 33f16597fa196778ac593697565e547d53707829 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 2 Apr 2026 16:26:20 +0800 Subject: [PATCH] refactor: formalize builtin pass matching --- .../Rendering/RenderMaterialUtility.h | 141 ++++++++++++++++-- .../Pipelines/BuiltinForwardPipeline.cpp | 2 +- .../unit/test_render_scene_extractor.cpp | 58 ++++++- 3 files changed, 181 insertions(+), 20 deletions(-) diff --git a/engine/include/XCEngine/Rendering/RenderMaterialUtility.h b/engine/include/XCEngine/Rendering/RenderMaterialUtility.h index 3670cb5d..4b32efd7 100644 --- a/engine/include/XCEngine/Rendering/RenderMaterialUtility.h +++ b/engine/include/XCEngine/Rendering/RenderMaterialUtility.h @@ -11,11 +11,20 @@ namespace XCEngine { namespace Rendering { enum class BuiltinMaterialPass : Core::uint32 { - Forward = 0 + ForwardLit = 0, + Unlit, + DepthOnly, + ShadowCaster, + ObjectId, + Forward = ForwardLit }; +inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::String& value) { + return value.Trim().ToLower(); +} + inline bool IsForwardPassName(const Containers::String& value) { - const Containers::String normalized = value.Trim().ToLower(); + const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); return normalized.Empty() || normalized == Containers::String("forward") || normalized == Containers::String("forwardbase") || @@ -23,6 +32,92 @@ inline bool IsForwardPassName(const Containers::String& value) { normalized == Containers::String("forwardonly"); } +inline bool IsUnlitPassName(const Containers::String& value) { + const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); + return normalized == Containers::String("unlit") || + normalized == Containers::String("forwardunlit") || + normalized == Containers::String("srpdefaultunlit"); +} + +inline bool IsDepthOnlyPassName(const Containers::String& value) { + const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); + return normalized == Containers::String("depthonly") || + normalized == Containers::String("depth"); +} + +inline bool IsShadowCasterPassName(const Containers::String& value) { + const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); + return normalized == Containers::String("shadowcaster") || + normalized == Containers::String("shadow"); +} + +inline bool IsObjectIdPassName(const Containers::String& value) { + const Containers::String normalized = value.Trim().ToLower(); + return normalized == Containers::String("objectid") || + normalized == Containers::String("editorobjectid"); +} + +inline bool MatchesBuiltinPassName( + const Containers::String& value, + BuiltinMaterialPass pass) { + switch (pass) { + case BuiltinMaterialPass::ForwardLit: + return IsForwardPassName(value); + case BuiltinMaterialPass::Unlit: + return IsUnlitPassName(value); + case BuiltinMaterialPass::DepthOnly: + return IsDepthOnlyPassName(value); + case BuiltinMaterialPass::ShadowCaster: + return IsShadowCasterPassName(value); + case BuiltinMaterialPass::ObjectId: + return IsObjectIdPassName(value); + default: + return false; + } +} + +inline bool ShaderPassHasExplicitBuiltinMetadata(const Resources::ShaderPass& shaderPass) { + if (!shaderPass.name.Empty() && + shaderPass.name != Containers::String("Default")) { + return true; + } + + for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) { + if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("lightmode")) { + return true; + } + } + + return false; +} + +inline bool ShaderPassMatchesBuiltinPass( + const Resources::ShaderPass& shaderPass, + BuiltinMaterialPass pass) { + bool hasMetadata = false; + + if (!shaderPass.name.Empty() && + shaderPass.name != Containers::String("Default")) { + hasMetadata = true; + if (!MatchesBuiltinPassName(shaderPass.name, pass)) { + return false; + } + } + + for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) { + if (NormalizeBuiltinPassMetadataValue(tag.name) != Containers::String("lightmode")) { + continue; + } + + hasMetadata = true; + if (!MatchesBuiltinPassName(tag.value, pass)) { + return false; + } + } + + return hasMetadata; +} + inline const Resources::Material* ResolveMaterial( const Components::MeshRendererComponent* meshRenderer, const Resources::Mesh* mesh, @@ -72,26 +167,44 @@ inline bool IsTransparentRenderQueue(Core::int32 renderQueue) { inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) { if (material == nullptr) { - return true; + return pass == BuiltinMaterialPass::ForwardLit; } - switch (pass) { - case BuiltinMaterialPass::Forward: { - const Containers::String shaderPass = material->GetShaderPass(); - if (!shaderPass.Empty() && !IsForwardPassName(shaderPass)) { + 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; } - - const Containers::String lightMode = material->GetTag("LightMode"); - if (!lightMode.Empty() && !IsForwardPassName(lightMode)) { + if (hasMaterialLightMode && + !MatchesBuiltinPassName(lightMode, pass)) { return false; } + return true; + } - return true; - } - default: - return true; + 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; + } } + + return pass == BuiltinMaterialPass::ForwardLit; } inline RHI::CullMode ToRHICullMode(Resources::MaterialCullMode mode) { diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index 82d6bd64..719a3c80 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -439,7 +439,7 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p RHI::RHIPipelineState* currentPipelineState = nullptr; for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { const Resources::Material* material = ResolveMaterial(visibleItem); - if (!MatchesBuiltinPass(material, BuiltinMaterialPass::Forward)) { + if (!MatchesBuiltinPass(material, BuiltinMaterialPass::ForwardLit)) { continue; } diff --git a/tests/Rendering/unit/test_render_scene_extractor.cpp b/tests/Rendering/unit/test_render_scene_extractor.cpp index d4b8cac6..8883b9c8 100644 --- a/tests/Rendering/unit/test_render_scene_extractor.cpp +++ b/tests/Rendering/unit/test_render_scene_extractor.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace XCEngine::Components; @@ -359,22 +360,69 @@ TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasN delete mesh; } -TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardPassMetadata) { +TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardLitPassMetadata) { Material forwardMaterial; forwardMaterial.SetShaderPass("ForwardLit"); forwardMaterial.SetTag("LightMode", "ForwardBase"); - EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::Forward)); + EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::ForwardLit)); Material noMetadataMaterial; - EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::Forward)); + EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit)); Material shadowMaterial; shadowMaterial.SetShaderPass("ShadowCaster"); - EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::Forward)); + EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ForwardLit)); Material depthOnlyMaterial; depthOnlyMaterial.SetTag("LightMode", "DepthOnly"); - EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::Forward)); + EXPECT_FALSE(MatchesBuiltinPass(&depthOnlyMaterial, BuiltinMaterialPass::ForwardLit)); +} + +TEST(RenderMaterialUtility_Test, MatchesBuiltinUnlitDepthAndObjectIdPassMetadata) { + Material unlitMaterial; + unlitMaterial.SetShaderPass("Unlit"); + EXPECT_TRUE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::Unlit)); + EXPECT_FALSE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::ForwardLit)); + + Material depthMaterial; + depthMaterial.SetTag("LightMode", "DepthOnly"); + EXPECT_TRUE(MatchesBuiltinPass(&depthMaterial, BuiltinMaterialPass::DepthOnly)); + EXPECT_FALSE(MatchesBuiltinPass(&depthMaterial, BuiltinMaterialPass::Unlit)); + + Material objectIdMaterial; + objectIdMaterial.SetShaderPass("ObjectId"); + EXPECT_TRUE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ObjectId)); + EXPECT_FALSE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ForwardLit)); +} + +TEST(RenderMaterialUtility_Test, ShaderPassMetadataCanDriveBuiltinPassMatching) { + Material material; + auto* shader = new Shader(); + + ShaderPass forwardPass = {}; + forwardPass.name = "ForwardLit"; + shader->AddPass(forwardPass); + + ResourceHandle shaderHandle(shader); + material.SetShader(shaderHandle); + + EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); +} + +TEST(RenderMaterialUtility_Test, ExplicitShaderPassMetadataDisablesImplicitForwardFallback) { + Material material; + auto* shader = new Shader(); + + ShaderPass unlitPass = {}; + unlitPass.name = "Unlit"; + shader->AddPass(unlitPass); + + ResourceHandle shaderHandle(shader); + material.SetShader(shaderHandle); + + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); } TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) {