resources: support multi_compile_local shader keywords

This commit is contained in:
2026-04-06 20:38:32 +08:00
parent c318f34f07
commit eea38d57d1
3 changed files with 78 additions and 5 deletions

View File

@@ -13,7 +13,8 @@ namespace Resources {
enum class ShaderKeywordDeclarationType : Core::uint8 {
MultiCompile = 0,
ShaderFeature,
ShaderFeatureLocal
ShaderFeatureLocal,
MultiCompileLocal = 3
};
struct ShaderKeywordDeclaration {
@@ -21,7 +22,8 @@ struct ShaderKeywordDeclaration {
Containers::Array<Containers::String> options;
bool IsLocal() const {
return type == ShaderKeywordDeclarationType::ShaderFeatureLocal;
return type == ShaderKeywordDeclarationType::ShaderFeatureLocal ||
type == ShaderKeywordDeclarationType::MultiCompileLocal;
}
};

View File

@@ -928,6 +928,7 @@ bool IsUnityStyleAuthoringPragmaDirective(const std::string& line) {
pragmaTokens[1] == "fragment" ||
pragmaTokens[1] == "target" ||
pragmaTokens[1] == "multi_compile" ||
pragmaTokens[1] == "multi_compile_local" ||
pragmaTokens[1] == "shader_feature" ||
pragmaTokens[1] == "shader_feature_local" ||
pragmaTokens[1] == "backend";
@@ -1037,6 +1038,8 @@ bool TryParseShaderKeywordDeclarationPragma(
if (pragmaTokens[1] == "multi_compile") {
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompile;
} else if (pragmaTokens[1] == "multi_compile_local") {
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompileLocal;
} else if (pragmaTokens[1] == "shader_feature") {
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeature;
} else if (pragmaTokens[1] == "shader_feature_local") {
@@ -1550,6 +1553,7 @@ bool ParseLegacyBackendSplitShaderAuthoring(
}
if (pragmaTokens.size() >= 2u &&
(pragmaTokens[1] == "multi_compile" ||
pragmaTokens[1] == "multi_compile_local" ||
pragmaTokens[1] == "shader_feature" ||
pragmaTokens[1] == "shader_feature_local")) {
ShaderKeywordDeclaration declaration = {};
@@ -1842,6 +1846,7 @@ bool ParseUnityStyleSingleSourceShaderAuthoring(
}
if (pragmaTokens.size() >= 2u &&
(pragmaTokens[1] == "multi_compile" ||
pragmaTokens[1] == "multi_compile_local" ||
pragmaTokens[1] == "shader_feature" ||
pragmaTokens[1] == "shader_feature_local")) {
ShaderKeywordDeclaration declaration = {};

View File

@@ -478,7 +478,7 @@ TEST(ShaderLoader, LoadLegacyBackendSplitShaderAuthoringExpandsKeywordVariantsPe
#pragma vertex MainVS
#pragma fragment MainPS
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
#pragma shader_feature_local _ XC_ALPHA_TEST
#pragma multi_compile_local _ XC_ALPHA_TEST
#pragma backend D3D12 HLSL "stages/keyword_test.vs.hlsl" "stages/keyword_test.ps.hlsl" vs_5_0 ps_5_0
#pragma backend OpenGL GLSL "stages/keyword_test.vert.glsl" "stages/keyword_test.frag.glsl"
ENDHLSL
@@ -501,7 +501,8 @@ TEST(ShaderLoader, LoadLegacyBackendSplitShaderAuthoringExpandsKeywordVariantsPe
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->keywordDeclarations.Size(), 2u);
EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompile);
EXPECT_EQ(pass->keywordDeclarations[1].type, ShaderKeywordDeclarationType::ShaderFeatureLocal);
EXPECT_EQ(pass->keywordDeclarations[1].type, ShaderKeywordDeclarationType::MultiCompileLocal);
EXPECT_TRUE(pass->keywordDeclarations[1].IsLocal());
EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_MAIN_LIGHT_SHADOWS"));
EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_ALPHA_TEST"));
ASSERT_EQ(pass->variants.Size(), 16u);
@@ -702,6 +703,68 @@ TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringBuildsGenericHlslVar
fs::remove_all(shaderRoot);
}
TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringParsesMultiCompileLocalKeywords) {
namespace fs = std::filesystem;
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_single_source_multi_compile_local";
const fs::path shaderPath = shaderRoot / "single_source_multi_compile_local.shader";
fs::remove_all(shaderRoot);
fs::create_directories(shaderRoot);
WriteTextFile(
shaderPath,
R"(Shader "SingleSourceLocalKeywords"
{
SubShader
{
Pass
{
Name "ForwardLit"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#pragma multi_compile_local _ XC_LOCAL_FOG
float4 Vert() : SV_POSITION { return 0; }
float4 Frag() : SV_TARGET { return 1; }
ENDHLSL
}
}
}
)");
ShaderLoader loader;
LoadResult result = loader.Load(shaderPath.string().c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->keywordDeclarations.Size(), 1u);
EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompileLocal);
EXPECT_TRUE(pass->keywordDeclarations[0].IsLocal());
ASSERT_EQ(pass->variants.Size(), 4u);
ShaderKeywordSet enabledKeywords = {};
enabledKeywords.enabledKeywords.PushBack("XC_LOCAL_FOG");
const ShaderStageVariant* keywordFragmentVariant =
shader->FindVariant(
"ForwardLit",
ShaderType::Fragment,
ShaderBackend::D3D12,
enabledKeywords);
ASSERT_NE(keywordFragmentVariant, nullptr);
EXPECT_NE(
std::string(keywordFragmentVariant->sourceCode.CStr()).find("#define XC_LOCAL_FOG 1"),
std::string::npos);
delete shader;
fs::remove_all(shaderRoot);
}
TEST(ShaderLoader, LoadUnityStyleSingleSourceShaderAuthoringRejectsBackendPragma) {
namespace fs = std::filesystem;
@@ -1051,7 +1114,7 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromLegacyBackendSplitAutho
#pragma vertex MainVS
#pragma fragment MainPS
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
#pragma shader_feature_local _ XC_ALPHA_TEST
#pragma multi_compile_local _ XC_ALPHA_TEST
#pragma backend D3D12 HLSL "stages/legacy_keywords.vs.hlsl" "stages/legacy_keywords.ps.hlsl" vs_5_0 ps_5_0
ENDHLSL
}
@@ -1080,6 +1143,9 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromLegacyBackendSplitAutho
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->keywordDeclarations.Size(), 2u);
EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompile);
EXPECT_EQ(pass->keywordDeclarations[1].type, ShaderKeywordDeclarationType::MultiCompileLocal);
EXPECT_TRUE(pass->keywordDeclarations[1].IsLocal());
ASSERT_EQ(pass->variants.Size(), 8u);
ShaderKeywordSet enabledKeywords = {};