Attempt to work around iOS varying limit in GLPrograms test

Dump shaders when linking fails.

Bug: skia:6627
Change-Id: I7f1df4be039eb56d990aa64c58c8dd2a22d97dbe
Reviewed-on: https://skia-review.googlesource.com/16867
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp b/src/gpu/GrSKSLPrettyPrint.cpp
similarity index 92%
rename from src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
rename to src/gpu/GrSKSLPrettyPrint.cpp
index 0280298..65175c9 100644
--- a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
+++ b/src/gpu/GrSKSLPrettyPrint.cpp
@@ -4,18 +4,15 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "gl/GrGLSLPrettyPrint.h"
+#include "GrSKSLPrettyPrint.h"
 
-namespace GrGLSLPrettyPrint {
+namespace GrSKSLPrettyPrint {
 
 class GLSLPrettyPrint {
 public:
     GLSLPrettyPrint() {}
 
-    SkString prettify(const char** strings,
-                      int* lengths,
-                      int count,
-                      bool countlines) {
+    SkString prettify(const char** strings, int* lengths, int count, bool countlines) {
         fCountlines = countlines;
         fTabs = 0;
         fLinecount = 1;
@@ -60,7 +57,8 @@
                  * 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.
+                 * Otherwise just add the new character to the pretty string, indenting if
+                 * necessary.
                  */
                 if (fInParseUntilNewline) {
                     this->parseUntilNewline();
@@ -87,7 +85,7 @@
                 } else if (!parensDepth && this->hasToken(";")) {
                     this->newline();
                 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
-                        (fFreshline && ' ' == fInput[fIndex])) {
+                           (fFreshline && ' ' == fInput[fIndex])) {
                     fIndex++;
                 } else {
                     this->appendChar(fInput[fIndex]);
@@ -96,6 +94,7 @@
         }
         return fPretty;
     }
+
 private:
     void appendChar(char c) {
         this->tabString();
@@ -193,12 +192,9 @@
     const char* fInParseUntilToken;
 };
 
-SkString PrettyPrintGLSL(const char** strings,
-                         int* lengths,
-                         int count,
-                         bool countlines) {
+SkString PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
     GLSLPrettyPrint pp;
     return pp.prettify(strings, lengths, count, countlines);
 }
 
-} // end namespace
+}  // namespace GrSKSLPrettyPrint
diff --git a/src/gpu/GrSKSLPrettyPrint.h b/src/gpu/GrSKSLPrettyPrint.h
new file mode 100644
index 0000000..8fa4c1e
--- /dev/null
+++ b/src/gpu/GrSKSLPrettyPrint.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef GrSKSLPrettyPrint_DEFINED
+#define GrSKSLPrettyPrint_DEFINED
+
+#include "SkString.h"
+
+namespace GrSKSLPrettyPrint {
+SkString PrettyPrint(const char** strings, int* lengths, int count, bool countlines);
+};
+
+#endif
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 79cb2ca..f63e56b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -19,7 +19,6 @@
 #include "SkTraceEvent.h"
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLProgram.h"
-#include "gl/GrGLSLPrettyPrint.h"
 #include "gl/builders/GrGLShaderStringBuilder.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
@@ -158,7 +157,24 @@
     checkLinked = true;
 #endif
     if (checkLinked) {
-        checkLinkStatus(programID);
+        if (!this->checkLinkStatus(programID)) {
+            SkDebugf("VS:\n");
+            GrGLPrintShader(fGpu->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(),
+                            fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(),
+                            settings);
+            if (primProc.willUseGeoShader()) {
+                SkDebugf("\nGS:\n");
+                GrGLPrintShader(fGpu->glContext(), GR_GL_GEOMETRY_SHADER,
+                                fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(),
+                                fGS.fCompilerStrings.count(), settings);
+            }
+            SkDebugf("\nFS:\n");
+            GrGLPrintShader(fGpu->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(),
+                            fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(),
+                            settings);
+            SkDEBUGFAIL("");
+            return nullptr;
+        }
     }
     this->resolveProgramResourceLocations(programID);
 
@@ -197,6 +213,7 @@
     GrGLint linked = GR_GL_INIT_ZERO;
     GL_CALL(GetProgramiv(programID, GR_GL_LINK_STATUS, &linked));
     if (!linked) {
+        SkDebugf("Program linking failed.\n");
         GrGLint infoLen = GR_GL_INIT_ZERO;
         GL_CALL(GetProgramiv(programID, GR_GL_INFO_LOG_LENGTH, &infoLen));
         SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
@@ -210,7 +227,6 @@
                                       (char*)log.get()));
             SkDebugf("%s", (char*)log.get());
         }
-        SkDEBUGFAIL("Error linking program");
         GL_CALL(DeleteProgram(programID));
         programID = 0;
     }
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
index e3d2def..eb320d3 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
@@ -6,12 +6,12 @@
  */
 
 #include "GrGLShaderStringBuilder.h"
+#include "GrSKSLPrettyPrint.h"
 #include "SkAutoMalloc.h"
 #include "SkSLCompiler.h"
 #include "SkSLGLSLCodeGenerator.h"
 #include "SkTraceEvent.h"
 #include "gl/GrGLGpu.h"
-#include "gl/GrGLSLPrettyPrint.h"
 #include "ir/SkSLProgram.h"
 
 #define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
@@ -20,12 +20,67 @@
 // Print the source code for all shaders generated.
 static const bool c_PrintShaders{false};
 
-static void print_source_with_line_numbers(const SkString&);
+static SkString list_source_with_line_numbers(const char* source) {
+    SkTArray<SkString> lines;
+    SkStrSplit(source, "\n", kStrict_SkStrSplitMode, &lines);
+    SkString result;
+    for (int line = 0; line < lines.count(); ++line) {
+        // Print the shader one line at the time so it doesn't get truncated by the adb log.
+        result.appendf("%4i\t%s\n", line + 1, lines[line].c_str());
+    }
+    return result;
+}
+
+SkString list_shaders(const char** skslStrings, int* lengths, int count, const SkSL::String& glsl) {
+    SkString sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false);
+    SkString result("SKSL:\n");
+    result.append(list_source_with_line_numbers(sksl.c_str()));
+    if (!glsl.isEmpty()) {
+        result.append("GLSL:\n");
+        result.append(list_source_with_line_numbers(glsl.c_str()));
+    }
+    return result;
+}
+
+std::unique_ptr<SkSL::Program> translate_to_glsl(const GrGLContext& context, GrGLenum type,
+                                                 const char** skslStrings, int* lengths, int count,
+                                                 const SkSL::Program::Settings& settings,
+                                                 SkSL::String* glsl) {
+    SkString sksl;
+#ifdef SK_DEBUG
+    sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false);
+#else
+    for (int i = 0; i < count; i++) {
+        sksl.append(skslStrings[i], lengths[i]);
+    }
+#endif
+    if (type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER) {
+        SkSL::Compiler* compiler = context.compiler();
+        std::unique_ptr<SkSL::Program> program;
+        program = compiler->convertProgram(type == GR_GL_VERTEX_SHADER
+                                                   ? SkSL::Program::kVertex_Kind
+                                                   : SkSL::Program::kFragment_Kind,
+                                           sksl,
+                                           settings);
+        if (!program || !compiler->toGLSL(*program, glsl)) {
+            SkDebugf("SKSL compilation error\n----------------------\n");
+            SkDebugf(list_shaders(skslStrings, lengths, count, *glsl).c_str());
+            SkDebugf("\nErrors:\n%s\n", compiler->errorText().c_str());
+            SkDEBUGFAIL("SKSL compilation failed!\n");
+            return nullptr;
+        }
+        return program;
+    } else {
+        // TODO: geometry shader support in sksl.
+        SkASSERT(type == GR_GL_GEOMETRY_SHADER);
+        return nullptr;
+    }
+}
 
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const char** strings,
+                                    const char** skslStrings,
                                     int* lengths,
                                     int count,
                                     GrGpu::Stats* stats,
@@ -33,55 +88,35 @@
                                     SkSL::Program::Inputs* outInputs) {
     const GrGLInterface* gli = glCtx.interface();
 
+    SkSL::String glsl;
+    auto program = translate_to_glsl(glCtx, type, skslStrings, lengths, count, settings, &glsl);
+    if (!program) {
+        return 0;
+    }
+
+    // Specify GLSL source to the driver.
     GrGLuint shaderId;
     GR_GL_CALL_RET(gli, shaderId, CreateShader(type));
     if (0 == shaderId) {
         return 0;
     }
-
-    SkString sksl;
-#ifdef SK_DEBUG
-    sksl = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
-#else
-    for (int i = 0; i < count; i++) {
-        sksl.append(strings[i], lengths[i]);
-    }
-#endif
-
-    SkSL::String glsl;
-    if (type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER) {
-        SkSL::Compiler& compiler = *glCtx.compiler();
-        std::unique_ptr<SkSL::Program> program;
-        program = compiler.convertProgram(
-                                        type == GR_GL_VERTEX_SHADER ? SkSL::Program::kVertex_Kind
-                                                                    : SkSL::Program::kFragment_Kind,
-                                        sksl,
-                                        settings);
-        if (!program || !compiler.toGLSL(*program, &glsl)) {
-            SkDebugf("SKSL compilation error\n----------------------\n");
-            SkDebugf("SKSL:\n");
-            print_source_with_line_numbers(sksl);
-            SkDebugf("\nErrors:\n%s\n", compiler.errorText().c_str());
-            SkDEBUGFAIL("SKSL compilation failed!\n");
-        }
-        *outInputs = program->fInputs;
-    } else {
-        // TODO: geometry shader support in sksl.
-        SkASSERT(type == GR_GL_GEOMETRY_SHADER);
-        glsl = sksl;
-    }
-
     const char* glslChars = glsl.c_str();
     GrGLint glslLength = (GrGLint) glsl.size();
     GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength));
 
-    // If tracing is enabled in chrome then we pretty print
+    // Lazy initialized pretty-printed shaders for dumping.
+    SkString shaderDebugString;
+
+    // Trace event for shader preceding driver compilation
     bool traceShader;
     TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
     if (traceShader) {
-        SkString shader = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
+        if (shaderDebugString.isEmpty()) {
+            shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+        }
         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "skia_gpu::GLShader",
-                             TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shader.c_str()));
+                             TRACE_EVENT_SCOPE_THREAD, "shader",
+                             TRACE_STR_COPY(shaderDebugString.c_str()));
     }
 
     stats->incShaderCompilations();
@@ -97,6 +132,11 @@
         GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled));
 
         if (!compiled) {
+            if (shaderDebugString.isEmpty()) {
+                shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+            }
+            SkDebugf("GLSL compilation error\n----------------------\n");
+            SkDebugf(shaderDebugString.c_str());
             GrGLint infoLen = GR_GL_INIT_ZERO;
             GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_INFO_LOG_LENGTH, &infoLen));
             SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
@@ -105,11 +145,6 @@
                 // buffer param validation.
                 GrGLsizei length = GR_GL_INIT_ZERO;
                 GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
-                SkDebugf("GLSL compilation error\n----------------------\n");
-                SkDebugf("SKSL:\n");
-                print_source_with_line_numbers(sksl);
-                SkDebugf("GLSL:\n");
-                print_source_with_line_numbers(glsl);
                 SkDebugf("Errors:\n%s\n", (const char*) log.get());
             }
             SkDEBUGFAIL("GLSL compilation failed!");
@@ -126,7 +161,10 @@
             case GR_GL_FRAGMENT_SHADER: typeName = "Fragment"; break;
         }
         SkDebugf("---- %s shader ----------------------------------------------------\n", typeName);
-        print_source_with_line_numbers(sksl);
+        if (shaderDebugString.isEmpty()) {
+            shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+        }
+        SkDebugf(shaderDebugString.c_str());
     }
 
     // Attach the shader, but defer deletion until after we have linked the program.
@@ -134,15 +172,14 @@
     // will immediately delete the shader object and free its memory even though it's
     // attached to a program, which then causes glLinkProgram to fail.
     GR_GL_CALL(gli, AttachShader(programId, shaderId));
-
+    *outInputs = program->fInputs;
     return shaderId;
 }
 
-static void print_source_with_line_numbers(const SkString& source) {
-    SkTArray<SkString> lines;
-    SkStrSplit(source.c_str(), "\n", kStrict_SkStrSplitMode, &lines);
-    for (int line = 0; line < lines.count(); ++line) {
-        // Print the shader one line at the time so it doesn't get truncated by the adb log.
-        SkDebugf("%4i\t%s\n", line + 1, lines[line].c_str());
+void GrGLPrintShader(const GrGLContext& context, GrGLenum type, const char** skslStrings,
+                     int* lengths, int count, const SkSL::Program::Settings& settings) {
+    SkSL::String glsl;
+    if (translate_to_glsl(context, type, skslStrings, lengths, count, settings, &glsl)) {
+        SkDebugf(list_shaders(skslStrings, lengths, count, glsl).c_str());
     }
 }
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.h b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
index 242fe61..59dea35 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
@@ -17,11 +17,14 @@
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const char** strings,
+                                    const char** skslStrings,
                                     int* lengths,
                                     int count,
                                     GrGpu::Stats*,
                                     const SkSL::Program::Settings& settings,
                                     SkSL::Program::Inputs* inputs);
 
+void GrGLPrintShader(const GrGLContext&, GrGLenum type, const char** skslStrings, int* lengths,
+                     int count, const SkSL::Program::Settings&);
+
 #endif