rendering: formalize legacy material shader pass hints
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
|
||||
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -430,10 +431,87 @@ inline const Resources::Material* ResolveMaterial(const VisibleRenderItem& visib
|
||||
return ResolveMaterial(visibleItem.meshRenderer, visibleItem.mesh, visibleItem.materialIndex);
|
||||
}
|
||||
|
||||
inline bool TryResolveRenderQueueTagValue(
|
||||
const Containers::String& queueValue,
|
||||
Core::int32& outRenderQueue) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(queueValue);
|
||||
if (normalized.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalized == Containers::String("background")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Background);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("geometry")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("alphatest")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::AlphaTest);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("transparent")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("overlay")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Overlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
const long parsedValue = std::strtol(normalized.CStr(), &end, 10);
|
||||
if (end != nullptr && *end == '\0') {
|
||||
outRenderQueue = static_cast<Core::int32>(parsedValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TryResolveShaderPassRenderQueue(const Resources::ShaderPass& shaderPass, Core::int32& outRenderQueue) {
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("queue") &&
|
||||
TryResolveRenderQueueTagValue(tag.value, outRenderQueue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline Core::int32 ResolveMaterialRenderQueue(const Resources::Material* material) {
|
||||
return material != nullptr
|
||||
? material->GetRenderQueue()
|
||||
: static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
|
||||
const Core::int32 defaultQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
|
||||
if (material == nullptr) {
|
||||
return defaultQueue;
|
||||
}
|
||||
|
||||
const Core::int32 materialQueue = material->GetRenderQueue();
|
||||
if (materialQueue != defaultQueue) {
|
||||
return materialQueue;
|
||||
}
|
||||
|
||||
if (const Resources::Shader* shader = material->GetShader()) {
|
||||
const Containers::String legacyExplicitPassName = material->GetLegacyShaderPassHint();
|
||||
if (!NormalizeBuiltinPassMetadataValue(legacyExplicitPassName).Empty()) {
|
||||
if (const Resources::ShaderPass* explicitPass = shader->FindPass(legacyExplicitPassName)) {
|
||||
Core::int32 shaderQueue = defaultQueue;
|
||||
if (TryResolveShaderPassRenderQueue(*explicitPass, shaderQueue)) {
|
||||
return shaderQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPass& pass : shader->GetPasses()) {
|
||||
Core::int32 shaderQueue = defaultQueue;
|
||||
if (TryResolveShaderPassRenderQueue(pass, shaderQueue)) {
|
||||
return shaderQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return materialQueue;
|
||||
}
|
||||
|
||||
inline bool IsTransparentRenderQueue(Core::int32 renderQueue) {
|
||||
@@ -445,7 +523,7 @@ inline bool HasLegacyMaterialBuiltinPassHints(const Resources::Material* materia
|
||||
return false;
|
||||
}
|
||||
|
||||
return !NormalizeBuiltinPassMetadataValue(material->GetShaderPass()).Empty() ||
|
||||
return !NormalizeBuiltinPassMetadataValue(material->GetLegacyShaderPassHint()).Empty() ||
|
||||
!NormalizeBuiltinPassMetadataValue(material->GetTag("LightMode")).Empty();
|
||||
}
|
||||
|
||||
@@ -456,7 +534,7 @@ inline bool LegacyMaterialBuiltinPassHintsMatch(
|
||||
return false;
|
||||
}
|
||||
|
||||
const Containers::String shaderPass = material->GetShaderPass();
|
||||
const Containers::String shaderPass = material->GetLegacyShaderPassHint();
|
||||
const Containers::String lightMode = material->GetTag("LightMode");
|
||||
const bool hasMaterialShaderPass = !NormalizeBuiltinPassMetadataValue(shaderPass).Empty();
|
||||
const bool hasMaterialLightMode = !NormalizeBuiltinPassMetadataValue(lightMode).Empty();
|
||||
|
||||
@@ -102,8 +102,15 @@ public:
|
||||
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
|
||||
void SetRenderStateOverrideEnabled(bool enabled);
|
||||
|
||||
// Legacy-only compatibility hint for older material assets that still
|
||||
// select a builtin pass by serialized name.
|
||||
void SetLegacyShaderPassHint(const Containers::String& shaderPass);
|
||||
const Containers::String& GetLegacyShaderPassHint() const { return m_legacyShaderPassHint; }
|
||||
bool HasLegacyShaderPassHint() const { return !m_legacyShaderPassHint.Empty(); }
|
||||
void ClearLegacyShaderPassHint();
|
||||
|
||||
void SetShaderPass(const Containers::String& shaderPass);
|
||||
const Containers::String& GetShaderPass() const { return m_shaderPass; }
|
||||
const Containers::String& GetShaderPass() const { return GetLegacyShaderPassHint(); }
|
||||
|
||||
void SetTag(const Containers::String& name, const Containers::String& value);
|
||||
Containers::String GetTag(const Containers::String& name) const;
|
||||
@@ -179,7 +186,7 @@ private:
|
||||
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||
MaterialRenderState m_renderState;
|
||||
bool m_hasRenderStateOverride = false;
|
||||
Containers::String m_shaderPass;
|
||||
Containers::String m_legacyShaderPassHint;
|
||||
Containers::Array<MaterialTagEntry> m_tags;
|
||||
ShaderKeywordSet m_keywordSet;
|
||||
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
|
||||
|
||||
@@ -425,8 +425,8 @@ Containers::String ResolveTextureBindingPath(
|
||||
return NormalizeArtifactPathString(material.GetTextureBindingPath(bindingIndex));
|
||||
}
|
||||
|
||||
Containers::String ResolveSerializedMaterialShaderPass(const Material& material) {
|
||||
const Containers::String& shaderPass = material.GetShaderPass();
|
||||
Containers::String ResolveSerializedLegacyMaterialShaderPassHint(const Material& material) {
|
||||
const Containers::String& shaderPass = material.GetLegacyShaderPassHint();
|
||||
if (shaderPass.Empty()) {
|
||||
return Containers::String();
|
||||
}
|
||||
@@ -459,7 +459,7 @@ bool WriteMaterialArtifactFile(
|
||||
material.GetShader() != nullptr
|
||||
? material.GetShader()->GetPath()
|
||||
: Containers::String());
|
||||
WriteString(output, ResolveSerializedMaterialShaderPass(material));
|
||||
WriteString(output, ResolveSerializedLegacyMaterialShaderPassHint(material));
|
||||
|
||||
MaterialArtifactHeader header;
|
||||
header.renderQueue = material.GetRenderQueue();
|
||||
|
||||
@@ -133,11 +133,19 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet)) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||
shaderPass,
|
||||
backend,
|
||||
*vertexVariant,
|
||||
pipelineDesc.vertexShader);
|
||||
}
|
||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet)) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||
shaderPass,
|
||||
backend,
|
||||
*fragmentVariant,
|
||||
pipelineDesc.fragmentShader);
|
||||
}
|
||||
|
||||
return pipelineDesc;
|
||||
@@ -317,8 +325,8 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve
|
||||
|
||||
if (!shaderHasExplicitBuiltinMetadata &&
|
||||
ownerMaterial != nullptr &&
|
||||
!ownerMaterial->GetShaderPass().Empty()) {
|
||||
const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetShaderPass());
|
||||
ownerMaterial->HasLegacyShaderPassHint()) {
|
||||
const Resources::ShaderPass* explicitPass = shader->FindPass(ownerMaterial->GetLegacyShaderPassHint());
|
||||
if (explicitPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(
|
||||
*shader,
|
||||
|
||||
@@ -53,8 +53,8 @@ const Resources::ShaderPass* FindCompatibleSurfacePass(
|
||||
|
||||
if (!shaderHasExplicitBuiltinMetadata &&
|
||||
material != nullptr &&
|
||||
!material->GetShaderPass().Empty()) {
|
||||
const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetShaderPass());
|
||||
material->HasLegacyShaderPassHint()) {
|
||||
const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetLegacyShaderPassHint());
|
||||
if (explicitPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(
|
||||
shader,
|
||||
@@ -130,10 +130,18 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
const Resources::ShaderStageVariant* fragmentVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend, keywordSet);
|
||||
if (vertexVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||
shaderPass,
|
||||
backend,
|
||||
*vertexVariant,
|
||||
pipelineDesc.vertexShader);
|
||||
}
|
||||
if (fragmentVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||
shaderPass,
|
||||
backend,
|
||||
*fragmentVariant,
|
||||
pipelineDesc.fragmentShader);
|
||||
}
|
||||
|
||||
return pipelineDesc;
|
||||
|
||||
@@ -323,7 +323,8 @@ void Material::Release() {
|
||||
m_shader.Reset();
|
||||
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||
m_renderState = MaterialRenderState();
|
||||
m_shaderPass.Clear();
|
||||
m_hasRenderStateOverride = false;
|
||||
m_legacyShaderPassHint.Clear();
|
||||
m_tags.Clear();
|
||||
m_keywordSet.enabledKeywords.Clear();
|
||||
m_properties.Clear();
|
||||
@@ -353,12 +354,31 @@ void Material::SetRenderQueue(MaterialRenderQueue renderQueue) {
|
||||
|
||||
void Material::SetRenderState(const MaterialRenderState& renderState) {
|
||||
m_renderState = renderState;
|
||||
m_hasRenderStateOverride = true;
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetRenderStateOverrideEnabled(bool enabled) {
|
||||
m_hasRenderStateOverride = enabled;
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetLegacyShaderPassHint(const Containers::String& shaderPass) {
|
||||
m_legacyShaderPassHint = shaderPass;
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::ClearLegacyShaderPassHint() {
|
||||
if (m_legacyShaderPassHint.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_legacyShaderPassHint.Clear();
|
||||
MarkChanged(false);
|
||||
}
|
||||
|
||||
void Material::SetShaderPass(const Containers::String& shaderPass) {
|
||||
m_shaderPass = shaderPass;
|
||||
MarkChanged(false);
|
||||
SetLegacyShaderPassHint(shaderPass);
|
||||
}
|
||||
|
||||
void Material::SetTag(const Containers::String& name, const Containers::String& value) {
|
||||
@@ -1100,7 +1120,7 @@ void Material::UpdateMemorySize() {
|
||||
m_memorySize = m_constantBufferData.Size() +
|
||||
m_constantLayout.Size() * sizeof(MaterialConstantFieldDesc) +
|
||||
sizeof(MaterialRenderState) +
|
||||
m_shaderPass.Length() +
|
||||
m_legacyShaderPassHint.Length() +
|
||||
m_tags.Size() * sizeof(MaterialTagEntry) +
|
||||
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
|
||||
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
|
||||
|
||||
@@ -1473,7 +1473,7 @@ bool MaterialFileExists(const Containers::String& path) {
|
||||
return std::filesystem::exists(std::filesystem::path(resourceRoot.CStr()) / inputPath);
|
||||
}
|
||||
|
||||
void ApplyMaterialShaderPassHint(Material* material, const Containers::String& shaderPass) {
|
||||
void ApplyLegacyMaterialShaderPassHint(Material* material, const Containers::String& shaderPass) {
|
||||
if (material == nullptr || shaderPass.Empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1482,7 +1482,7 @@ void ApplyMaterialShaderPassHint(Material* material, const Containers::String& s
|
||||
return;
|
||||
}
|
||||
|
||||
material->SetShaderPass(shaderPass);
|
||||
material->SetLegacyShaderPassHint(shaderPass);
|
||||
}
|
||||
|
||||
ResourceHandle<Shader> LoadShaderHandle(const Containers::String& shaderPath);
|
||||
@@ -1608,7 +1608,7 @@ LoadResult LoadMaterialArtifact(const Containers::String& path) {
|
||||
material->SetShader(shaderHandle);
|
||||
}
|
||||
}
|
||||
ApplyMaterialShaderPassHint(material.get(), shaderPass);
|
||||
ApplyLegacyMaterialShaderPassHint(material.get(), shaderPass);
|
||||
|
||||
MaterialArtifactHeader header = {};
|
||||
if (isLegacySchema) {
|
||||
@@ -1799,12 +1799,12 @@ bool MaterialLoader::ParseMaterialData(const Containers::Array<Core::uint8>& dat
|
||||
if (!TryParseStringValue(jsonText, "shaderPass", shaderPass)) {
|
||||
return false;
|
||||
}
|
||||
ApplyMaterialShaderPassHint(material, shaderPass);
|
||||
ApplyLegacyMaterialShaderPassHint(material, shaderPass);
|
||||
} else if (HasKey(jsonText, "pass")) {
|
||||
if (!TryParseStringValue(jsonText, "pass", shaderPass)) {
|
||||
return false;
|
||||
}
|
||||
ApplyMaterialShaderPassHint(material, shaderPass);
|
||||
ApplyLegacyMaterialShaderPassHint(material, shaderPass);
|
||||
}
|
||||
|
||||
if (HasKey(jsonText, "renderQueue")) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -90,7 +90,7 @@ TEST(Material, DefaultRenderMetadata) {
|
||||
EXPECT_TRUE(material.GetRenderState().depthTestEnable);
|
||||
EXPECT_TRUE(material.GetRenderState().depthWriteEnable);
|
||||
EXPECT_EQ(material.GetRenderState().depthFunc, MaterialComparisonFunc::Less);
|
||||
EXPECT_TRUE(material.GetShaderPass().Empty());
|
||||
EXPECT_TRUE(material.GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material.GetTagCount(), 0u);
|
||||
}
|
||||
|
||||
@@ -138,11 +138,16 @@ TEST(Material, SetGetRenderState) {
|
||||
EXPECT_EQ(result.colorWriteMask, 0x7);
|
||||
}
|
||||
|
||||
TEST(Material, SetGetShaderPass) {
|
||||
TEST(Material, SetGetLegacyShaderPassHint) {
|
||||
Material material;
|
||||
|
||||
material.SetShaderPass("ForwardLit");
|
||||
EXPECT_EQ(material.GetShaderPass(), "ForwardLit");
|
||||
material.SetLegacyShaderPassHint("ForwardLit");
|
||||
EXPECT_TRUE(material.HasLegacyShaderPassHint());
|
||||
EXPECT_EQ(material.GetLegacyShaderPassHint(), "ForwardLit");
|
||||
|
||||
material.ClearLegacyShaderPassHint();
|
||||
EXPECT_FALSE(material.HasLegacyShaderPassHint());
|
||||
EXPECT_TRUE(material.GetLegacyShaderPassHint().Empty());
|
||||
}
|
||||
|
||||
TEST(Material, SetGetTags) {
|
||||
|
||||
@@ -243,7 +243,7 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
|
||||
EXPECT_TRUE(material->IsValid());
|
||||
EXPECT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_EQ(material->GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Transparent));
|
||||
EXPECT_EQ(material->GetShaderPass(), "ForwardLit");
|
||||
EXPECT_EQ(material->GetLegacyShaderPassHint(), "ForwardLit");
|
||||
EXPECT_EQ(material->GetTag("LightMode"), "ForwardBase");
|
||||
EXPECT_EQ(material->GetTag("RenderType"), "Transparent");
|
||||
EXPECT_EQ(material->GetRenderState().cullMode, MaterialCullMode::Back);
|
||||
@@ -344,7 +344,7 @@ TEST(MaterialLoader, LoadMaterialWithShaderManifestResolvesShaderPass) {
|
||||
Material* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetShaderPass().Empty());
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
ASSERT_NE(material->GetShader()->FindPass("ForwardLit"), nullptr);
|
||||
const ShaderStageVariant* vertexVariant =
|
||||
material->GetShader()->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::OpenGL);
|
||||
@@ -386,7 +386,7 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetShaderPass().Empty());
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(0.2f, 0.4f, 0.6f, 0.8f));
|
||||
EXPECT_FLOAT_EQ(material->GetFloat("_Metallic"), 0.15f);
|
||||
EXPECT_EQ(material->GetInt("_Mode"), 5);
|
||||
@@ -423,7 +423,7 @@ TEST(MaterialLoader, LoadBuiltinShaderMaterialDropsRedundantBuiltinShaderPassHin
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetShaderPass().Empty());
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_NE(material->GetShader()->FindPass("Unlit"), nullptr);
|
||||
|
||||
delete material;
|
||||
@@ -865,7 +865,7 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactStripsRedundantBuiltinShaderPa
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetShaderPass().Empty());
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
delete material;
|
||||
|
||||
Reference in New Issue
Block a user