Multi-string shaders

BUG=skia:

Review URL: https://codereview.chromium.org/929503002
diff --git a/include/gpu/gl/GrGLSLPrettyPrint.h b/include/gpu/gl/GrGLSLPrettyPrint.h
index 7273aaa..52fb745 100644
--- a/include/gpu/gl/GrGLSLPrettyPrint.h
+++ b/include/gpu/gl/GrGLSLPrettyPrint.h
@@ -10,7 +10,10 @@
 #include "SkString.h"
 
 namespace GrGLSLPrettyPrint {
-    SkString PrettyPrintGLSL(const SkString& input, bool countlines);
+    SkString PrettyPrintGLSL(const char** strings,
+                             int* lengths,
+                             int count,
+                             bool countlines);
 };
 
 #endif /* GRGLPRETTYPRINTSL_H_ */
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
index 86c622d..c739f11 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrGLFragmentShaderBuilder.h"
-#include "GrGLShaderStringBuilder.h"
 #include "GrGLProgramBuilder.h"
 #include "../GrGLGpu.h"
 
@@ -210,33 +209,19 @@
 }
 
 bool GrGLFragmentShaderBuilder::compileAndAttachShaders(GrGLuint programId,
-                                                        SkTDArray<GrGLuint>* shaderIds) const {
+                                                        SkTDArray<GrGLuint>* shaderIds) {
     GrGLGpu* gpu = fProgramBuilder->gpu();
-    SkString fragShaderSrc(GrGetGLSLVersionDecl(gpu->ctxInfo()));
-    fragShaderSrc.append(fExtensions);
+    this->versionDecl() = GrGetGLSLVersionDecl(gpu->ctxInfo());
     append_default_precision_qualifier(kDefault_GrSLPrecision,
                                        gpu->glStandard(),
-                                       &fragShaderSrc);
-    fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kFragment_Visibility, &fragShaderSrc);
-    this->appendDecls(fInputs, &fragShaderSrc);
+                                       &this->precisionQualifier());
+    fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kFragment_Visibility,
+                                        &this->uniforms());
+    this->appendDecls(fInputs, &this->inputs());
     // We shouldn't have declared outputs on 1.10
     SkASSERT(k110_GrGLSLGeneration != gpu->glslGeneration() || fOutputs.empty());
-    this->appendDecls(fOutputs, &fragShaderSrc);
-    fragShaderSrc.append(fFunctions);
-    fragShaderSrc.append("void main() {\n");
-    fragShaderSrc.append(fCode);
-    fragShaderSrc.append("}\n");
-
-    GrGLuint fragShaderId = GrGLCompileAndAttachShader(gpu->glContext(), programId,
-                                                       GR_GL_FRAGMENT_SHADER, fragShaderSrc,
-                                                       gpu->stats());
-    if (!fragShaderId) {
-        return false;
-    }
-
-    *shaderIds->append() = fragShaderId;
-
-    return true;
+    this->appendDecls(fOutputs, &this->outputs());
+    return this->finalize(programId, GR_GL_FRAGMENT_SHADER, shaderIds);
 }
 
 void GrGLFragmentShaderBuilder::bindFragmentShaderLocations(GrGLuint programID) {
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
index 903c5e1..f294257 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
@@ -101,7 +101,7 @@
     void enableSecondaryOutput();
     const char* getPrimaryColorOutputName() const;
     const char* getSecondaryColorOutputName() const;
-    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) const;
+    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds);
     void bindFragmentShaderLocations(GrGLuint programID);
 
     // As GLProcessors emit code, there are some conditions we need to verify.  We use the below
diff --git a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
index 8be2531..b205752 100644
--- a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrGLGeometryShaderBuilder.h"
-#include "GrGLShaderStringBuilder.h"
 #include "GrGLProgramBuilder.h"
 #include "../GrGLGpu.h"
 
@@ -37,35 +36,7 @@
 }
 
 bool GrGLGeometryBuilder::compileAndAttachShaders(GrGLuint programId,
-        SkTDArray<GrGLuint>* shaderIds) const {
-    const GrGLContext& glCtx = fProgramBuilder->gpu()->glContext();
-    SkASSERT(fProgramBuilder->ctxInfo().glslGeneration() >= k150_GrGLSLGeneration);
-    SkString geomShaderSrc(GrGetGLSLVersionDecl(fProgramBuilder->ctxInfo()));
-    geomShaderSrc.append("layout(triangles) in;\n"
-                         "layout(triangle_strip, max_vertices = 6) out;\n");
-    this->appendDecls(fInputs, &geomShaderSrc);
-    this->appendDecls(fOutputs, &geomShaderSrc);
-    geomShaderSrc.append("void main() {\n");
-    geomShaderSrc.append("\tfor (int i = 0; i < 3; ++i) {\n"
-                         "\t\tgl_Position = gl_in[i].gl_Position;\n");
-    geomShaderSrc.append("\t\tgl_PointSize = 1.0;\n");
-    SkASSERT(fInputs.count() == fOutputs.count());
-    for (int i = 0; i < fInputs.count(); ++i) {
-        geomShaderSrc.appendf("\t\t%s = %s[i];\n",
-                              fOutputs[i].getName().c_str(),
-                              fInputs[i].getName().c_str());
-    }
-    geomShaderSrc.append("\t\tEmitVertex();\n"
-                         "\t}\n"
-                         "\tEndPrimitive();\n");
-    geomShaderSrc.append("}\n");
-    GrGLuint geomShaderId =
-        GrGLCompileAndAttachShader(glCtx, programId,
-                                   GR_GL_GEOMETRY_SHADER, geomShaderSrc,
-                                   fProgramBuilder->gpu()->stats());
-    if (!geomShaderId) {
-        return false;
-    }
-    *shaderIds->append() = geomShaderId;
-    return true;
+        SkTDArray<GrGLuint>* shaderIds) {
+    SkFAIL("Geometry shaders are not currently supported");
+    return false;
 }
diff --git a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.h b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.h
index 88fa298..c4c019b 100644
--- a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.h
@@ -22,7 +22,7 @@
      */
     void addVarying(const char* name, GrGLVarying*);
 
-    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) const;
+    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds);
 
     friend class GrGLProgramBuilder;
 
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 37908f9..46d2816 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -356,7 +356,12 @@
     };
     class AutoStageAdvance {
     public:
-        AutoStageAdvance(GrGLProgramBuilder* pb) : fPB(pb) { fPB->reset(); }
+        AutoStageAdvance(GrGLProgramBuilder* pb)
+            : fPB(pb) {
+            fPB->reset();
+            // Each output to the fragment processor gets its own code section
+            fPB->fFS.nextStage();
+        }
         ~AutoStageAdvance() { fPB->exitStage(); }
     private:
         GrGLProgramBuilder* fPB;
diff --git a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp b/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
index 27f4b44..0280298 100644
--- a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
+++ b/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
@@ -12,69 +12,86 @@
 public:
     GLSLPrettyPrint() {}
 
-    SkString prettify(const SkString& input, bool countlines) {
-        // setup pretty state
-        fIndex = 0;
-        fLength = input.size();
-        fInput = input;
+    SkString prettify(const char** strings,
+                      int* lengths,
+                      int count,
+                      bool countlines) {
         fCountlines = countlines;
         fTabs = 0;
         fLinecount = 1;
         fFreshline = true;
 
+        // If a string breaks while in the middle 'parse until' we need to continue parsing on the
+        // next string
+        fInParseUntilNewline = false;
+        fInParseUntil = false;
+
         int parensDepth = 0;
+
         // number 1st line
         this->lineNumbering();
-        while (fLength > fIndex) {
-            /* the heart and soul of our prettification algorithm.  The rules should hopefully 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 (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(input[fIndex]);
+        for (int i = 0; i < count; i++) {
+            // setup pretty state
+            fIndex = 0;
+            fLength = lengths[i];
+            fInput = strings[i];
+
+            while (fLength > fIndex) {
+                /* the heart and soul of our prettification algorithm.  The rules should hopefully
+                 * 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;
@@ -107,9 +124,11 @@
             if ('\n' == fInput[fIndex]) {
                 fIndex++;
                 this->newline();
+                fInParseUntilNewline = false;
                 break;
             }
             fPretty.appendf("%c", fInput[fIndex++]);
+            fInParseUntilNewline = true;
         }
     }
 
@@ -127,10 +146,13 @@
                 fIndex++;
             }
             if (this->hasToken(token)) {
+                fInParseUntil = false;
                 break;
             }
             fFreshline = false;
             fPretty.appendf("%c", fInput[fIndex++]);
+            fInParseUntil = true;
+            fInParseUntilToken = token;
         }
     }
 
@@ -162,12 +184,21 @@
     bool fCountlines, fFreshline;
     int fTabs, fLinecount;
     size_t fIndex, fLength;
-    SkString fInput, fPretty;
+    const char* fInput;
+    SkString fPretty;
+
+    // Some helpers for parseUntil when we go over a string length
+    bool fInParseUntilNewline;
+    bool fInParseUntil;
+    const char* fInParseUntilToken;
 };
 
-SkString PrettyPrintGLSL(const SkString& input, bool countlines) {
+SkString PrettyPrintGLSL(const char** strings,
+                         int* lengths,
+                         int count,
+                         bool countlines) {
     GLSLPrettyPrint pp;
-    return pp.prettify(input, countlines);
+    return pp.prettify(strings, lengths, count, countlines);
 }
 
 } // end namespace
diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.cpp b/src/gpu/gl/builders/GrGLShaderBuilder.cpp
index 4887225..0711c91 100644
--- a/src/gpu/gl/builders/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderBuilder.cpp
@@ -7,7 +7,7 @@
 
 #include "GrGLShaderBuilder.h"
 #include "GrGLProgramBuilder.h"
-#include "GrGLProgramBuilder.h"
+#include "GrGLShaderStringBuilder.h"
 #include "../GrGLGpu.h"
 #include "../GrGLShaderVar.h"
 
@@ -59,7 +59,17 @@
     : fProgramBuilder(program)
     , fInputs(GrGLProgramBuilder::kVarsPerBlock)
     , fOutputs(GrGLProgramBuilder::kVarsPerBlock)
-    , fFeaturesAddedMask(0) {
+    , fFeaturesAddedMask(0)
+    , fCodeIndex(kCode)
+    , fFinalized(false) {
+    // We push back some dummy pointers which will later become our header
+    for (int i = 0; i <= kCode; i++) {
+        fShaderStrings.push_back();
+        fCompilerStrings.push_back(NULL);
+        fCompilerStringLengths.push_back(0);
+    }
+
+    this->main() = "void main() {";
 }
 
 void GrGLShaderBuilder::declAppend(const GrGLShaderVar& var) {
@@ -74,20 +84,20 @@
                                      const GrGLShaderVar* args,
                                      const char* body,
                                      SkString* outName) {
-    fFunctions.append(GrGLSLTypeString(returnType));
+    this->functions().append(GrGLSLTypeString(returnType));
     fProgramBuilder->nameVariable(outName, '\0', name);
-    fFunctions.appendf(" %s", outName->c_str());
-    fFunctions.append("(");
+    this->functions().appendf(" %s", outName->c_str());
+    this->functions().append("(");
     const GrGLContextInfo& ctxInfo = fProgramBuilder->gpu()->ctxInfo();
     for (int i = 0; i < argCnt; ++i) {
-        args[i].appendDecl(ctxInfo, &fFunctions);
+        args[i].appendDecl(ctxInfo, &this->functions());
         if (i < argCnt - 1) {
-            fFunctions.append(", ");
+            this->functions().append(", ");
         }
     }
-    fFunctions.append(") {\n");
-    fFunctions.append(body);
-    fFunctions.append("}\n\n");
+    this->functions().append(") {\n");
+    this->functions().append(body);
+    this->functions().append("}\n\n");
 }
 
 void GrGLShaderBuilder::appendTextureLookup(SkString* out,
@@ -106,7 +116,7 @@
 void GrGLShaderBuilder::appendTextureLookup(const TextureSampler& sampler,
                                             const char* coordName,
                                             GrSLType varyingType) {
-    this->appendTextureLookup(&fCode, sampler, coordName, varyingType);
+    this->appendTextureLookup(&this->code(), sampler, coordName, varyingType);
 }
 
 void GrGLShaderBuilder::appendTextureLookupAndModulate(const char* modulation,
@@ -137,8 +147,8 @@
 
 void GrGLShaderBuilder::addFeature(uint32_t featureBit, const char* extensionName) {
     if (!(featureBit & fFeaturesAddedMask)) {
-        fExtensions.appendf("#extension %s: require\n", extensionName);
-            fFeaturesAddedMask |= featureBit;
+        this->extensions().appendf("#extension %s: require\n", extensionName);
+        fFeaturesAddedMask |= featureBit;
     }
 }
 
@@ -153,7 +163,7 @@
                                             const char* coordName,
                                             uint32_t configComponentMask,
                                             const char* swizzle) {
-    append_texture_lookup(&fCode,
+    append_texture_lookup(&this->code(),
                           fProgramBuilder->gpu(),
                           samplerName,
                           coordName,
@@ -161,3 +171,34 @@
                           swizzle,
                           kVec2f_GrSLType);
 }
+
+bool
+GrGLShaderBuilder::finalize(GrGLuint programId, GrGLenum type, SkTDArray<GrGLuint>* shaderIds) {
+    SkASSERT(!fFinalized);
+    // append the 'footer' to code
+    this->code().append("}");
+
+    for (int i = 0; i <= fCodeIndex; i++) {
+        fCompilerStrings[i] = fShaderStrings[i].c_str();
+        fCompilerStringLengths[i] = fShaderStrings[i].size();
+    }
+
+    GrGLGpu* gpu = fProgramBuilder->gpu();
+    GrGLuint shaderId = GrGLCompileAndAttachShader(gpu->glContext(),
+                                                   programId,
+                                                   type,
+                                                   fCompilerStrings.begin(),
+                                                   fCompilerStringLengths.begin(),
+                                                   fCompilerStrings.count(),
+                                                   gpu->stats());
+
+    fFinalized = true;
+
+    if (!shaderId) {
+        return false;
+    }
+
+    *shaderIds->append() = shaderId;
+
+    return true;
+}
diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.h b/src/gpu/gl/builders/GrGLShaderBuilder.h
index 8b7b9f9..86db597 100644
--- a/src/gpu/gl/builders/GrGLShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderBuilder.h
@@ -8,6 +8,7 @@
 #ifndef GrGLShaderBuilder_DEFINED
 #define GrGLShaderBuilder_DEFINED
 
+#include "SkTArray.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/GrGLProgramDesc.h"
 #include "gl/GrGLProgramDataManager.h"
@@ -73,16 +74,16 @@
     void codeAppendf(const char format[], ...) SK_PRINTF_LIKE(2, 3) {
        va_list args;
        va_start(args, format);
-       fCode.appendVAList(format, args);
+       this->code().appendVAList(format, args);
        va_end(args);
     }
 
-    void codeAppend(const char* str) { fCode.append(str); }
+    void codeAppend(const char* str) { this->code().append(str); }
 
     void codePrependf(const char format[], ...) SK_PRINTF_LIKE(2, 3) {
        va_list args;
        va_start(args, format);
-       fCode.prependVAList(format, args);
+       this->code().prependVAList(format, args);
        va_end(args);
     }
 
@@ -138,8 +139,40 @@
      */
     void addFeature(uint32_t featureBit, const char* extensionName);
 
-    GrGLProgramBuilder* fProgramBuilder;
+    void nextStage() {
+        fShaderStrings.push_back();
+        fCompilerStrings.push_back(this->code().c_str());
+        fCompilerStringLengths.push_back(this->code().size());
+        fCodeIndex++;
+    }
 
+    SkString& versionDecl() { return fShaderStrings[kVersionDecl]; }
+    SkString& extensions() { return fShaderStrings[kExtensions]; }
+    SkString& precisionQualifier() { return fShaderStrings[kPrecisionQualifier]; }
+    SkString& uniforms() { return fShaderStrings[kUniforms]; }
+    SkString& inputs() { return fShaderStrings[kInputs]; }
+    SkString& outputs() { return fShaderStrings[kOutputs]; }
+    SkString& functions() { return fShaderStrings[kFunctions]; }
+    SkString& main() { return fShaderStrings[kMain]; }
+    SkString& code() { return fShaderStrings[fCodeIndex]; }
+    bool finalize(GrGLuint programId, GrGLenum type, SkTDArray<GrGLuint>* shaderIds);
+
+    enum {
+        kVersionDecl,
+        kExtensions,
+        kPrecisionQualifier,
+        kUniforms,
+        kInputs,
+        kOutputs,
+        kFunctions,
+        kMain,
+        kCode,
+    };
+
+    GrGLProgramBuilder* fProgramBuilder;
+    SkSTArray<kCode, const char*, true> fCompilerStrings;
+    SkSTArray<kCode, int, true> fCompilerStringLengths;
+    SkSTArray<kCode, SkString> fShaderStrings;
     SkString fCode;
     SkString fFunctions;
     SkString fExtensions;
@@ -147,5 +180,9 @@
     VarArray fInputs;
     VarArray fOutputs;
     uint32_t fFeaturesAddedMask;
+    int fCodeIndex;
+    bool fFinalized;
+
+    friend class GrGLProgramBuilder;
 };
 #endif
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
index 1e75048..59e0cd8 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
@@ -20,7 +20,9 @@
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const SkString& shaderSrc,
+                                    const char** strings,
+                                    int* lengths,
+                                    int count,
                                     GrGpu::Stats* stats) {
     const GrGLInterface* gli = glCtx.interface();
 
@@ -31,14 +33,23 @@
     }
 
 #ifdef SK_DEBUG
-    SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(shaderSrc, false);
+    SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
     const GrGLchar* sourceStr = prettySource.c_str();
     GrGLint sourceLength = static_cast<GrGLint>(prettySource.size());
-#else
-    GrGLint sourceLength = static_cast<GrGLint>(shaderSrc.size());
-    const GrGLchar* sourceStr = shaderSrc.c_str();
-#endif
     GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength));
+#else
+    GR_GL_CALL(gli, ShaderSource(shaderId, count, strings, lengths));
+#endif
+
+    // If tracing is enabled in chrome then we pretty print
+    bool traceShader;
+    TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
+    if (traceShader) {
+        SkString shader = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
+        TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "skia_gpu::GLShader",
+                             TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shader.c_str()));
+    }
+
     stats->incShaderCompilations();
     GR_GL_CALL(gli, CompileShader(shaderId));
 
@@ -59,9 +70,8 @@
                 // retrieve length even though we don't need it to workaround bug in Chromium cmd
                 // buffer param validation.
                 GrGLsizei length = GR_GL_INIT_ZERO;
-                GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1,
-                                                 &length, (char*)log.get()));
-                SkDebugf(GrGLSLPrettyPrint::PrettyPrintGLSL(shaderSrc, true).c_str());
+                GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
+                SkDebugf(GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, true).c_str());
                 SkDebugf("\n%s", log.get());
             }
             SkDEBUGFAIL("Shader compilation failed!");
@@ -70,10 +80,8 @@
         }
     }
 
-    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "skia_gpu::GLShader",
-                         TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shaderSrc.c_str()));
     if (c_PrintShaders) {
-        SkDebugf(GrGLSLPrettyPrint::PrettyPrintGLSL(shaderSrc, true).c_str());
+        SkDebugf(GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, true).c_str());
         SkDebugf("\n");
     }
 
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.h b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
index cf54253..062e229 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
@@ -16,7 +16,9 @@
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const SkString& shaderSrc,
+                                    const char** strings,
+                                    int* lengths,
+                                    int count,
                                     GrGpu::Stats*);
 
 #endif
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
index f1671af..00e96a5 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
@@ -7,7 +7,6 @@
 
 #include "GrGLVertexShaderBuilder.h"
 #include "GrGLProgramBuilder.h"
-#include "GrGLShaderStringBuilder.h"
 #include "../GrGLGpu.h"
 
 #define GL_CALL(X) GR_GL_CALL(fProgramBuilder->gpu()->glInterface(), X)
@@ -73,25 +72,13 @@
     return;
 }
 
-bool GrGLVertexBuilder::compileAndAttachShaders(GrGLuint programId,
-        SkTDArray<GrGLuint>* shaderIds) const {
-    GrGLGpu* gpu = fProgramBuilder->gpu();
-    const GrGLContext& glCtx = gpu->glContext();
-    const GrGLContextInfo& ctxInfo = gpu->ctxInfo();
-    SkString vertShaderSrc(GrGetGLSLVersionDecl(ctxInfo));
-    fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kVertex_Visibility, &vertShaderSrc);
-    this->appendDecls(fInputs, &vertShaderSrc);
-    this->appendDecls(fOutputs, &vertShaderSrc);
-    vertShaderSrc.append("void main() {");
-    vertShaderSrc.append(fCode);
-    vertShaderSrc.append("}\n");
-    GrGLuint vertShaderId = GrGLCompileAndAttachShader(glCtx, programId, GR_GL_VERTEX_SHADER,
-                                                       vertShaderSrc, gpu->stats());
-    if (!vertShaderId) {
-        return false;
-    }
-    *shaderIds->append() = vertShaderId;
-    return true;
+bool
+GrGLVertexBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) {
+    this->versionDecl() = GrGetGLSLVersionDecl(fProgramBuilder->ctxInfo());
+    fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kVertex_Visibility, &this->uniforms());
+    this->appendDecls(fInputs, &this->inputs());
+    this->appendDecls(fOutputs, &this->outputs());
+    return this->finalize(programId, GR_GL_VERTEX_SHADER, shaderIds);
 }
 
 bool GrGLVertexBuilder::addAttribute(const GrShaderVar& var) {
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
index 4c99a7b..71a60a0 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
@@ -35,7 +35,7 @@
      * private helpers for compilation by GrGLProgramBuilder
      */
     void bindVertexAttributes(GrGLuint programID);
-    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) const;
+    bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds);
 
     // an internal call which checks for uniquness of a var before adding it to the list of inputs
     bool addAttribute(const GrShaderVar& var);
diff --git a/tests/GrGLSLPrettyPrintTest.cpp b/tests/GrGLSLPrettyPrintTest.cpp
index 9977488..37aa5a8 100644
--- a/tests/GrGLSLPrettyPrintTest.cpp
+++ b/tests/GrGLSLPrettyPrintTest.cpp
@@ -13,12 +13,16 @@
 
 const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;"
                      "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}"
-                     "void main()"
-                     "{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){doingstuff"
+                     "void main()");
+const SkString input2("{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){"
+                     "doingstuff"
                      ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this"
-                     " comment\n is"
-                     " dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
-                     "//a comment\n}}a; little ;  love; for   ; leading;  spaces;} "
+                     " comment\n is");
+const SkString input3(" dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
+                     "//a comment");
+const SkString input4("breaking in comment");
+const SkString input5("continuing the comment");
+const SkString input6("\n}}a; little ;  love; for   ; leading;  spaces;} "
                      "an struct = { int a; int b; };"
                      "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }");
 
@@ -52,7 +56,7 @@
         "  27\t\t\t\t\t\t is dangerous\n"
         "  28\t\t\t\t\t\tdo so at your own\n"
         "  29\t\t\t\t\t\t risk*/;\n"
-        "  30\t\t\t\t\t\t//a comment\n"
+        "  30\t\t\t\t\t\t//a commentbreaking in commentcontinuing the comment\n"
         "  31\t\t\t\t\t}\n"
         "  32\t\t\t\t}\n"
         "  33\t\t\t\ta;\n"
@@ -77,16 +81,43 @@
         "  52\t}\n"
         "  53\t");
 
-const SkString input2("{;;{{{{;;;{{{{{{{{{{{###\n##\n#####(((((((((((((unbalanced verything;;;"
-        "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
+const SkString neg1("{;;{{{{;;;{{{{{{{{{{{");
+const SkString neg2("###\n##\n#####(((((((((((((unbalanced verything;;;");
+const SkString neg3("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
         ";;;;;;/////");
 
 DEF_TEST(GrGLSLPrettyPrint, r) {
-    SkString test = GrGLSLPrettyPrint::PrettyPrintGLSL(input1, true);
+    SkTArray<const char*> testStr;
+    SkTArray<int> lengths;
+    testStr.push_back(input1.c_str());
+    lengths.push_back(input1.size());
+    testStr.push_back(input2.c_str());
+    lengths.push_back(input2.size());
+    testStr.push_back(input3.c_str());
+    lengths.push_back(input3.size());
+    testStr.push_back(input4.c_str());
+    lengths.push_back(input4.size());
+    testStr.push_back(input5.c_str());
+    lengths.push_back(input5.size());
+    testStr.push_back(input6.c_str());
+    lengths.push_back(input6.size());
+
+    SkString test = GrGLSLPrettyPrint::PrettyPrintGLSL(testStr.begin(), lengths.begin(),
+                                                       testStr.count(), true);
     ASSERT(output1 == test);
 
+    testStr.reset();
+    lengths.reset();
+    testStr.push_back(neg1.c_str());
+    lengths.push_back(neg1.size());
+    testStr.push_back(neg2.c_str());
+    lengths.push_back(neg2.size());
+    testStr.push_back(neg3.c_str());
+    lengths.push_back(neg3.size());
+
     // Just test we don't crash with garbage input
-    ASSERT(GrGLSLPrettyPrint::PrettyPrintGLSL(input2, true).c_str() != NULL);
+    ASSERT(GrGLSLPrettyPrint::PrettyPrintGLSL(testStr.begin(), lengths.begin(), 1,
+                                              true).c_str() != NULL);
 }
 
 #endif