rendering: formalize shader keyword metadata contract

This commit is contained in:
2026-04-06 18:55:26 +08:00
parent 7acc397714
commit a8b4da16a3
16 changed files with 795 additions and 16 deletions

View File

@@ -611,6 +611,7 @@ struct AuthoringPassEntry {
Containers::String name;
std::vector<AuthoringTagEntry> tags;
Containers::Array<ShaderResourceBindingDesc> resources;
Containers::Array<ShaderKeywordDeclaration> keywordDeclarations;
Containers::String vertexEntryPoint;
Containers::String fragmentEntryPoint;
Containers::String sharedProgramSource;
@@ -924,6 +925,31 @@ Containers::String StripUnityStyleAuthoringPragmas(const Containers::String& sou
return strippedSource.c_str();
}
bool TryParseShaderKeywordDeclarationPragma(
const std::vector<std::string>& pragmaTokens,
ShaderKeywordDeclaration& outDeclaration) {
outDeclaration = {};
if (pragmaTokens.size() < 3u) {
return false;
}
if (pragmaTokens[1] == "multi_compile") {
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompile;
} else if (pragmaTokens[1] == "shader_feature") {
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeature;
} else if (pragmaTokens[1] == "shader_feature_local") {
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeatureLocal;
} else {
return false;
}
for (size_t tokenIndex = 2; tokenIndex < pragmaTokens.size(); ++tokenIndex) {
outDeclaration.options.PushBack(pragmaTokens[tokenIndex].c_str());
}
return !outDeclaration.options.Empty();
}
size_t FindMatchingDelimiter(
const std::string& text,
size_t openPos,
@@ -1703,6 +1729,12 @@ bool ParseUnityStyleSingleSourceShaderAuthoring(
(pragmaTokens[1] == "multi_compile" ||
pragmaTokens[1] == "shader_feature" ||
pragmaTokens[1] == "shader_feature_local")) {
ShaderKeywordDeclaration declaration = {};
if (!TryParseShaderKeywordDeclarationPragma(pragmaTokens, declaration)) {
return fail("keyword pragma must declare at least one option", humanLine);
}
currentPass->keywordDeclarations.PushBack(declaration);
continue;
}
@@ -1932,6 +1964,9 @@ LoadResult BuildShaderFromAuthoringDesc(
for (const ShaderResourceBindingDesc& resourceBinding : pass.resources) {
shader->AddPassResourceBinding(pass.name, resourceBinding);
}
for (const ShaderKeywordDeclaration& keywordDeclaration : pass.keywordDeclarations) {
shader->AddPassKeywordDeclaration(pass.name, keywordDeclaration);
}
if (!pass.backendVariants.empty()) {
for (const AuthoringBackendVariantEntry& backendVariant : pass.backendVariants) {
@@ -2182,6 +2217,11 @@ size_t CalculateShaderMemorySize(const Shader& shader) {
memorySize += binding.name.Length();
memorySize += binding.semantic.Length();
}
for (const ShaderKeywordDeclaration& declaration : pass.keywordDeclarations) {
for (const Containers::String& option : declaration.options) {
memorySize += option.Length();
}
}
for (const ShaderStageVariant& variant : pass.variants) {
memorySize += variant.entryPoint.Length();
memorySize += variant.profile.Length();
@@ -2439,7 +2479,10 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
}
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
if (magic != "XCSHD01" || fileHeader.schemaVersion != kShaderArtifactSchemaVersion) {
const bool isLegacySchema = magic == "XCSHD01" && fileHeader.schemaVersion == 1u;
const bool isCurrentSchema =
magic == "XCSHD02" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion;
if (!isLegacySchema && !isCurrentSchema) {
return LoadResult("Invalid shader artifact header: " + path);
}
@@ -2478,17 +2521,40 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
for (Core::uint32 passIndex = 0; passIndex < shaderHeader.passCount; ++passIndex) {
Containers::String passName;
ShaderPassArtifactHeader passHeader;
if (!ReadShaderArtifactString(data, offset, passName) ||
!ReadShaderArtifactValue(data, offset, passHeader)) {
Core::uint32 tagCount = 0;
Core::uint32 resourceCount = 0;
Core::uint32 keywordDeclarationCount = 0;
Core::uint32 variantCount = 0;
if (!ReadShaderArtifactString(data, offset, passName)) {
return LoadResult("Failed to read shader artifact passes: " + path);
}
if (isLegacySchema) {
ShaderPassArtifactHeaderV1 passHeader = {};
if (!ReadShaderArtifactValue(data, offset, passHeader)) {
return LoadResult("Failed to read shader artifact passes: " + path);
}
tagCount = passHeader.tagCount;
resourceCount = passHeader.resourceCount;
variantCount = passHeader.variantCount;
} else {
ShaderPassArtifactHeader passHeader = {};
if (!ReadShaderArtifactValue(data, offset, passHeader)) {
return LoadResult("Failed to read shader artifact passes: " + path);
}
tagCount = passHeader.tagCount;
resourceCount = passHeader.resourceCount;
keywordDeclarationCount = passHeader.keywordDeclarationCount;
variantCount = passHeader.variantCount;
}
ShaderPass pass = {};
pass.name = passName;
shader->AddPass(pass);
for (Core::uint32 tagIndex = 0; tagIndex < passHeader.tagCount; ++tagIndex) {
for (Core::uint32 tagIndex = 0; tagIndex < tagCount; ++tagIndex) {
Containers::String tagName;
Containers::String tagValue;
if (!ReadShaderArtifactString(data, offset, tagName) ||
@@ -2499,7 +2565,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
shader->SetPassTag(passName, tagName, tagValue);
}
for (Core::uint32 resourceIndex = 0; resourceIndex < passHeader.resourceCount; ++resourceIndex) {
for (Core::uint32 resourceIndex = 0; resourceIndex < resourceCount; ++resourceIndex) {
ShaderResourceBindingDesc binding = {};
ShaderResourceArtifact resourceArtifact;
if (!ReadShaderArtifactString(data, offset, binding.name) ||
@@ -2514,7 +2580,29 @@ LoadResult LoadShaderArtifact(const Containers::String& path) {
shader->AddPassResourceBinding(passName, binding);
}
for (Core::uint32 variantIndex = 0; variantIndex < passHeader.variantCount; ++variantIndex) {
for (Core::uint32 declarationIndex = 0; declarationIndex < keywordDeclarationCount; ++declarationIndex) {
ShaderKeywordDeclaration declaration = {};
ShaderKeywordDeclarationArtifactHeader declarationHeader = {};
if (!ReadShaderArtifactValue(data, offset, declarationHeader)) {
return LoadResult("Failed to read shader artifact pass keywords: " + path);
}
declaration.type =
static_cast<ShaderKeywordDeclarationType>(declarationHeader.declarationType);
declaration.options.Reserve(declarationHeader.optionCount);
for (Core::uint32 optionIndex = 0; optionIndex < declarationHeader.optionCount; ++optionIndex) {
Containers::String option;
if (!ReadShaderArtifactString(data, offset, option)) {
return LoadResult("Failed to read shader artifact keyword options: " + path);
}
declaration.options.PushBack(option);
}
shader->AddPassKeywordDeclaration(passName, declaration);
}
for (Core::uint32 variantIndex = 0; variantIndex < variantCount; ++variantIndex) {
ShaderStageVariant variant = {};
ShaderVariantArtifactHeader variantHeader;
if (!ReadShaderArtifactValue(data, offset, variantHeader) ||