refactor: formalize builtin pass matching
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user