refactor: formalize builtin pass matching
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <XCEngine/Rendering/RenderSceneExtractor.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
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<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) {
|
||||
|
||||
Reference in New Issue
Block a user