rendering: add keyword-aware shader variant selection

This commit is contained in:
2026-04-06 19:37:01 +08:00
parent a8b4da16a3
commit 261dd44fd5
26 changed files with 469 additions and 76 deletions

View File

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