rendering: add keyword-aware shader variant selection
This commit is contained in:
@@ -144,6 +144,88 @@ TEST(Shader, FindsBackendSpecificVariantAndFallsBackToGeneric) {
|
||||
EXPECT_EQ(openglVariant->sourceCode, "generic fragment");
|
||||
}
|
||||
|
||||
TEST(Shader, FindsMostSpecificKeywordVariantAndPrefersExactBackend) {
|
||||
Shader shader;
|
||||
|
||||
ShaderStageVariant baseFragment = {};
|
||||
baseFragment.stage = ShaderType::Fragment;
|
||||
baseFragment.language = ShaderLanguage::GLSL;
|
||||
baseFragment.backend = ShaderBackend::Generic;
|
||||
baseFragment.sourceCode = "generic base";
|
||||
shader.AddPassVariant("ForwardLit", baseFragment);
|
||||
|
||||
ShaderStageVariant genericKeywordFragment = {};
|
||||
genericKeywordFragment.stage = ShaderType::Fragment;
|
||||
genericKeywordFragment.language = ShaderLanguage::GLSL;
|
||||
genericKeywordFragment.backend = ShaderBackend::Generic;
|
||||
genericKeywordFragment.requiredKeywords.enabledKeywords.PushBack("XC_DEBUG");
|
||||
genericKeywordFragment.requiredKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
genericKeywordFragment.requiredKeywords.enabledKeywords.PushBack("XC_DEBUG");
|
||||
genericKeywordFragment.sourceCode = "generic keyword";
|
||||
shader.AddPassVariant("ForwardLit", genericKeywordFragment);
|
||||
|
||||
ShaderStageVariant d3d12KeywordFragment = {};
|
||||
d3d12KeywordFragment.stage = ShaderType::Fragment;
|
||||
d3d12KeywordFragment.language = ShaderLanguage::HLSL;
|
||||
d3d12KeywordFragment.backend = ShaderBackend::D3D12;
|
||||
d3d12KeywordFragment.requiredKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
d3d12KeywordFragment.sourceCode = "d3d12 keyword";
|
||||
shader.AddPassVariant("ForwardLit", d3d12KeywordFragment);
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_DEBUG");
|
||||
|
||||
const ShaderStageVariant* d3d12Variant =
|
||||
shader.FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::D3D12,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(d3d12Variant, nullptr);
|
||||
EXPECT_EQ(d3d12Variant->sourceCode, "d3d12 keyword");
|
||||
|
||||
const ShaderStageVariant* openglVariant =
|
||||
shader.FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::OpenGL,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(openglVariant, nullptr);
|
||||
EXPECT_EQ(openglVariant->sourceCode, "generic keyword");
|
||||
ASSERT_EQ(openglVariant->requiredKeywords.enabledKeywords.Size(), 2u);
|
||||
EXPECT_EQ(openglVariant->requiredKeywords.enabledKeywords[0], "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(openglVariant->requiredKeywords.enabledKeywords[1], "XC_DEBUG");
|
||||
}
|
||||
|
||||
TEST(Shader, RejectsVariantWhenRequiredKeywordsAreMissing) {
|
||||
Shader shader;
|
||||
|
||||
ShaderStageVariant keywordFragment = {};
|
||||
keywordFragment.stage = ShaderType::Fragment;
|
||||
keywordFragment.language = ShaderLanguage::GLSL;
|
||||
keywordFragment.backend = ShaderBackend::OpenGL;
|
||||
keywordFragment.requiredKeywords.enabledKeywords.PushBack("XC_CLIP");
|
||||
keywordFragment.sourceCode = "clip fragment";
|
||||
shader.AddPassVariant("ForwardLit", keywordFragment);
|
||||
|
||||
EXPECT_EQ(
|
||||
shader.FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::OpenGL),
|
||||
nullptr);
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_CLIP");
|
||||
|
||||
const ShaderStageVariant* matchedVariant =
|
||||
shader.FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::OpenGL,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(matchedVariant, nullptr);
|
||||
EXPECT_EQ(matchedVariant->sourceCode, "clip fragment");
|
||||
}
|
||||
|
||||
TEST(Shader, StoresPerPassTags) {
|
||||
Shader shader;
|
||||
shader.SetPassTag("ForwardLit", "LightMode", "ForwardBase");
|
||||
|
||||
@@ -382,6 +382,66 @@ TEST(ShaderLoader, LoadLegacyBackendSplitShaderAuthoringBuildsRuntimeContract) {
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderManifestParsesVariantKeywordsAndKeywordAwareLookup) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_manifest_variant_keywords";
|
||||
const fs::path manifestPath = shaderRoot / "variant_keywords.shader";
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
fs::create_directories(shaderRoot);
|
||||
|
||||
{
|
||||
std::ofstream manifest(manifestPath);
|
||||
ASSERT_TRUE(manifest.is_open());
|
||||
manifest << "{\n";
|
||||
manifest << " \"name\": \"VariantKeywordShader\",\n";
|
||||
manifest << " \"passes\": [\n";
|
||||
manifest << " {\n";
|
||||
manifest << " \"name\": \"ForwardLit\",\n";
|
||||
manifest << " \"variants\": [\n";
|
||||
manifest << " { \"stage\": \"Vertex\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"sourceCode\": \"#version 430\\nvoid main() {}\\n\" },\n";
|
||||
manifest << " { \"stage\": \"Fragment\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"sourceCode\": \"#version 430\\n// BASE_FRAGMENT\\nvoid main() {}\\n\" },\n";
|
||||
manifest << " { \"stage\": \"Fragment\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"keywords\": [\"XC_ALPHA_TEST\", \"_\", \"XC_FOG\", \"XC_ALPHA_TEST\"], \"sourceCode\": \"#version 430\\n// KEYWORD_FRAGMENT\\nvoid main() {}\\n\" }\n";
|
||||
manifest << " ]\n";
|
||||
manifest << " }\n";
|
||||
manifest << " ]\n";
|
||||
manifest << "}\n";
|
||||
}
|
||||
|
||||
ShaderLoader loader;
|
||||
LoadResult result = loader.Load(manifestPath.string().c_str());
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
Shader* shader = static_cast<Shader*>(result.resource);
|
||||
ASSERT_NE(shader, nullptr);
|
||||
|
||||
const ShaderStageVariant* baseFragment =
|
||||
shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::OpenGL);
|
||||
ASSERT_NE(baseFragment, nullptr);
|
||||
EXPECT_NE(std::string(baseFragment->sourceCode.CStr()).find("BASE_FRAGMENT"), std::string::npos);
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_FOG");
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
|
||||
const ShaderStageVariant* keywordFragment =
|
||||
shader->FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::OpenGL,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(keywordFragment, nullptr);
|
||||
EXPECT_NE(std::string(keywordFragment->sourceCode.CStr()).find("KEYWORD_FRAGMENT"), std::string::npos);
|
||||
ASSERT_EQ(keywordFragment->requiredKeywords.enabledKeywords.Size(), 2u);
|
||||
EXPECT_EQ(keywordFragment->requiredKeywords.enabledKeywords[0], "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(keywordFragment->requiredKeywords.enabledKeywords[1], "XC_FOG");
|
||||
|
||||
delete shader;
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringBuildsGenericHlslVariants) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -680,7 +740,8 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactAndLoaderReadsItBack) {
|
||||
manifest << " ],\n";
|
||||
manifest << " \"variants\": [\n";
|
||||
manifest << " { \"stage\": \"Vertex\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"source\": \"lit.vert.glsl\" },\n";
|
||||
manifest << " { \"stage\": \"Fragment\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"source\": \"lit.frag.glsl\" }\n";
|
||||
manifest << " { \"stage\": \"Fragment\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"source\": \"lit.frag.glsl\" },\n";
|
||||
manifest << " { \"stage\": \"Fragment\", \"backend\": \"OpenGL\", \"language\": \"GLSL\", \"keywords\": [\"XC_ALPHA_TEST\", \"XC_FOG\"], \"sourceCode\": \"#version 430\\n// ARTIFACT_GL_PS_KEYWORD\\nvoid main() {}\\n\" }\n";
|
||||
manifest << " ]\n";
|
||||
manifest << " }\n";
|
||||
manifest << " ]\n";
|
||||
@@ -711,7 +772,7 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactAndLoaderReadsItBack) {
|
||||
|
||||
const ShaderPass* pass = shader->FindPass("ForwardLit");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
ASSERT_EQ(pass->variants.Size(), 2u);
|
||||
ASSERT_EQ(pass->variants.Size(), 3u);
|
||||
ASSERT_EQ(pass->resources.Size(), 1u);
|
||||
|
||||
const ShaderStageVariant* fragmentVariant =
|
||||
@@ -719,6 +780,22 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactAndLoaderReadsItBack) {
|
||||
ASSERT_NE(fragmentVariant, nullptr);
|
||||
EXPECT_NE(std::string(fragmentVariant->sourceCode.CStr()).find("ARTIFACT_GL_PS"), std::string::npos);
|
||||
|
||||
ShaderKeywordSet enabledKeywords = {};
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_FOG");
|
||||
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
|
||||
|
||||
const ShaderStageVariant* keywordVariant =
|
||||
shader->FindVariant(
|
||||
"ForwardLit",
|
||||
ShaderType::Fragment,
|
||||
ShaderBackend::OpenGL,
|
||||
enabledKeywords);
|
||||
ASSERT_NE(keywordVariant, nullptr);
|
||||
EXPECT_NE(std::string(keywordVariant->sourceCode.CStr()).find("ARTIFACT_GL_PS_KEYWORD"), std::string::npos);
|
||||
ASSERT_EQ(keywordVariant->requiredKeywords.enabledKeywords.Size(), 2u);
|
||||
EXPECT_EQ(keywordVariant->requiredKeywords.enabledKeywords[0], "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(keywordVariant->requiredKeywords.enabledKeywords[1], "XC_FOG");
|
||||
|
||||
delete shader;
|
||||
database.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
|
||||
Reference in New Issue
Block a user