|
|
@@ -2385,7 +2385,7 @@ do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
|
|
|
|
|
|
/**
|
|
|
* Loads a given GLSL file line by line, and processes any #pragma include and
|
|
|
- * once statements.
|
|
|
+ * once statements, as well as removes any comments.
|
|
|
*
|
|
|
* The set keeps track of which files we have already included, for checking
|
|
|
* recursive includes.
|
|
|
@@ -2398,7 +2398,8 @@ r_preprocess_source(ostream &out, const Filename &fn,
|
|
|
|
|
|
if (depth > glsl_include_recursion_limit) {
|
|
|
shader_cat.error()
|
|
|
- << "#pragma include nested too deeply\n";
|
|
|
+ << "GLSL includes nested too deeply, raise glsl-include-recursion-limit"
|
|
|
+ " if necessary\n";
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -2459,56 +2460,226 @@ r_preprocess_source(ostream &out, const Filename &fn,
|
|
|
|
|
|
// Iterate over the lines for things we may need to preprocess.
|
|
|
string line;
|
|
|
+ int ext_google_include = 0; // 1 = warn, 2 = enable
|
|
|
+ int ext_google_line = 0;
|
|
|
bool had_include = false;
|
|
|
int lineno = 0;
|
|
|
while (getline(*source, line)) {
|
|
|
++lineno;
|
|
|
|
|
|
- // Check if this line contains a #pragma.
|
|
|
- char pragma[64];
|
|
|
- if (line.size() < 8 ||
|
|
|
- sscanf(line.c_str(), " # pragma %63s", pragma) != 1) {
|
|
|
- // Just pass the line through unmodified.
|
|
|
- out << line << "\n";
|
|
|
+ if (line.empty()) {
|
|
|
+ out.put('\n');
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // One exception: check for an #endif after an include. We have to
|
|
|
- // restore the line number in case the include happened under an #if
|
|
|
- // block.
|
|
|
- int nread = 0;
|
|
|
- if (had_include && sscanf(line.c_str(), " # endif %n", &nread) == 0 && nread >= 6) {
|
|
|
- out << "#line " << (lineno + 1) << " " << fileno << "\n";
|
|
|
+ // If the line ends with a backslash, concatenate the following line.
|
|
|
+ // Preprocessor definitions may be broken up into multiple lines.
|
|
|
+ while (line[line.size() - 1] == '\\') {
|
|
|
+ line.resize(line.size() - 1);
|
|
|
+ string line2;
|
|
|
+
|
|
|
+ if (getline(*source, line2)) {
|
|
|
+ line += line2;
|
|
|
+ out.put('\n');
|
|
|
+ ++lineno;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Look for comments to strip. This is necessary because comments may
|
|
|
+ // appear in the middle of or around a preprocessor definition.
|
|
|
+ size_t line_comment = line.find("//");
|
|
|
+ size_t block_comment = line.find("/*");
|
|
|
+ if (line_comment < block_comment) {
|
|
|
+ // A line comment - strip off the rest of the line.
|
|
|
+ line.resize(line_comment);
|
|
|
+
|
|
|
+ } else if (block_comment < line_comment) {
|
|
|
+ // A block comment. Search for closing block.
|
|
|
+ string line2 = line.substr(block_comment + 2);
|
|
|
+
|
|
|
+ // According to the GLSL specification, a block comment is replaced with
|
|
|
+ // a single whitespace character.
|
|
|
+ line.resize(block_comment);
|
|
|
+ line += ' ';
|
|
|
+
|
|
|
+ size_t block_end = line2.find("*/");
|
|
|
+ while (block_end == string::npos) {
|
|
|
+ // Didn't find it - look in the next line.
|
|
|
+ if (getline(*source, line2)) {
|
|
|
+ out.put('\n');
|
|
|
+ ++lineno;
|
|
|
+ block_end = line2.find("*/");
|
|
|
+ } else {
|
|
|
+ shader_cat.error()
|
|
|
+ << "Expected */ before end of file " << fn << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ line += line2.substr(block_end + 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if this line contains a #directive.
|
|
|
+ char directive[64];
|
|
|
+ if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
|
|
|
+ // Nope. Just pass the line through unmodified.
|
|
|
+ out << line << "\n";
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ char pragma[64];
|
|
|
int nread = 0;
|
|
|
- if (strcmp(pragma, "include") == 0) {
|
|
|
- // Allow both double quotes and angle brackets.
|
|
|
- Filename incfn, source_dir;
|
|
|
- {
|
|
|
- char incfile[2048];
|
|
|
- if (sscanf(line.c_str(), " # pragma%*[ \t]include \"%2047[^\"]\" %n", incfile, &nread) == 1
|
|
|
- && nread == line.size()) {
|
|
|
- // A regular include, with double quotes. Probably a local file.
|
|
|
- source_dir = full_fn.get_dirname();
|
|
|
- incfn = incfile;
|
|
|
-
|
|
|
- } else if (sscanf(line.c_str(), " # pragma%*[ \t]include <%2047[^\"]> %n", incfile, &nread) == 1
|
|
|
- && nread == line.size()) {
|
|
|
- // Angled includes are also OK, but we don't search in the directory
|
|
|
- // of the source file.
|
|
|
- incfn = incfile;
|
|
|
+ // What kind of directive is it?
|
|
|
+ if (strcmp(directive, "pragma") == 0 &&
|
|
|
+ sscanf(line.c_str(), " # pragma %63s", pragma) == 1) {
|
|
|
+ if (strcmp(pragma, "include") == 0) {
|
|
|
+ // Allow both double quotes and angle brackets.
|
|
|
+ Filename incfn, source_dir;
|
|
|
+ {
|
|
|
+ char incfile[2048];
|
|
|
+ if (sscanf(line.c_str(), " # pragma%*[ \t]include \"%2047[^\"]\" %n", incfile, &nread) == 1
|
|
|
+ && nread == line.size()) {
|
|
|
+ // A regular include, with double quotes. Probably a local file.
|
|
|
+ source_dir = full_fn.get_dirname();
|
|
|
+ incfn = incfile;
|
|
|
+
|
|
|
+ } else if (sscanf(line.c_str(), " # pragma%*[ \t]include <%2047[^\"]> %n", incfile, &nread) == 1
|
|
|
+ && nread == line.size()) {
|
|
|
+ // Angled includes are also OK, but we don't search in the directory
|
|
|
+ // of the source file.
|
|
|
+ incfn = incfile;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // Couldn't parse it.
|
|
|
+ shader_cat.error()
|
|
|
+ << "Malformed #pragma include at line " << lineno
|
|
|
+ << " of file " << fn << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // OK, great. Process the include.
|
|
|
+ if (!r_preprocess_source(out, incfn, source_dir, once_files, record, depth + 1)) {
|
|
|
+ // An error occurred. Pass on the failure.
|
|
|
+ shader_cat.error(false) << "included at line "
|
|
|
+ << lineno << " of file " << fn << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Restore the line counter.
|
|
|
+ out << "#line " << (lineno + 1) << " " << fileno << " // " << fn << "\n";
|
|
|
+ had_include = true;
|
|
|
+
|
|
|
+ } else if (strcmp(pragma, "once") == 0) {
|
|
|
+ // Do a stricter syntax check, just to be extra safe.
|
|
|
+ if (sscanf(line.c_str(), " # pragma%*[ \t]once %n", &nread) != 0 ||
|
|
|
+ nread != line.size()) {
|
|
|
+ shader_cat.error()
|
|
|
+ << "Malformed #pragma once at line " << lineno
|
|
|
+ << " of file " << fn << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ once_files.insert(full_fn);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // Forward it, the driver will ignore it if it doesn't know it.
|
|
|
+ out << line << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (strcmp(directive, "endif") == 0) {
|
|
|
+ // Check for an #endif after an include. We have to restore the line
|
|
|
+ // number in case the include happened under an #if block.
|
|
|
+ out << line << "\n";
|
|
|
+ int nread = 0;
|
|
|
+ if (had_include) {
|
|
|
+ out << "#line " << (lineno + 1) << " " << fileno << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (strcmp(directive, "extension") == 0) {
|
|
|
+ // Check for special preprocessing extensions.
|
|
|
+ char extension[256];
|
|
|
+ char behavior[9];
|
|
|
+ if (sscanf(line.c_str(), " # extension%*[ \t]%255s : %8s", extension, behavior) == 2) {
|
|
|
+ // Parse the behavior string.
|
|
|
+ int mode;
|
|
|
+ if (strcmp(behavior, "require") == 0 || strcmp(behavior, "enable") == 0) {
|
|
|
+ mode = 2;
|
|
|
+ } else if (strcmp(behavior, "warn") == 0) {
|
|
|
+ mode = 1;
|
|
|
+ } else if (strcmp(behavior, "disable") == 0) {
|
|
|
+ mode = 0;
|
|
|
+ } else {
|
|
|
+ shader_cat.error()
|
|
|
+ << "Extension directive specifies invalid behavior at line "
|
|
|
+ << lineno << " of file " << fn << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strcmp(extension, "all") == 0) {
|
|
|
+ if (mode == 2) {
|
|
|
+ shader_cat.error()
|
|
|
+ << "Extension directive for 'all' may only specify 'warn' or "
|
|
|
+ "'disable' at line " << lineno << " of file " << fn
|
|
|
+ << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ ext_google_include = mode;
|
|
|
+ ext_google_line = mode;
|
|
|
+ out << line << "\n";
|
|
|
+
|
|
|
+ } else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) {
|
|
|
+ // Enable the Google extension support for #include statements.
|
|
|
+ // This also implicitly enables GL_GOOGLE_cpp_style_line_directive.
|
|
|
+ // This matches the behavior of Khronos' glslang reference compiler.
|
|
|
+ ext_google_include = mode;
|
|
|
+ ext_google_line = mode;
|
|
|
+
|
|
|
+ } else if (strcmp(extension, "GL_GOOGLE_cpp_style_line_directive") == 0) {
|
|
|
+ // Enables strings in #line statements.
|
|
|
+ ext_google_line = mode;
|
|
|
|
|
|
} else {
|
|
|
+ // It's an extension the driver should worry about.
|
|
|
+ out << line << "\n";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ shader_cat.error()
|
|
|
+ << "Failed to parse extension directive at line "
|
|
|
+ << lineno << " of file " << fn << ":\n " << line << "\n";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else if (ext_google_include > 0 && strcmp(directive, "include") == 0) {
|
|
|
+ // Warn about extension use if requested.
|
|
|
+ if (ext_google_include == 1) {
|
|
|
+ shader_cat.warning()
|
|
|
+ << "Extension GL_GOOGLE_include_directive is being used at line "
|
|
|
+ << lineno << " of file " << fn
|
|
|
+#ifndef NDEBUG
|
|
|
+ << ":\n " << line
|
|
|
+#endif
|
|
|
+ << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // This syntax allows only double quotes, not angle brackets.
|
|
|
+ Filename incfn;
|
|
|
+ {
|
|
|
+ char incfile[2048];
|
|
|
+ if (sscanf(line.c_str(), " # include%*[ \t]\"%2047[^\"]\" %n", incfile, &nread) != 1
|
|
|
+ || nread != line.size()) {
|
|
|
// Couldn't parse it.
|
|
|
shader_cat.error()
|
|
|
- << "Malformed #pragma include at line " << lineno
|
|
|
+ << "Malformed #include at line " << lineno
|
|
|
<< " of file " << fn << ":\n " << line << "\n";
|
|
|
return false;
|
|
|
}
|
|
|
+ incfn = incfile;
|
|
|
}
|
|
|
|
|
|
// OK, great. Process the include.
|
|
|
+ Filename source_dir = full_fn.get_dirname();
|
|
|
if (!r_preprocess_source(out, incfn, source_dir, once_files, record, depth + 1)) {
|
|
|
// An error occurred. Pass on the failure.
|
|
|
shader_cat.error(false) << "included at line "
|
|
|
@@ -2520,28 +2691,36 @@ r_preprocess_source(ostream &out, const Filename &fn,
|
|
|
out << "#line " << (lineno + 1) << " " << fileno << " // " << fn << "\n";
|
|
|
had_include = true;
|
|
|
|
|
|
- } else if (strcmp(pragma, "once") == 0) {
|
|
|
- // Do a stricter syntax check, just to be extra safe.
|
|
|
- if (sscanf(line.c_str(), " # pragma%*[ \t]once %n", &nread) != 0 ||
|
|
|
- nread != line.size()) {
|
|
|
- shader_cat.error()
|
|
|
- << "Malformed #pragma once at line " << lineno
|
|
|
- << " of file " << fn << ":\n " << line << "\n";
|
|
|
- return false;
|
|
|
- }
|
|
|
+ } else if (ext_google_line > 0 && strcmp(directive, "line") == 0) {
|
|
|
+ // It's a #line directive. See if it uses a string instead of number.
|
|
|
+ char filestr[2048];
|
|
|
+ if (sscanf(line.c_str(), " # line%*[ \t]%d%*[ \t]\"%2047[^\"]\" %n", &lineno, filestr, &nread) == 2
|
|
|
+ && nread == line.size()) {
|
|
|
+ // Warn about extension use if requested.
|
|
|
+ if (ext_google_line == 1) {
|
|
|
+ shader_cat.warning()
|
|
|
+ << "Extension GL_GOOGLE_cpp_style_line_directive is being used at line "
|
|
|
+ << lineno << " of file " << fn
|
|
|
+#ifndef NDEBUG
|
|
|
+ << ":\n " << line
|
|
|
+#endif
|
|
|
+ << "\n";
|
|
|
+ }
|
|
|
|
|
|
- once_files.insert(full_fn);
|
|
|
+ // Replace the string line number with an integer. This is something
|
|
|
+ // we can substitute later when parsing the GLSL log from the driver.
|
|
|
+ fileno = 2048 + _included_files.size();
|
|
|
+ _included_files.push_back(Filename(filestr));
|
|
|
|
|
|
- } else if (strcmp(pragma, "optionNV") == 0) {
|
|
|
- // This is processed by NVIDIA drivers. Don't touch it.
|
|
|
- out << line << "\n";
|
|
|
+ out << "#line " << lineno << " " << fileno << " // " << filestr << "\n";
|
|
|
|
|
|
+ } else {
|
|
|
+ // We couldn't parse the #line directive. Pass it through unmodified.
|
|
|
+ out << line << "\n";
|
|
|
+ }
|
|
|
} else {
|
|
|
- // Forward it, the driver will ignore it if it doesn't know it.
|
|
|
+ // Different directive (eg. #version). Leave it untouched.
|
|
|
out << line << "\n";
|
|
|
- shader_cat.warning()
|
|
|
- << "Ignoring unknown pragma directive \"" << pragma << "\" at line "
|
|
|
- << lineno << " of file " << fn << ":\n " << line << "\n";
|
|
|
}
|
|
|
}
|
|
|
|