mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 20:58:31 +09:00
[Fix] (MG_Util/ShaderTranspiler): keep #line directives instead of deleting them, dropping only the GLSL-illegal quoted filename and the ones that precede #version, so __LINE__ and compiler diagnostics follow the application's own numbering
This commit is contained in:
@@ -446,6 +446,9 @@ void main() {
|
||||
EXPECT_NE(versionPos, String::npos);
|
||||
EXPECT_EQ(outputPos, versionPos + std::strlen("#version 330 core\n"));
|
||||
EXPECT_NE(source.find("// #version 460 core"), String::npos);
|
||||
// This #line sits ahead of the version directive, where GLSL would never have honoured it, so
|
||||
// it is still dropped. Directives that follow the version line are kept - see
|
||||
// PreprocessKeepsPlainLineDirectivesAndSparesLookalikeIdentifiers.
|
||||
EXPECT_EQ(source.find("#line"), String::npos);
|
||||
|
||||
ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source};
|
||||
@@ -526,6 +529,34 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
// KHR-GL33.shaders.preprocessor.builtin.line_* checks that __LINE__ follows #line. That only works
|
||||
// if the directive reaches glslang, so a plain integer form must pass through untouched - while
|
||||
// "#linear" and friends must not be mistaken for it.
|
||||
TEST_F(ProgramUtilTest, PreprocessKeepsPlainLineDirectivesAndSparesLookalikeIdentifiers) {
|
||||
using namespace MG_Util::ShaderTranspiler;
|
||||
|
||||
String source = R"(#version 330 core
|
||||
out vec4 fragColor;
|
||||
#line 42
|
||||
float linear(float x) { return x; }
|
||||
void main() {
|
||||
#line 100
|
||||
fragColor = vec4(linear(float(__LINE__)));
|
||||
}
|
||||
)";
|
||||
PreprocessShaderSource(ShaderStage::Fragment, source);
|
||||
|
||||
EXPECT_NE(source.find("#line 42"), String::npos) << source;
|
||||
EXPECT_NE(source.find("#line 100"), String::npos) << source;
|
||||
EXPECT_NE(source.find("float linear(float x)"), String::npos) << "identifier lookalike was eaten:\n" << source;
|
||||
|
||||
ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source};
|
||||
auto res = ShaderCompiler::CompileShader(attrib);
|
||||
if (!res) {
|
||||
FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ProgramUtilTest, PreprocessModernSampleQualifierStaysAtVersion460) {
|
||||
using namespace MG_Util::ShaderTranspiler;
|
||||
|
||||
|
||||
@@ -833,6 +833,58 @@ namespace {
|
||||
return info.HasVersionDirective() ? info.versionDirectiveEnd : 0;
|
||||
}
|
||||
|
||||
// GLSL's #line takes integer expressions only, but plenty of shader-pack preprocessors emit the
|
||||
// C form with a quoted filename. Deleting every #line outright made those harmless - at the cost
|
||||
// of __LINE__ reporting the position in MobileGL's rewritten text rather than the one the pack
|
||||
// author wrote, and of every later diagnostic pointing at the wrong line. Dropping just the
|
||||
// quoted operand keeps the directive doing its job and still hands glslang something it accepts.
|
||||
void NormalizeLineDirectives(MobileGL::String& source) {
|
||||
const MobileGL::String masked = MaskCommentsAndQuotedText(source);
|
||||
const SizeT versionEnd = FindAfterVersionDirective(source);
|
||||
MobileGL::String result;
|
||||
result.reserve(source.size());
|
||||
|
||||
SizeT lineStart = 0;
|
||||
while (lineStart <= source.size()) {
|
||||
SizeT lineEnd = source.find('\n', lineStart);
|
||||
const bool lastLine = lineEnd == MobileGL::String::npos;
|
||||
if (lastLine) lineEnd = source.size();
|
||||
|
||||
SizeT probe = lineStart;
|
||||
while (probe < lineEnd && (source[probe] == ' ' || source[probe] == '\t')) probe++;
|
||||
|
||||
const bool isLineDirective = masked.compare(probe, 5, "#line") == 0 &&
|
||||
(probe + 5 >= lineEnd || !IsIdentifierChar(source[probe + 5]));
|
||||
if (isLineDirective && lineStart < versionEnd) {
|
||||
// #version has to be the first token in the shader, so a #line ahead of it could
|
||||
// never have taken effect. Drop it rather than hand glslang a source it must reject
|
||||
// - some pack preprocessors emit their directives before the version line.
|
||||
} else if (isLineDirective) {
|
||||
// Keep everything up to the first quote that the masker identified as string text.
|
||||
SizeT quotePos = MobileGL::String::npos;
|
||||
for (SizeT i = probe + 5; i < lineEnd; i++) {
|
||||
if (source[i] == '"' || source[i] == '\'') {
|
||||
quotePos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (quotePos != MobileGL::String::npos) {
|
||||
result.append(source, lineStart, quotePos - lineStart);
|
||||
} else {
|
||||
result.append(source, lineStart, lineEnd - lineStart);
|
||||
}
|
||||
} else {
|
||||
result.append(source, lineStart, lineEnd - lineStart);
|
||||
}
|
||||
|
||||
if (lastLine) break;
|
||||
result.push_back('\n');
|
||||
lineStart = lineEnd + 1;
|
||||
}
|
||||
|
||||
source = std::move(result);
|
||||
}
|
||||
|
||||
bool IsExtensionAdvertised(MobileGL::GLExtension extension) {
|
||||
const auto& activeBackendObject = MobileGL::MG_Backend::pActiveBackendObject;
|
||||
if (!activeBackendObject) {
|
||||
@@ -1154,19 +1206,7 @@ namespace MobileGL {
|
||||
|
||||
BlankBlockComments(source);
|
||||
|
||||
// remove #line directives
|
||||
SizeT linedirPos = source.find("#line");
|
||||
while (linedirPos != String::npos) {
|
||||
SizeT newlinePos = source.find('\n', linedirPos);
|
||||
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);
|
||||
}
|
||||
NormalizeLineDirectives(source);
|
||||
|
||||
// remove "noperspective"
|
||||
const char* str_np = "noperspective";
|
||||
|
||||
Reference in New Issue
Block a user