refactor: formalize builtin pass matching

This commit is contained in:
2026-04-02 16:26:20 +08:00
parent 70ced2d91f
commit 33f16597fa
3 changed files with 181 additions and 20 deletions

View File

@@ -11,11 +11,20 @@ namespace XCEngine {
namespace Rendering { namespace Rendering {
enum class BuiltinMaterialPass : Core::uint32 { 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) { inline bool IsForwardPassName(const Containers::String& value) {
const Containers::String normalized = value.Trim().ToLower(); const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value);
return normalized.Empty() || return normalized.Empty() ||
normalized == Containers::String("forward") || normalized == Containers::String("forward") ||
normalized == Containers::String("forwardbase") || normalized == Containers::String("forwardbase") ||
@@ -23,6 +32,92 @@ inline bool IsForwardPassName(const Containers::String& value) {
normalized == Containers::String("forwardonly"); 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( inline const Resources::Material* ResolveMaterial(
const Components::MeshRendererComponent* meshRenderer, const Components::MeshRendererComponent* meshRenderer,
const Resources::Mesh* mesh, const Resources::Mesh* mesh,
@@ -72,26 +167,44 @@ inline bool IsTransparentRenderQueue(Core::int32 renderQueue) {
inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) { inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) {
if (material == nullptr) { if (material == nullptr) {
return true; return pass == BuiltinMaterialPass::ForwardLit;
} }
switch (pass) { const Containers::String shaderPass = material->GetShaderPass();
case BuiltinMaterialPass::Forward: { const Containers::String lightMode = material->GetTag("LightMode");
const Containers::String shaderPass = material->GetShaderPass(); const bool hasMaterialShaderPass = !NormalizeBuiltinPassMetadataValue(shaderPass).Empty();
if (!shaderPass.Empty() && !IsForwardPassName(shaderPass)) { const bool hasMaterialLightMode = !NormalizeBuiltinPassMetadataValue(lightMode).Empty();
if (hasMaterialShaderPass || hasMaterialLightMode) {
if (hasMaterialShaderPass &&
!MatchesBuiltinPassName(shaderPass, pass)) {
return false; return false;
} }
if (hasMaterialLightMode &&
const Containers::String lightMode = material->GetTag("LightMode"); !MatchesBuiltinPassName(lightMode, pass)) {
if (!lightMode.Empty() && !IsForwardPassName(lightMode)) {
return false; return false;
} }
return true;
}
return true; const Resources::Shader* shader = material->GetShader();
} if (shader != nullptr) {
default: bool shaderHasExplicitBuiltinMetadata = false;
return true; 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) { inline RHI::CullMode ToRHICullMode(Resources::MaterialCullMode mode) {

View File

@@ -439,7 +439,7 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p
RHI::RHIPipelineState* currentPipelineState = nullptr; RHI::RHIPipelineState* currentPipelineState = nullptr;
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
const Resources::Material* material = ResolveMaterial(visibleItem); const Resources::Material* material = ResolveMaterial(visibleItem);
if (!MatchesBuiltinPass(material, BuiltinMaterialPass::Forward)) { if (!MatchesBuiltinPass(material, BuiltinMaterialPass::ForwardLit)) {
continue; continue;
} }

View File

@@ -13,6 +13,7 @@
#include <XCEngine/Rendering/RenderSceneExtractor.h> #include <XCEngine/Rendering/RenderSceneExtractor.h>
#include <XCEngine/Resources/Material/Material.h> #include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Mesh/Mesh.h> #include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Scene/Scene.h> #include <XCEngine/Scene/Scene.h>
using namespace XCEngine::Components; using namespace XCEngine::Components;
@@ -359,22 +360,69 @@ TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasN
delete mesh; delete mesh;
} }
TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardPassMetadata) { TEST(RenderMaterialUtility_Test, MatchesBuiltinForwardLitPassMetadata) {
Material forwardMaterial; Material forwardMaterial;
forwardMaterial.SetShaderPass("ForwardLit"); forwardMaterial.SetShaderPass("ForwardLit");
forwardMaterial.SetTag("LightMode", "ForwardBase"); forwardMaterial.SetTag("LightMode", "ForwardBase");
EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::Forward)); EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::ForwardLit));
Material noMetadataMaterial; Material noMetadataMaterial;
EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::Forward)); EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit));
Material shadowMaterial; Material shadowMaterial;
shadowMaterial.SetShaderPass("ShadowCaster"); shadowMaterial.SetShaderPass("ShadowCaster");
EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::Forward)); EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ForwardLit));
Material depthOnlyMaterial; Material depthOnlyMaterial;
depthOnlyMaterial.SetTag("LightMode", "DepthOnly"); 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<Shader> 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<Shader> shaderHandle(shader);
material.SetShader(shaderHandle);
EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit));
EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit));
} }
TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) { TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) {