GrShaderUtils.cpp 7.5 KB

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