rendering: formalize shader keyword metadata contract

This commit is contained in:
2026-04-06 18:55:26 +08:00
parent 7acc397714
commit a8b4da16a3
16 changed files with 795 additions and 16 deletions

View File

@@ -95,6 +95,21 @@ bool IsTextureMaterialPropertyType(MaterialPropertyType type) {
return type == MaterialPropertyType::Texture || type == MaterialPropertyType::Cubemap;
}
Containers::String NormalizeShaderKeyword(const Containers::String& keyword) {
const Containers::String normalized = keyword.Trim();
if (normalized.Empty() ||
normalized == Containers::String("_") ||
normalized == Containers::String("__")) {
return Containers::String();
}
return normalized;
}
bool CompareShaderKeywords(const Containers::String& left, const Containers::String& right) {
return std::strcmp(left.CStr(), right.CStr()) < 0;
}
MaterialPropertyType GetMaterialPropertyTypeForShaderProperty(ShaderPropertyType type) {
switch (type) {
case ShaderPropertyType::Float:
@@ -312,6 +327,7 @@ Material::~Material() {
// resetting them here avoids teardown-order issues during destruction.
m_shader.Reset();
m_tags = Containers::Array<MaterialTagEntry>();
m_keywordSet = ShaderKeywordSet();
m_properties = Containers::HashMap<Containers::String, MaterialProperty>();
m_constantLayout = Containers::Array<MaterialConstantFieldDesc>();
m_constantBufferData = Containers::Array<Core::uint8>();
@@ -324,6 +340,7 @@ void Material::Release() {
m_renderState = MaterialRenderState();
m_shaderPass.Clear();
m_tags.Clear();
m_keywordSet.enabledKeywords.Clear();
m_properties.Clear();
m_constantLayout.Clear();
m_textureBindings.Clear();
@@ -336,6 +353,7 @@ void Material::Release() {
void Material::SetShader(const ResourceHandle<Shader>& shader) {
m_shader = shader;
SyncShaderSchemaProperties(true);
SyncShaderSchemaKeywords(true);
MarkChanged(true);
}
@@ -420,6 +438,90 @@ Containers::String Material::GetTagValue(Core::uint32 index) const {
return index < m_tags.Size() ? m_tags[index].value : Containers::String();
}
void Material::EnableKeyword(const Containers::String& keyword) {
const Containers::String normalizedKeyword = NormalizeShaderKeyword(keyword);
if (normalizedKeyword.Empty()) {
return;
}
if (m_shader.Get() != nullptr && !m_shader->DeclaresKeyword(normalizedKeyword)) {
return;
}
for (const Containers::String& existingKeyword : m_keywordSet.enabledKeywords) {
if (existingKeyword == normalizedKeyword) {
return;
}
}
m_keywordSet.enabledKeywords.PushBack(normalizedKeyword);
std::sort(
m_keywordSet.enabledKeywords.begin(),
m_keywordSet.enabledKeywords.end(),
CompareShaderKeywords);
MarkChanged(false);
}
void Material::DisableKeyword(const Containers::String& keyword) {
const Containers::String normalizedKeyword = NormalizeShaderKeyword(keyword);
if (normalizedKeyword.Empty()) {
return;
}
for (size_t keywordIndex = 0; keywordIndex < m_keywordSet.enabledKeywords.Size(); ++keywordIndex) {
if (m_keywordSet.enabledKeywords[keywordIndex] == normalizedKeyword) {
if (keywordIndex != m_keywordSet.enabledKeywords.Size() - 1) {
m_keywordSet.enabledKeywords[keywordIndex] = std::move(m_keywordSet.enabledKeywords.Back());
}
m_keywordSet.enabledKeywords.PopBack();
std::sort(
m_keywordSet.enabledKeywords.begin(),
m_keywordSet.enabledKeywords.end(),
CompareShaderKeywords);
MarkChanged(false);
return;
}
}
}
void Material::SetKeywordEnabled(const Containers::String& keyword, bool enabled) {
if (enabled) {
EnableKeyword(keyword);
} else {
DisableKeyword(keyword);
}
}
bool Material::IsKeywordEnabled(const Containers::String& keyword) const {
const Containers::String normalizedKeyword = NormalizeShaderKeyword(keyword);
if (normalizedKeyword.Empty()) {
return false;
}
for (const Containers::String& existingKeyword : m_keywordSet.enabledKeywords) {
if (existingKeyword == normalizedKeyword) {
return true;
}
}
return false;
}
void Material::ClearKeywords() {
if (m_keywordSet.enabledKeywords.Empty()) {
return;
}
m_keywordSet.enabledKeywords.Clear();
MarkChanged(false);
}
Containers::String Material::GetKeyword(Core::uint32 index) const {
return index < m_keywordSet.enabledKeywords.Size()
? m_keywordSet.enabledKeywords[index]
: Containers::String();
}
void Material::SetFloat(const Containers::String& name, float value) {
if (!CanAssignPropertyType(name, MaterialPropertyType::Float)) {
return;
@@ -976,6 +1078,29 @@ void Material::SyncShaderSchemaProperties(bool removeUnknownProperties) {
}
}
void Material::SyncShaderSchemaKeywords(bool removeUnknownKeywords) {
if (m_shader.Get() == nullptr || !removeUnknownKeywords) {
return;
}
for (size_t keywordIndex = 0; keywordIndex < m_keywordSet.enabledKeywords.Size();) {
if (m_shader->DeclaresKeyword(m_keywordSet.enabledKeywords[keywordIndex])) {
++keywordIndex;
continue;
}
if (keywordIndex != m_keywordSet.enabledKeywords.Size() - 1) {
m_keywordSet.enabledKeywords[keywordIndex] = std::move(m_keywordSet.enabledKeywords.Back());
}
m_keywordSet.enabledKeywords.PopBack();
}
std::sort(
m_keywordSet.enabledKeywords.begin(),
m_keywordSet.enabledKeywords.end(),
CompareShaderKeywords);
}
void Material::MarkChanged(bool updateConstantBuffer) {
if (updateConstantBuffer) {
UpdateConstantBuffer();
@@ -992,6 +1117,7 @@ void Material::UpdateMemorySize() {
sizeof(MaterialRenderState) +
m_shaderPass.Length() +
m_tags.Size() * sizeof(MaterialTagEntry) +
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
m_properties.Size() * sizeof(MaterialProperty) +
m_name.Length() +
@@ -1002,6 +1128,10 @@ void Material::UpdateMemorySize() {
m_memorySize += tag.value.Length();
}
for (const Containers::String& keyword : m_keywordSet.enabledKeywords) {
m_memorySize += keyword.Length();
}
for (const MaterialConstantFieldDesc& field : m_constantLayout) {
m_memorySize += field.name.Length();
}