* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkString.h"
#include "include/gpu/GrContextOptions.h"
#include "src/gpu/GrShaderUtils.h"
#include "src/sksl/SkSLString.h"
namespace GrShaderUtils {
class GLSLPrettyPrint {
public:
GLSLPrettyPrint() {}
SkSL::String prettify(const SkSL::String& string) {
fTabs = 0;
fFreshline = true;
fInParseUntilNewline = false;
fInParseUntil = false;
int parensDepth = 0;
fIndex = 0;
fLength = string.length();
fInput = string.c_str();
while (fLength > fIndex) {
* be self explanatory. For '#' and '//' tokens we parse until we reach a newline.
*
* For long style comments like this one, we search for the ending token. We also
* preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
* ourselves. This allows us to remain in control of line numbers, and matching
* tabs Existing tabs in the input string are copied over too, but this will look
* funny
*
* '{' and '}' are handled in basically the same way. We add a newline if we aren't
* on a fresh line, dirty the line, then add a second newline, ie braces are always
* on their own lines indented properly. The one funkiness here is structs print
* with the semicolon on its own line. Its not a problem for a glsl compiler though
*
* '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
* in for loops.
*
* ';' means add a new line
*
* '\t' and '\n' are ignored in general parsing for backwards compatability with
* existing shader code and we also have a special case for handling whitespace
* at the beginning of fresh lines.
*
* Otherwise just add the new character to the pretty string, indenting if
* necessary.
*/
if (fInParseUntilNewline) {
this->parseUntilNewline();
} else if (fInParseUntil) {
this->parseUntil(fInParseUntilToken);
} else if (this->hasToken("#") || this->hasToken("//")) {
this->parseUntilNewline();
} else if (this->hasToken("/*")) {
this->parseUntil("*/");
} else if ('{' == fInput[fIndex]) {
this->newline();
this->appendChar('{');
fTabs++;
this->newline();
} else if ('}' == fInput[fIndex]) {
fTabs--;
this->newline();
this->appendChar('}');
this->newline();
} else if (this->hasToken(")")) {
parensDepth--;
} else if (this->hasToken("(")) {
parensDepth++;
} else if (!parensDepth && this->hasToken(";")) {
this->newline();
} else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
(fFreshline && ' ' == fInput[fIndex])) {
fIndex++;
} else {
this->appendChar(fInput[fIndex]);
}
}
return fPretty;
}
private:
void appendChar(char c) {
this->tabString();
fPretty.appendf("%c", fInput[fIndex++]);
fFreshline = false;
}
bool hasToken(const char* token) {
size_t i = fIndex;
for (size_t j = 0; token[j] && fLength > i; i++, j++) {
if (token[j] != fInput[i]) {
return false;
}
}
this->tabString();
fIndex = i;
fPretty.append(token);
fFreshline = false;
return true;
}
void parseUntilNewline() {
while (fLength > fIndex) {
if ('\n' == fInput[fIndex]) {
fIndex++;
this->newline();
fInParseUntilNewline = false;
break;
}
fPretty.appendf("%c", fInput[fIndex++]);
fInParseUntilNewline = true;
}
}
void parseUntil(const char* token) {
while (fLength > fIndex) {
if ('\n' == fInput[fIndex]) {
this->newline();
this->tabString();
fIndex++;
}
if (this->hasToken(token)) {
fInParseUntil = false;
break;
}
fFreshline = false;
fPretty.appendf("%c", fInput[fIndex++]);
fInParseUntil = true;
fInParseUntilToken = token;
}
}
void tabString() {
if (fFreshline) {
for (int t = 0; t < fTabs; t++) {
fPretty.append("\t");
}
}
}
void newline() {
if (!fFreshline) {
fFreshline = true;
fPretty.append("\n");
}
}
bool fFreshline;
int fTabs;
size_t fIndex, fLength;
const char* fInput;
SkSL::String fPretty;
bool fInParseUntilNewline;
bool fInParseUntil;
const char* fInParseUntilToken;
};
SkSL::String PrettyPrint(const SkSL::String& string) {
GLSLPrettyPrint pp;
return pp.prettify(string);
}
void PrintLineByLine(const char* header, const SkSL::String& text) {
if (header) {
SkDebugf("%s\n", header);
}
SkTArray<SkString> lines;
SkStrSplit(text.c_str(), "\n", kStrict_SkStrSplitMode, &lines);
for (int i = 0; i < lines.count(); ++i) {
SkDebugf("%4i\t%s\n", i + 1, lines[i].c_str());
}
}
GrContextOptions::ShaderErrorHandler* DefaultShaderErrorHandler() {
class GrDefaultShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
public:
void compileError(const char* shader, const char* errors) override {
SkDebugf("Shader compilation error\n"
"------------------------\n");
PrintLineByLine(nullptr, shader);
SkDebugf("Errors:\n%s\n", errors);
SkDEBUGFAIL("Shader compilation failed!");
}
};
static GrDefaultShaderErrorHandler gHandler;
return &gHandler;
}
}