1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2019 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkString.h" 9cb93a386Sopenharmony_ci#include "include/gpu/GrContextOptions.h" 10cb93a386Sopenharmony_ci#include "include/private/SkSLString.h" 11cb93a386Sopenharmony_ci#include "src/gpu/GrShaderUtils.h" 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_cinamespace GrShaderUtils { 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciclass GLSLPrettyPrint { 16cb93a386Sopenharmony_cipublic: 17cb93a386Sopenharmony_ci GLSLPrettyPrint() {} 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci SkSL::String prettify(const SkSL::String& string) { 20cb93a386Sopenharmony_ci fTabs = 0; 21cb93a386Sopenharmony_ci fFreshline = true; 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci // If a string breaks while in the middle 'parse until' we need to continue parsing on the 24cb93a386Sopenharmony_ci // next string 25cb93a386Sopenharmony_ci fInParseUntilNewline = false; 26cb93a386Sopenharmony_ci fInParseUntil = false; 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ci int parensDepth = 0; 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci // setup pretty state 31cb93a386Sopenharmony_ci fIndex = 0; 32cb93a386Sopenharmony_ci fLength = string.length(); 33cb93a386Sopenharmony_ci fInput = string.c_str(); 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci while (fLength > fIndex) { 36cb93a386Sopenharmony_ci /* the heart and soul of our prettification algorithm. The rules should hopefully 37cb93a386Sopenharmony_ci * be self explanatory. For '#' and '//' tokens we parse until we reach a newline. 38cb93a386Sopenharmony_ci * 39cb93a386Sopenharmony_ci * For long style comments like this one, we search for the ending token. We also 40cb93a386Sopenharmony_ci * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines 41cb93a386Sopenharmony_ci * ourselves. This allows us to remain in control of line numbers, and matching 42cb93a386Sopenharmony_ci * tabs Existing tabs in the input string are copied over too, but this will look 43cb93a386Sopenharmony_ci * funny 44cb93a386Sopenharmony_ci * 45cb93a386Sopenharmony_ci * '{' and '}' are handled in basically the same way. We add a newline if we aren't 46cb93a386Sopenharmony_ci * on a fresh line, dirty the line, then add a second newline, ie braces are always 47cb93a386Sopenharmony_ci * on their own lines indented properly. The one funkiness here is structs print 48cb93a386Sopenharmony_ci * with the semicolon on its own line. Its not a problem for a glsl compiler though 49cb93a386Sopenharmony_ci * 50cb93a386Sopenharmony_ci * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala 51cb93a386Sopenharmony_ci * in for loops. 52cb93a386Sopenharmony_ci * 53cb93a386Sopenharmony_ci * ';' means add a new line 54cb93a386Sopenharmony_ci * 55cb93a386Sopenharmony_ci * '\t' and '\n' are ignored in general parsing for backwards compatability with 56cb93a386Sopenharmony_ci * existing shader code and we also have a special case for handling whitespace 57cb93a386Sopenharmony_ci * at the beginning of fresh lines. 58cb93a386Sopenharmony_ci * 59cb93a386Sopenharmony_ci * Otherwise just add the new character to the pretty string, indenting if 60cb93a386Sopenharmony_ci * necessary. 61cb93a386Sopenharmony_ci */ 62cb93a386Sopenharmony_ci if (fInParseUntilNewline) { 63cb93a386Sopenharmony_ci this->parseUntilNewline(); 64cb93a386Sopenharmony_ci } else if (fInParseUntil) { 65cb93a386Sopenharmony_ci this->parseUntil(fInParseUntilToken); 66cb93a386Sopenharmony_ci } else if (this->hasToken("#") || this->hasToken("//")) { 67cb93a386Sopenharmony_ci this->parseUntilNewline(); 68cb93a386Sopenharmony_ci } else if (this->hasToken("/*")) { 69cb93a386Sopenharmony_ci this->parseUntil("*/"); 70cb93a386Sopenharmony_ci } else if ('{' == fInput[fIndex]) { 71cb93a386Sopenharmony_ci this->newline(); 72cb93a386Sopenharmony_ci this->appendChar('{'); 73cb93a386Sopenharmony_ci fTabs++; 74cb93a386Sopenharmony_ci this->newline(); 75cb93a386Sopenharmony_ci } else if ('}' == fInput[fIndex]) { 76cb93a386Sopenharmony_ci fTabs--; 77cb93a386Sopenharmony_ci this->newline(); 78cb93a386Sopenharmony_ci this->appendChar('}'); 79cb93a386Sopenharmony_ci this->newline(); 80cb93a386Sopenharmony_ci } else if (this->hasToken(")")) { 81cb93a386Sopenharmony_ci parensDepth--; 82cb93a386Sopenharmony_ci } else if (this->hasToken("(")) { 83cb93a386Sopenharmony_ci parensDepth++; 84cb93a386Sopenharmony_ci } else if (!parensDepth && this->hasToken(";")) { 85cb93a386Sopenharmony_ci this->newline(); 86cb93a386Sopenharmony_ci } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || 87cb93a386Sopenharmony_ci (fFreshline && ' ' == fInput[fIndex])) { 88cb93a386Sopenharmony_ci fIndex++; 89cb93a386Sopenharmony_ci } else { 90cb93a386Sopenharmony_ci this->appendChar(fInput[fIndex]); 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci } 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci return fPretty; 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_ciprivate: 98cb93a386Sopenharmony_ci void appendChar(char c) { 99cb93a386Sopenharmony_ci this->tabString(); 100cb93a386Sopenharmony_ci fPretty.appendf("%c", fInput[fIndex++]); 101cb93a386Sopenharmony_ci fFreshline = false; 102cb93a386Sopenharmony_ci } 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci // hasToken automatically consumes the next token, if it is a match, and then tabs 105cb93a386Sopenharmony_ci // if necessary, before inserting the token into the pretty string 106cb93a386Sopenharmony_ci bool hasToken(const char* token) { 107cb93a386Sopenharmony_ci size_t i = fIndex; 108cb93a386Sopenharmony_ci for (size_t j = 0; token[j] && fLength > i; i++, j++) { 109cb93a386Sopenharmony_ci if (token[j] != fInput[i]) { 110cb93a386Sopenharmony_ci return false; 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci } 113cb93a386Sopenharmony_ci this->tabString(); 114cb93a386Sopenharmony_ci fIndex = i; 115cb93a386Sopenharmony_ci fPretty.append(token); 116cb93a386Sopenharmony_ci fFreshline = false; 117cb93a386Sopenharmony_ci return true; 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci void parseUntilNewline() { 121cb93a386Sopenharmony_ci while (fLength > fIndex) { 122cb93a386Sopenharmony_ci if ('\n' == fInput[fIndex]) { 123cb93a386Sopenharmony_ci fIndex++; 124cb93a386Sopenharmony_ci this->newline(); 125cb93a386Sopenharmony_ci fInParseUntilNewline = false; 126cb93a386Sopenharmony_ci break; 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci fPretty.appendf("%c", fInput[fIndex++]); 129cb93a386Sopenharmony_ci fInParseUntilNewline = true; 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci // this code assumes it is not actually searching for a newline. If you need to search for a 134cb93a386Sopenharmony_ci // newline, then use the function above. If you do search for a newline with this function 135cb93a386Sopenharmony_ci // it will consume the entire string and the output will certainly not be prettified 136cb93a386Sopenharmony_ci void parseUntil(const char* token) { 137cb93a386Sopenharmony_ci while (fLength > fIndex) { 138cb93a386Sopenharmony_ci // For embedded newlines, this code will make sure to embed the newline in the 139cb93a386Sopenharmony_ci // pretty string, increase the linecount, and tab out the next line to the appropriate 140cb93a386Sopenharmony_ci // place 141cb93a386Sopenharmony_ci if ('\n' == fInput[fIndex]) { 142cb93a386Sopenharmony_ci this->newline(); 143cb93a386Sopenharmony_ci this->tabString(); 144cb93a386Sopenharmony_ci fIndex++; 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci if (this->hasToken(token)) { 147cb93a386Sopenharmony_ci fInParseUntil = false; 148cb93a386Sopenharmony_ci break; 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci fFreshline = false; 151cb93a386Sopenharmony_ci fPretty.appendf("%c", fInput[fIndex++]); 152cb93a386Sopenharmony_ci fInParseUntil = true; 153cb93a386Sopenharmony_ci fInParseUntilToken = token; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci // We only tab if on a newline, otherwise consider the line tabbed 158cb93a386Sopenharmony_ci void tabString() { 159cb93a386Sopenharmony_ci if (fFreshline) { 160cb93a386Sopenharmony_ci for (int t = 0; t < fTabs; t++) { 161cb93a386Sopenharmony_ci fPretty.append("\t"); 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci // newline is really a request to add a newline, if we are on a fresh line there is no reason 167cb93a386Sopenharmony_ci // to add another newline 168cb93a386Sopenharmony_ci void newline() { 169cb93a386Sopenharmony_ci if (!fFreshline) { 170cb93a386Sopenharmony_ci fFreshline = true; 171cb93a386Sopenharmony_ci fPretty.append("\n"); 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci } 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci bool fFreshline; 176cb93a386Sopenharmony_ci int fTabs; 177cb93a386Sopenharmony_ci size_t fIndex, fLength; 178cb93a386Sopenharmony_ci const char* fInput; 179cb93a386Sopenharmony_ci SkSL::String fPretty; 180cb93a386Sopenharmony_ci 181cb93a386Sopenharmony_ci // Some helpers for parseUntil when we go over a string length 182cb93a386Sopenharmony_ci bool fInParseUntilNewline; 183cb93a386Sopenharmony_ci bool fInParseUntil; 184cb93a386Sopenharmony_ci const char* fInParseUntilToken; 185cb93a386Sopenharmony_ci}; 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ciSkSL::String PrettyPrint(const SkSL::String& string) { 188cb93a386Sopenharmony_ci GLSLPrettyPrint pp; 189cb93a386Sopenharmony_ci return pp.prettify(string); 190cb93a386Sopenharmony_ci} 191cb93a386Sopenharmony_ci 192cb93a386Sopenharmony_civoid VisitLineByLine(const SkSL::String& text, 193cb93a386Sopenharmony_ci const std::function<void(int lineNumber, const char* lineText)>& visitFn) { 194cb93a386Sopenharmony_ci SkTArray<SkString> lines; 195cb93a386Sopenharmony_ci SkStrSplit(text.c_str(), "\n", kStrict_SkStrSplitMode, &lines); 196cb93a386Sopenharmony_ci for (int i = 0; i < lines.count(); ++i) { 197cb93a386Sopenharmony_ci visitFn(i + 1, lines[i].c_str()); 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci} 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ciSkSL::String BuildShaderErrorMessage(const char* shader, const char* errors) { 202cb93a386Sopenharmony_ci SkSL::String abortText{"Shader compilation error\n" 203cb93a386Sopenharmony_ci "------------------------\n"}; 204cb93a386Sopenharmony_ci VisitLineByLine(shader, [&](int lineNumber, const char* lineText) { 205cb93a386Sopenharmony_ci abortText.appendf("%4i\t%s\n", lineNumber, lineText); 206cb93a386Sopenharmony_ci }); 207cb93a386Sopenharmony_ci abortText.appendf("Errors:\n%s", errors); 208cb93a386Sopenharmony_ci return abortText; 209cb93a386Sopenharmony_ci} 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ciGrContextOptions::ShaderErrorHandler* DefaultShaderErrorHandler() { 212cb93a386Sopenharmony_ci class GrDefaultShaderErrorHandler : public GrContextOptions::ShaderErrorHandler { 213cb93a386Sopenharmony_ci public: 214cb93a386Sopenharmony_ci void compileError(const char* shader, const char* errors) override { 215cb93a386Sopenharmony_ci SkSL::String message = BuildShaderErrorMessage(shader, errors); 216cb93a386Sopenharmony_ci VisitLineByLine(message, [](int, const char* lineText) { 217cb93a386Sopenharmony_ci SkDebugf("%s\n", lineText); 218cb93a386Sopenharmony_ci }); 219cb93a386Sopenharmony_ci SkDEBUGFAIL("Shader compilation failed!"); 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci }; 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci static GrDefaultShaderErrorHandler gHandler; 224cb93a386Sopenharmony_ci return &gHandler; 225cb93a386Sopenharmony_ci} 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_civoid PrintShaderBanner(SkSL::ProgramKind programKind) { 228cb93a386Sopenharmony_ci const char* typeName = "Unknown"; 229cb93a386Sopenharmony_ci switch (programKind) { 230cb93a386Sopenharmony_ci case SkSL::ProgramKind::kVertex: typeName = "Vertex"; break; 231cb93a386Sopenharmony_ci case SkSL::ProgramKind::kFragment: typeName = "Fragment"; break; 232cb93a386Sopenharmony_ci default: break; 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci SkDebugf("---- %s shader ----------------------------------------------------\n", typeName); 235cb93a386Sopenharmony_ci} 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci} // namespace GrShaderUtils 238