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

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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,

View File

@@ -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;

View File

@@ -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) +

View File

@@ -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")) {