[Chore] (MG_Backend/DirectVulkan): make Photon fix a SPIR-V patch rather than source-level

This commit is contained in:
2026-05-09 17:46:16 +08:00
parent c5bf0dc07d
commit e13b5d0618
7 changed files with 733 additions and 248 deletions
@@ -13,253 +13,10 @@
namespace {
using MobileGL::SizeT;
struct FlattenedVaryingMember {
const char* typeName;
const char* memberName;
};
constexpr FlattenedVaryingMember kDailyWeatherVariationMembers[] = {
{"vec2", "clouds_cumulus_coverage"},
{"vec2", "clouds_altocumulus_coverage"},
{"vec2", "clouds_cirrus_coverage"},
{"float", "clouds_cumulus_congestus_amount"},
{"float", "clouds_stratus_amount"},
{"float", "fogginess"},
{"float", "aurora_amount"},
{"float", "nlc_amount"},
{"mat2x3", "aurora_colors"},
};
bool IsIdentifierChar(char ch) {
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_';
}
bool HasIdentifierBoundaries(const MobileGL::String& source, SizeT pos, SizeT length) {
const bool hasLeftBoundary = pos == 0 || !IsIdentifierChar(source[pos - 1]);
const SizeT end = pos + length;
const bool hasRightBoundary = end >= source.size() || !IsIdentifierChar(source[end]);
return hasLeftBoundary && hasRightBoundary;
}
SizeT FindToken(const MobileGL::String& source, const MobileGL::String& token, SizeT start = 0) {
SizeT pos = start;
while ((pos = source.find(token, pos)) != MobileGL::String::npos) {
if (HasIdentifierBoundaries(source, pos, token.size())) {
return pos;
}
pos += token.size();
}
return MobileGL::String::npos;
}
void ReplaceTokenOccurrencesInRange(MobileGL::String& source, SizeT rangeStart, SizeT rangeEnd,
const MobileGL::String& from, const MobileGL::String& to) {
SizeT pos = rangeStart;
while ((pos = source.find(from, pos)) != MobileGL::String::npos && pos < rangeEnd) {
if (!HasIdentifierBoundaries(source, pos, from.size())) {
pos += from.size();
continue;
}
source.replace(pos, from.size(), to);
const auto delta = static_cast<std::ptrdiff_t>(to.size()) - static_cast<std::ptrdiff_t>(from.size());
rangeEnd = static_cast<SizeT>(static_cast<std::ptrdiff_t>(rangeEnd) + delta);
pos += to.size();
}
}
void ReplaceAll(MobileGL::String& source, const MobileGL::String& from, const MobileGL::String& to) {
SizeT pos = 0;
while ((pos = source.find(from, pos)) != MobileGL::String::npos) {
source.replace(pos, from.size(), to);
pos += to.size();
}
}
MobileGL::String TrimWhitespace(const MobileGL::String& input) {
SizeT begin = 0;
while (begin < input.size() && std::isspace(static_cast<unsigned char>(input[begin]))) {
begin++;
}
SizeT end = input.size();
while (end > begin && std::isspace(static_cast<unsigned char>(input[end - 1]))) {
end--;
}
return input.substr(begin, end - begin);
}
bool FindFunctionBody(const MobileGL::String& source, const MobileGL::String& signature, SizeT* bodyStart,
SizeT* bodyEnd) {
const SizeT signaturePos = source.find(signature);
if (signaturePos == MobileGL::String::npos) {
return false;
}
const SizeT bracePos = source.find('{', signaturePos + signature.size());
if (bracePos == MobileGL::String::npos) {
return false;
}
int depth = 1;
for (SizeT pos = bracePos + 1; pos < source.size(); pos++) {
if (source[pos] == '{') {
depth++;
} else if (source[pos] == '}') {
depth--;
if (depth == 0) {
*bodyStart = bracePos + 1;
*bodyEnd = pos;
return true;
}
}
}
return false;
}
void RenameDailyWeatherVariationHelperLocal(MobileGL::String& source) {
constexpr const char* kHelperSignature = "DailyWeatherVariation get_daily_weather_variation()";
constexpr const char* kInterfaceName = "daily_weather_variation";
constexpr const char* kLocalName = "mg_daily_weather_variation_local";
SizeT bodyStart = 0;
SizeT bodyEnd = 0;
if (!FindFunctionBody(source, kHelperSignature, &bodyStart, &bodyEnd)) {
return;
}
ReplaceTokenOccurrencesInRange(source, bodyStart, bodyEnd, kInterfaceName, kLocalName);
}
bool RewriteDailyWeatherVariationInterface(MobileGL::ShaderStage stage, MobileGL::String& source) {
using MobileGL::ShaderStage;
if (stage != ShaderStage::Vertex && stage != ShaderStage::Fragment) {
return false;
}
constexpr const char* kTypeName = "DailyWeatherVariation";
constexpr const char* kInterfaceName = "daily_weather_variation";
constexpr const char* kTempName = "mg_daily_weather_variation_tmp";
const MobileGL::String declarationNeedle = MobileGL::String(kTypeName) + " " + kInterfaceName + ";";
RenameDailyWeatherVariationHelperLocal(source);
const SizeT declarationPos = source.find(declarationNeedle);
if (declarationPos == MobileGL::String::npos) {
return false;
}
SizeT lineStart = source.rfind('\n', declarationPos);
lineStart = (lineStart == MobileGL::String::npos) ? 0 : lineStart + 1;
SizeT lineEnd = source.find('\n', declarationPos);
if (lineEnd == MobileGL::String::npos) {
lineEnd = source.size();
}
const MobileGL::String declarationLine = source.substr(lineStart, lineEnd - lineStart);
MOBILEGL_ASSERT(declarationLine.find("layout(") == MobileGL::String::npos,
"PreprocessShaderSource: unexpected explicit layout on DailyWeatherVariation interface in stage=%d",
static_cast<int>(stage));
const bool hasInputQualifier = declarationLine.find(" in ") != MobileGL::String::npos ||
declarationLine.rfind("in ", 0) == 0;
const bool hasOutputQualifier = declarationLine.find(" out ") != MobileGL::String::npos ||
declarationLine.rfind("out ", 0) == 0;
MOBILEGL_ASSERT(hasInputQualifier != hasOutputQualifier,
"PreprocessShaderSource: expected a single in/out qualifier on DailyWeatherVariation interface in stage=%d line='%s'",
static_cast<int>(stage), declarationLine.c_str());
const MobileGL::String qualifierPrefix = source.substr(lineStart, declarationPos - lineStart);
MobileGL::String replacementDeclaration;
for (const auto& member : kDailyWeatherVariationMembers) {
replacementDeclaration += qualifierPrefix;
replacementDeclaration += member.typeName;
replacementDeclaration += " ";
replacementDeclaration += kInterfaceName;
replacementDeclaration += "_";
replacementDeclaration += member.memberName;
replacementDeclaration += ";\n";
}
source.replace(lineStart, lineEnd - lineStart + (lineEnd < source.size() ? 1 : 0), replacementDeclaration);
SizeT assignPos = FindToken(source, kInterfaceName);
while (assignPos != MobileGL::String::npos) {
SizeT probe = assignPos + strlen(kInterfaceName);
while (probe < source.size() && std::isspace(static_cast<unsigned char>(source[probe]))) {
probe++;
}
if (probe >= source.size() || source[probe] != '=') {
assignPos = FindToken(source, kInterfaceName, assignPos + strlen(kInterfaceName));
continue;
}
SizeT statementStart = source.rfind('\n', assignPos);
statementStart = (statementStart == MobileGL::String::npos) ? 0 : statementStart + 1;
for (SizeT i = statementStart; i < assignPos; i++) {
MOBILEGL_ASSERT(std::isspace(static_cast<unsigned char>(source[i])),
"PreprocessShaderSource: unexpected inline DailyWeatherVariation assignment in stage=%d",
static_cast<int>(stage));
}
const MobileGL::String indentation = source.substr(statementStart, assignPos - statementStart);
const SizeT statementEnd = source.find(';', probe);
MOBILEGL_ASSERT(statementEnd != MobileGL::String::npos,
"PreprocessShaderSource: missing ';' after DailyWeatherVariation assignment in stage=%d",
static_cast<int>(stage));
const MobileGL::String rhsExpression = TrimWhitespace(source.substr(probe + 1, statementEnd - probe - 1));
MobileGL::String replacementStatement;
replacementStatement += indentation;
replacementStatement += "{\n";
replacementStatement += indentation;
replacementStatement += " DailyWeatherVariation ";
replacementStatement += kTempName;
replacementStatement += " = ";
replacementStatement += rhsExpression;
replacementStatement += ";\n";
for (const auto& member : kDailyWeatherVariationMembers) {
replacementStatement += indentation;
replacementStatement += " ";
replacementStatement += kInterfaceName;
replacementStatement += "_";
replacementStatement += member.memberName;
replacementStatement += " = ";
replacementStatement += kTempName;
replacementStatement += ".";
replacementStatement += member.memberName;
replacementStatement += ";\n";
}
replacementStatement += indentation;
replacementStatement += "}";
if (statementEnd + 1 < source.size() && source[statementEnd + 1] == '\n') {
replacementStatement += "\n";
source.replace(statementStart, statementEnd - statementStart + 2, replacementStatement);
} else {
source.replace(statementStart, statementEnd - statementStart + 1, replacementStatement);
}
assignPos = FindToken(source, kInterfaceName, statementStart + replacementStatement.size());
}
for (const auto& member : kDailyWeatherVariationMembers) {
const MobileGL::String from = MobileGL::String(kInterfaceName) + "." + member.memberName;
const MobileGL::String to = MobileGL::String(kInterfaceName) + "_" + member.memberName;
ReplaceAll(source, from, to);
}
MOBILEGL_ASSERT(source.find(MobileGL::String(kTypeName) + " " + kInterfaceName + ";") == MobileGL::String::npos,
"PreprocessShaderSource: unrewritten DailyWeatherVariation interface declaration remained in stage=%d",
static_cast<int>(stage));
MOBILEGL_ASSERT(source.find(MobileGL::String(kInterfaceName) + ".") == MobileGL::String::npos,
"PreprocessShaderSource: unrewritten DailyWeatherVariation member access remained in stage=%d",
static_cast<int>(stage));
return true;
}
bool HasSingleLineFunctionDefinition(const MobileGL::String& source, const MobileGL::String& functionName) {
SizeT lineStart = 0;
while (lineStart < source.size()) {
@@ -351,8 +108,13 @@ namespace MobileGL {
SizeT linedirPos = source.find("#line");
while (linedirPos != String::npos) {
SizeT newlinePos = source.find('\n', linedirPos);
// + length of "\n"
source = source.replace(linedirPos, newlinePos - linedirPos + 1, "");
if (newlinePos == String::npos) {
source.erase(linedirPos);
break;
}
// Preserve a line break so adjacent preprocessor directives do not merge.
source = source.replace(linedirPos, newlinePos - linedirPos + 1, "\n");
linedirPos = source.find("#line", linedirPos);
}
@@ -407,8 +169,6 @@ namespace MobileGL {
RenameBuiltinShadowingFunction(source, "round", "mg_round");
RenameBuiltinShadowingFunction(source, "tanh", "mg_tanh");
RenameBuiltinShadowingFunction(source, "fma", "mg_fma");
RewriteDailyWeatherVariationInterface(stage, source);
}
} // namespace ShaderTranspiler