rendering: generate single-source shader keyword variants
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringBuildsGenericHlslVar
|
||||
EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_MAIN_LIGHT_SHADOWS"));
|
||||
EXPECT_TRUE(shader->DeclaresKeyword("XC_ALPHA_TEST"));
|
||||
EXPECT_FALSE(shader->DeclaresKeyword("_"));
|
||||
ASSERT_EQ(pass->variants.Size(), 2u);
|
||||
ASSERT_EQ(pass->variants.Size(), 8u);
|
||||
|
||||
const ShaderStageVariant* vertexVariant =
|
||||
shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12);
|
||||
@@ -543,6 +543,8 @@ TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringBuildsGenericHlslVar
|
||||
EXPECT_EQ(vertexVariant->profile, "vs_5_0");
|
||||
EXPECT_NE(std::string(vertexVariant->sourceCode.CStr()).find("#include \"shaderlib/shared.hlsl\""), std::string::npos);
|
||||
EXPECT_NE(std::string(vertexVariant->sourceCode.CStr()).find("XC_SINGLE_SOURCE_FRAG_BODY"), std::string::npos);
|
||||
EXPECT_EQ(vertexVariant->requiredKeywords.enabledKeywords.Size(), 0u);
|
||||
EXPECT_EQ(std::string(vertexVariant->sourceCode.CStr()).find("#pragma multi_compile"), std::string::npos);
|
||||
|
||||
const ShaderStageVariant* fragmentVariant =
|
||||
shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::D3D12);
|
||||
@@ -552,6 +554,24 @@ TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringBuildsGenericHlslVar
|
||||
EXPECT_EQ(fragmentVariant->entryPoint, "Frag");
|
||||
EXPECT_EQ(fragmentVariant->profile, "ps_5_0");
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS");
|
||||
|
||||
const ShaderStageVariant* keywordFragmentVariant =
|
||||
shader->FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::D3D12,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(keywordFragmentVariant, nullptr);
|
||||
ASSERT_EQ(keywordFragmentVariant->requiredKeywords.enabledKeywords.Size(), 2u);
|
||||
EXPECT_EQ(keywordFragmentVariant->requiredKeywords.enabledKeywords[0], "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(keywordFragmentVariant->requiredKeywords.enabledKeywords[1], "XC_MAIN_LIGHT_SHADOWS");
|
||||
EXPECT_NE(std::string(keywordFragmentVariant->sourceCode.CStr()).find("#define XC_ALPHA_TEST 1"), std::string::npos);
|
||||
EXPECT_NE(std::string(keywordFragmentVariant->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"), std::string::npos);
|
||||
EXPECT_EQ(std::string(keywordFragmentVariant->sourceCode.CStr()).find("#pragma shader_feature_local"), std::string::npos);
|
||||
|
||||
Array<String> dependencies;
|
||||
ASSERT_TRUE(loader.CollectSourceDependencies(shaderPath.string().c_str(), dependencies));
|
||||
ASSERT_EQ(dependencies.Size(), 1u);
|
||||
@@ -855,9 +875,29 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromSingleSourceAuthoringPr
|
||||
const ShaderPass* pass = shader->FindPass("ForwardLit");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
ASSERT_EQ(pass->keywordDeclarations.Size(), 2u);
|
||||
ASSERT_EQ(pass->variants.Size(), 8u);
|
||||
EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompile);
|
||||
EXPECT_EQ(pass->keywordDeclarations[1].type, ShaderKeywordDeclarationType::ShaderFeatureLocal);
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS");
|
||||
|
||||
const ShaderStageVariant* keywordFragmentVariant =
|
||||
shader->FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::D3D12,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(keywordFragmentVariant, nullptr);
|
||||
ASSERT_EQ(keywordFragmentVariant->requiredKeywords.enabledKeywords.Size(), 2u);
|
||||
EXPECT_NE(
|
||||
std::string(keywordFragmentVariant->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"),
|
||||
std::string::npos);
|
||||
EXPECT_NE(
|
||||
std::string(keywordFragmentVariant->sourceCode.CStr()).find("#define XC_ALPHA_TEST 1"),
|
||||
std::string::npos);
|
||||
|
||||
delete shader;
|
||||
database.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
|
||||
Reference in New Issue
Block a user