rendering: formalize legacy material shader pass hints

This commit is contained in:
2026-04-07 09:49:21 +08:00
parent 7f4b647394
commit 558b6438cf
10 changed files with 216 additions and 48 deletions

View File

@@ -54,7 +54,7 @@ Mesh* CreateSectionedTestMesh(const char* path, std::initializer_list<uint32_t>
return mesh;
}
Material* CreateTestMaterial(const char* path, int32_t renderQueue, const char* shaderPass = nullptr, const char* lightMode = nullptr) {
Material* CreateTestMaterial(const char* path, int32_t renderQueue, const char* legacyShaderPassHint = nullptr, const char* lightMode = nullptr) {
auto* material = new Material();
IResource::ConstructParams params = {};
params.name = "TestMaterial";
@@ -62,8 +62,8 @@ Material* CreateTestMaterial(const char* path, int32_t renderQueue, const char*
params.guid = ResourceGUID::Generate(path);
material->Initialize(params);
material->SetRenderQueue(renderQueue);
if (shaderPass != nullptr) {
material->SetShaderPass(shaderPass);
if (legacyShaderPassHint != nullptr) {
material->SetLegacyShaderPassHint(legacyShaderPassHint);
}
if (lightMode != nullptr) {
material->SetTag("LightMode", lightMode);
@@ -461,7 +461,7 @@ TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasN
TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsCanDriveBuiltinPassMatching) {
Material forwardMaterial;
forwardMaterial.SetShaderPass("ForwardLit");
forwardMaterial.SetLegacyShaderPassHint("ForwardLit");
forwardMaterial.SetTag("LightMode", "ForwardBase");
EXPECT_TRUE(MatchesBuiltinPass(&forwardMaterial, BuiltinMaterialPass::ForwardLit));
@@ -469,7 +469,7 @@ TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsCanDriveBuiltinPassMatch
EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit));
Material shadowMaterial;
shadowMaterial.SetShaderPass("ShadowCaster");
shadowMaterial.SetLegacyShaderPassHint("ShadowCaster");
EXPECT_FALSE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ForwardLit));
EXPECT_TRUE(MatchesBuiltinPass(&shadowMaterial, BuiltinMaterialPass::ShadowCaster));
@@ -480,7 +480,7 @@ TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsCanDriveBuiltinPassMatch
TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsSupportUnlitDepthAndObjectId) {
Material unlitMaterial;
unlitMaterial.SetShaderPass("Unlit");
unlitMaterial.SetLegacyShaderPassHint("Unlit");
EXPECT_TRUE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::Unlit));
EXPECT_FALSE(MatchesBuiltinPass(&unlitMaterial, BuiltinMaterialPass::ForwardLit));
@@ -490,7 +490,7 @@ TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsSupportUnlitDepthAndObje
EXPECT_FALSE(MatchesBuiltinPass(&depthMaterial, BuiltinMaterialPass::Unlit));
Material objectIdMaterial;
objectIdMaterial.SetShaderPass("ObjectId");
objectIdMaterial.SetLegacyShaderPassHint("ObjectId");
EXPECT_TRUE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ObjectId));
EXPECT_FALSE(MatchesBuiltinPass(&objectIdMaterial, BuiltinMaterialPass::ForwardLit));
}
@@ -534,7 +534,7 @@ TEST(RenderMaterialUtility_Test, ShaderMetadataOverridesConflictingLegacyMateria
shader->AddPass(forwardPass);
material.SetShader(ResourceHandle<Shader>(shader));
material.SetShaderPass("ShadowCaster");
material.SetLegacyShaderPassHint("ShadowCaster");
material.SetTag("LightMode", "DepthOnly");
EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit));
@@ -551,7 +551,7 @@ TEST(RenderMaterialUtility_Test, LegacyMaterialPassHintsRemainAvailableForShader
shader->AddPass(defaultPass);
material.SetShader(ResourceHandle<Shader>(shader));
material.SetShaderPass("ShadowCaster");
material.SetLegacyShaderPassHint("ShadowCaster");
EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit));
EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ShadowCaster));
@@ -734,9 +734,10 @@ TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) {
renderState.depthFunc = MaterialComparisonFunc::LessEqual;
material.SetRenderState(renderState);
const XCEngine::RHI::RasterizerDesc rasterizerState = BuildRasterizerState(&material);
const XCEngine::RHI::BlendDesc blendState = BuildBlendState(&material);
const XCEngine::RHI::DepthStencilStateDesc depthStencilState = BuildDepthStencilState(&material);
const MaterialRenderState effectiveRenderState = ResolveEffectiveRenderState(nullptr, &material);
const XCEngine::RHI::RasterizerDesc rasterizerState = BuildRasterizerState(effectiveRenderState);
const XCEngine::RHI::BlendDesc blendState = BuildBlendState(effectiveRenderState);
const XCEngine::RHI::DepthStencilStateDesc depthStencilState = BuildDepthStencilState(effectiveRenderState);
EXPECT_EQ(rasterizerState.cullMode, static_cast<uint32_t>(XCEngine::RHI::CullMode::Back));
EXPECT_EQ(rasterizerState.frontFace, static_cast<uint32_t>(XCEngine::RHI::FrontFace::CounterClockwise));
@@ -755,4 +756,45 @@ TEST(RenderMaterialUtility_Test, MapsMaterialRenderStateToRhiDescriptors) {
EXPECT_EQ(depthStencilState.depthFunc, static_cast<uint32_t>(XCEngine::RHI::ComparisonFunc::LessEqual));
}
TEST(RenderMaterialUtility_Test, ShaderPassFixedFunctionStateIsUsedBeforeLegacyMaterialOverride) {
ShaderPass shaderPass = {};
shaderPass.name = "ForwardLit";
shaderPass.hasFixedFunctionState = true;
shaderPass.fixedFunctionState.cullMode = MaterialCullMode::Back;
shaderPass.fixedFunctionState.blendEnable = true;
shaderPass.fixedFunctionState.srcBlend = MaterialBlendFactor::SrcAlpha;
shaderPass.fixedFunctionState.dstBlend = MaterialBlendFactor::InvSrcAlpha;
shaderPass.fixedFunctionState.srcBlendAlpha = MaterialBlendFactor::SrcAlpha;
shaderPass.fixedFunctionState.dstBlendAlpha = MaterialBlendFactor::InvSrcAlpha;
shaderPass.fixedFunctionState.depthWriteEnable = false;
shaderPass.fixedFunctionState.depthFunc = MaterialComparisonFunc::LessEqual;
Material material;
MaterialRenderState effectiveState = ResolveEffectiveRenderState(&shaderPass, &material);
EXPECT_EQ(effectiveState.cullMode, MaterialCullMode::Back);
EXPECT_TRUE(effectiveState.blendEnable);
EXPECT_FALSE(effectiveState.depthWriteEnable);
EXPECT_EQ(effectiveState.depthFunc, MaterialComparisonFunc::LessEqual);
MaterialRenderState legacyOverride = {};
legacyOverride.cullMode = MaterialCullMode::Front;
legacyOverride.depthWriteEnable = true;
material.SetRenderState(legacyOverride);
effectiveState = ResolveEffectiveRenderState(&shaderPass, &material);
EXPECT_EQ(effectiveState.cullMode, MaterialCullMode::Front);
EXPECT_FALSE(effectiveState.blendEnable);
EXPECT_TRUE(effectiveState.depthWriteEnable);
EXPECT_EQ(effectiveState.depthFunc, MaterialComparisonFunc::Less);
}
TEST(RenderMaterialUtility_Test, ShaderPassQueueTagResolvesUnityStyleRenderQueue) {
ShaderPass shaderPass = {};
shaderPass.tags.PushBack({ "Queue", "Transparent" });
int32 renderQueue = 0;
EXPECT_TRUE(TryResolveShaderPassRenderQueue(shaderPass, renderQueue));
EXPECT_EQ(renderQueue, static_cast<int32>(MaterialRenderQueue::Transparent));
}
} // namespace