rendering: generate single-source shader keyword variants

This commit is contained in:
2026-04-06 19:58:49 +08:00
parent 261dd44fd5
commit f16620afc6
2 changed files with 156 additions and 30 deletions

View File

@@ -954,6 +954,82 @@ Containers::String StripUnityStyleAuthoringPragmas(const Containers::String& sou
return strippedSource.c_str();
}
Containers::String BuildShaderKeywordSetSignature(const ShaderKeywordSet& keywordSet) {
ShaderKeywordSet normalizedKeywords = keywordSet;
NormalizeShaderKeywordSetInPlace(normalizedKeywords);
Containers::String signature;
for (size_t keywordIndex = 0; keywordIndex < normalizedKeywords.enabledKeywords.Size(); ++keywordIndex) {
if (keywordIndex > 0) {
signature += ";";
}
signature += normalizedKeywords.enabledKeywords[keywordIndex];
}
return signature;
}
std::vector<ShaderKeywordSet> BuildShaderKeywordVariantSets(
const Containers::Array<ShaderKeywordDeclaration>& declarations) {
std::vector<ShaderKeywordSet> keywordSets(1);
for (const ShaderKeywordDeclaration& declaration : declarations) {
if (declaration.options.Empty()) {
continue;
}
std::vector<ShaderKeywordSet> nextKeywordSets;
std::unordered_set<std::string> seenSignatures;
nextKeywordSets.reserve(keywordSets.size() * declaration.options.Size());
for (const ShaderKeywordSet& currentKeywordSet : keywordSets) {
for (const Containers::String& option : declaration.options) {
ShaderKeywordSet nextKeywordSet = currentKeywordSet;
const Containers::String normalizedKeyword = NormalizeShaderKeywordToken(option);
if (!normalizedKeyword.Empty()) {
nextKeywordSet.enabledKeywords.PushBack(normalizedKeyword);
}
NormalizeShaderKeywordSetInPlace(nextKeywordSet);
const std::string signature =
ToStdString(BuildShaderKeywordSetSignature(nextKeywordSet));
if (seenSignatures.insert(signature).second) {
nextKeywordSets.push_back(std::move(nextKeywordSet));
}
}
}
if (!nextKeywordSets.empty()) {
keywordSets = std::move(nextKeywordSets);
}
}
if (keywordSets.empty()) {
keywordSets.emplace_back();
}
return keywordSets;
}
Containers::String BuildSingleSourceVariantSource(
const Containers::String& strippedCombinedSource,
const ShaderKeywordSet& requiredKeywords) {
Containers::String variantSource;
for (const Containers::String& keyword : requiredKeywords.enabledKeywords) {
variantSource += "#define ";
variantSource += keyword;
variantSource += " 1\n";
}
if (!variantSource.Empty() && !strippedCombinedSource.Empty()) {
variantSource += '\n';
}
variantSource += strippedCombinedSource;
return variantSource;
}
bool TryParseShaderKeywordDeclarationPragma(
const std::vector<std::string>& pragmaTokens,
ShaderKeywordDeclaration& outDeclaration) {
@@ -2043,37 +2119,47 @@ LoadResult BuildShaderFromAuthoringDesc(
AppendAuthoringSourceBlock(combinedSource, subShader.sharedProgramSource);
AppendAuthoringSourceBlock(combinedSource, pass.sharedProgramSource);
AppendAuthoringSourceBlock(combinedSource, pass.programSource);
combinedSource = StripUnityStyleAuthoringPragmas(combinedSource);
const Containers::String strippedCombinedSource =
StripUnityStyleAuthoringPragmas(combinedSource);
const std::vector<ShaderKeywordSet> keywordSets =
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
ShaderStageVariant vertexVariant = {};
vertexVariant.stage = ShaderType::Vertex;
vertexVariant.backend = ShaderBackend::Generic;
vertexVariant.language = ShaderLanguage::HLSL;
vertexVariant.entryPoint =
!pass.vertexEntryPoint.Empty()
? pass.vertexEntryPoint
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Vertex);
vertexVariant.profile = GetDefaultProfile(
ShaderLanguage::HLSL,
ShaderBackend::Generic,
ShaderType::Vertex);
vertexVariant.sourceCode = combinedSource;
shader->AddPassVariant(pass.name, vertexVariant);
for (const ShaderKeywordSet& keywordSet : keywordSets) {
const Containers::String variantSource =
BuildSingleSourceVariantSource(strippedCombinedSource, keywordSet);
ShaderStageVariant fragmentVariant = {};
fragmentVariant.stage = ShaderType::Fragment;
fragmentVariant.backend = ShaderBackend::Generic;
fragmentVariant.language = ShaderLanguage::HLSL;
fragmentVariant.entryPoint =
!pass.fragmentEntryPoint.Empty()
? pass.fragmentEntryPoint
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Fragment);
fragmentVariant.profile = GetDefaultProfile(
ShaderLanguage::HLSL,
ShaderBackend::Generic,
ShaderType::Fragment);
fragmentVariant.sourceCode = combinedSource;
shader->AddPassVariant(pass.name, fragmentVariant);
ShaderStageVariant vertexVariant = {};
vertexVariant.stage = ShaderType::Vertex;
vertexVariant.backend = ShaderBackend::Generic;
vertexVariant.language = ShaderLanguage::HLSL;
vertexVariant.requiredKeywords = keywordSet;
vertexVariant.entryPoint =
!pass.vertexEntryPoint.Empty()
? pass.vertexEntryPoint
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Vertex);
vertexVariant.profile = GetDefaultProfile(
ShaderLanguage::HLSL,
ShaderBackend::Generic,
ShaderType::Vertex);
vertexVariant.sourceCode = variantSource;
shader->AddPassVariant(pass.name, vertexVariant);
ShaderStageVariant fragmentVariant = {};
fragmentVariant.stage = ShaderType::Fragment;
fragmentVariant.backend = ShaderBackend::Generic;
fragmentVariant.language = ShaderLanguage::HLSL;
fragmentVariant.requiredKeywords = keywordSet;
fragmentVariant.entryPoint =
!pass.fragmentEntryPoint.Empty()
? pass.fragmentEntryPoint
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Fragment);
fragmentVariant.profile = GetDefaultProfile(
ShaderLanguage::HLSL,
ShaderBackend::Generic,
ShaderType::Fragment);
fragmentVariant.sourceCode = variantSource;
shader->AddPassVariant(pass.name, fragmentVariant);
}
}
}
}