Add ShaderErrorHandler to GrContextOptions

Allows clients to customize behavior when shaders fail to compile.
Added nicer shader error handling to viewer.

Change-Id: If82b48e40d64fd786f37e88c564fd623b53c7f9d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/211361
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index ca9e679..98d6f21 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -21,6 +21,7 @@
 #include "src/gpu/GrResourceCache.h"
 #include "src/gpu/GrResourceProvider.h"
 #include "src/gpu/GrSemaphore.h"
+#include "src/gpu/GrShaderUtils.h"
 #include "src/gpu/GrSoftwarePathRenderer.h"
 #include "src/gpu/GrTracing.h"
 #include "src/gpu/SkGr.h"
@@ -92,6 +93,10 @@
     }
 
     fPersistentCache = this->options().fPersistentCache;
+    fShaderErrorHandler = this->options().fShaderErrorHandler;
+    if (!fShaderErrorHandler) {
+        fShaderErrorHandler = GrShaderUtils::DefaultShaderErrorHandler();
+    }
 
     return true;
 }
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 97c76e4..ebf3f6a 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -253,6 +253,9 @@
     void copyOpListsFromDDL(const SkDeferredDisplayList*, GrRenderTargetProxy* newDest);
 
     GrContextOptions::PersistentCache* getPersistentCache() { return fContext->fPersistentCache; }
+    GrContextOptions::ShaderErrorHandler* getShaderErrorHandler() const {
+        return fContext->fShaderErrorHandler;
+    }
 
 #ifdef SK_ENABLE_DUMP_GPU
     /** Returns a string with detailed information about the context & GPU, in JSON format. */
diff --git a/src/gpu/GrShaderUtils.cpp b/src/gpu/GrShaderUtils.cpp
index 5367202..d0fe2d7 100644
--- a/src/gpu/GrShaderUtils.cpp
+++ b/src/gpu/GrShaderUtils.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkString.h"
+#include "include/gpu/GrContextOptions.h"
 #include "src/gpu/GrShaderUtils.h"
 #include "src/sksl/SkSLString.h"
 
@@ -190,8 +191,10 @@
 
 // Prints shaders one line at the time. This ensures they don't get truncated by the adb log.
 void PrintLineByLine(const char* header, const SkSL::String& text) {
+    if (header) {
+        SkDebugf("%s\n", header);
+    }
     SkSL::String pretty = PrettyPrint(text);
-    SkDebugf("%s\n", header);
     SkTArray<SkString> lines;
     SkStrSplit(pretty.c_str(), "\n", kStrict_SkStrSplitMode, &lines);
     for (int i = 0; i < lines.count(); ++i) {
@@ -199,4 +202,20 @@
     }
 }
 
+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;
+}
+
 }  // namespace
diff --git a/src/gpu/GrShaderUtils.h b/src/gpu/GrShaderUtils.h
index 731d546..c30120d 100644
--- a/src/gpu/GrShaderUtils.h
+++ b/src/gpu/GrShaderUtils.h
@@ -9,12 +9,14 @@
 #define GrShaderUtils_DEFINED
 
 #include "include/core/SkTypes.h"
+#include "include/gpu/GrContextOptions.h"
 #include "src/sksl/SkSLString.h"
 
 namespace GrShaderUtils {
 
 SkSL::String PrettyPrint(const SkSL::String& string);
 void PrintLineByLine(const char* header, const SkSL::String& text);
+GrContextOptions::ShaderErrorHandler* DefaultShaderErrorHandler();
 
 }
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 5efd09e..0a044b6 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -3440,24 +3440,23 @@
         "}"
     );
 
+    auto errorHandler = this->getContext()->priv().getShaderErrorHandler();
     SkSL::String sksl(vshaderTxt.c_str(), vshaderTxt.size());
     SkSL::Program::Settings settings;
     settings.fCaps = shaderCaps;
     SkSL::String glsl;
-    bool assertOnCompileFailure =
-            this->getContext()->priv().options().fAssertOnShaderCompileFailure;
     std::unique_ptr<SkSL::Program> program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kVertex_Kind,
-                                                          sksl, settings, &glsl);
+                                                          sksl, settings, &glsl, errorHandler);
     GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[progIdx].fProgram,
-                                                  GR_GL_VERTEX_SHADER, glsl, &fStats,
-                                                  assertOnCompileFailure);
+                                                  GR_GL_VERTEX_SHADER, glsl, &fStats, errorHandler);
     SkASSERT(program->fInputs.isEmpty());
 
     sksl.assign(fshaderTxt.c_str(), fshaderTxt.size());
-    program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kFragment_Kind, sksl, settings, &glsl);
+    program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kFragment_Kind, sksl, settings, &glsl,
+                           errorHandler);
     GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[progIdx].fProgram,
                                                   GR_GL_FRAGMENT_SHADER, glsl, &fStats,
-                                                  assertOnCompileFailure);
+                                                  errorHandler);
     SkASSERT(program->fInputs.isEmpty());
 
     GL_CALL(LinkProgram(fCopyPrograms[progIdx].fProgram));
@@ -3596,24 +3595,23 @@
 
     fshaderTxt.append("}");
 
+    auto errorHandler = this->getContext()->priv().getShaderErrorHandler();
     SkSL::String sksl(vshaderTxt.c_str(), vshaderTxt.size());
     SkSL::Program::Settings settings;
     settings.fCaps = shaderCaps;
     SkSL::String glsl;
-    bool assertOnCompileFailure =
-            this->getContext()->priv().options().fAssertOnShaderCompileFailure;
     std::unique_ptr<SkSL::Program> program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kVertex_Kind,
-                                                          sksl, settings, &glsl);
+                                                          sksl, settings, &glsl, errorHandler);
     GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
-                                                  GR_GL_VERTEX_SHADER, glsl, &fStats,
-                                                  assertOnCompileFailure);
+                                                  GR_GL_VERTEX_SHADER, glsl, &fStats, errorHandler);
     SkASSERT(program->fInputs.isEmpty());
 
     sksl.assign(fshaderTxt.c_str(), fshaderTxt.size());
-    program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kFragment_Kind, sksl, settings, &glsl);
+    program = GrSkSLtoGLSL(*fGLContext, SkSL::Program::kFragment_Kind, sksl, settings, &glsl,
+                           errorHandler);
     GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
                                                   GR_GL_FRAGMENT_SHADER, glsl, &fStats,
-                                                  assertOnCompileFailure);
+                                                  errorHandler);
     SkASSERT(program->fInputs.isEmpty());
 
     GL_CALL(LinkProgram(fMipmapPrograms[progIdx].fProgram));
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index d7ac7e4..bed69a3 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -89,15 +89,15 @@
                                                  GrGLuint programId,
                                                  GrGLenum type,
                                                  SkTDArray<GrGLuint>* shaderIds,
-                                                 const SkSL::Program::Inputs& inputs) {
+                                                 const SkSL::Program::Inputs& inputs,
+                                                 GrContextOptions::ShaderErrorHandler* errHandler) {
     GrGLGpu* gpu = this->gpu();
-    bool assertOnCompileFailure = gpu->getContext()->priv().options().fAssertOnShaderCompileFailure;
     GrGLuint shaderId = GrGLCompileAndAttachShader(gpu->glContext(),
                                                    programId,
                                                    type,
                                                    glsl,
                                                    gpu->stats(),
-                                                   assertOnCompileFailure);
+                                                   errHandler);
     if (!shaderId) {
         return false;
     }
@@ -202,6 +202,7 @@
     this->finalizeShaders();
 
     // compile shaders and bind attributes / uniforms
+    auto errorHandler = this->gpu()->getContext()->priv().getShaderErrorHandler();
     const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
     SkSL::Program::Settings settings;
     settings.fCaps = this->gpu()->glCaps().shaderCaps();
@@ -241,7 +242,7 @@
                                                 length));
             if (GR_GL_GET_ERROR(this->gpu()->glInterface()) == GR_GL_NO_ERROR) {
                 if (checkLinked) {
-                    cached = this->checkLinkStatus(programID);
+                    cached = this->checkLinkStatus(programID, errorHandler, nullptr, nullptr);
                 }
                 if (cached) {
                     this->addInputVars(inputs);
@@ -284,7 +285,8 @@
                                                              SkSL::Program::kFragment_Kind,
                                                              *sksl[kFragment_GrShaderType],
                                                              settings,
-                                                             &glsl[kFragment_GrShaderType]);
+                                                             &glsl[kFragment_GrShaderType],
+                                                             errorHandler);
             if (!fs) {
                 this->cleanupProgram(programID, shadersToDelete);
                 return nullptr;
@@ -297,7 +299,8 @@
             this->computeCountsAndStrides(programID, primProc, false);
         }
         if (!this->compileAndAttachShaders(glsl[kFragment_GrShaderType], programID,
-                                           GR_GL_FRAGMENT_SHADER, &shadersToDelete, inputs)) {
+                                           GR_GL_FRAGMENT_SHADER, &shadersToDelete, inputs,
+                                           errorHandler)) {
             this->cleanupProgram(programID, shadersToDelete);
             return nullptr;
         }
@@ -308,14 +311,16 @@
                                                              SkSL::Program::kVertex_Kind,
                                                              *sksl[kVertex_GrShaderType],
                                                              settings,
-                                                             &glsl[kVertex_GrShaderType]);
+                                                             &glsl[kVertex_GrShaderType],
+                                                             errorHandler);
             if (!vs) {
                 this->cleanupProgram(programID, shadersToDelete);
                 return nullptr;
             }
         }
         if (!this->compileAndAttachShaders(glsl[kVertex_GrShaderType], programID,
-                                           GR_GL_VERTEX_SHADER, &shadersToDelete, inputs)) {
+                                           GR_GL_VERTEX_SHADER, &shadersToDelete, inputs,
+                                           errorHandler)) {
             this->cleanupProgram(programID, shadersToDelete);
             return nullptr;
         }
@@ -334,14 +339,16 @@
                                   SkSL::Program::kGeometry_Kind,
                                   *sksl[kGeometry_GrShaderType],
                                   settings,
-                                  &glsl[kGeometry_GrShaderType]);
+                                  &glsl[kGeometry_GrShaderType],
+                                  errorHandler);
                 if (!gs) {
                     this->cleanupProgram(programID, shadersToDelete);
                     return nullptr;
                 }
             }
             if (!this->compileAndAttachShaders(glsl[kGeometry_GrShaderType], programID,
-                                               GR_GL_GEOMETRY_SHADER, &shadersToDelete, inputs)) {
+                                               GR_GL_GEOMETRY_SHADER, &shadersToDelete, inputs,
+                                               errorHandler)) {
                 this->cleanupProgram(programID, shadersToDelete);
                 return nullptr;
             }
@@ -350,22 +357,8 @@
 
         GL_CALL(LinkProgram(programID));
         if (checkLinked) {
-            if (!this->checkLinkStatus(programID)) {
+            if (!this->checkLinkStatus(programID, errorHandler, sksl, glsl)) {
                 GL_CALL(DeleteProgram(programID));
-                GrGLPrintShader(fGpu->glContext(),
-                                SkSL::Program::kVertex_Kind,
-                                fVS.fCompilerString,
-                                settings);
-                if (primProc.willUseGeoShader()) {
-                    GrGLPrintShader(fGpu->glContext(),
-                                    SkSL::Program::kGeometry_Kind,
-                                    fGS.fCompilerString,
-                                    settings);
-                }
-                GrGLPrintShader(fGpu->glContext(),
-                                SkSL::Program::kFragment_Kind,
-                                fFS.fCompilerString,
-                                settings);
                 return nullptr;
             }
         }
@@ -414,11 +407,27 @@
     }
 }
 
-bool GrGLProgramBuilder::checkLinkStatus(GrGLuint programID) {
+bool GrGLProgramBuilder::checkLinkStatus(GrGLuint programID,
+                                         GrContextOptions::ShaderErrorHandler* errorHandler,
+                                         SkSL::String* sksl[], const SkSL::String glsl[]) {
     GrGLint linked = GR_GL_INIT_ZERO;
     GL_CALL(GetProgramiv(programID, GR_GL_LINK_STATUS, &linked));
     if (!linked) {
-        SkDebugf("Program linking failed.\n");
+        SkSL::String allShaders;
+        if (sksl) {
+            allShaders.appendf("// Vertex SKSL\n%s\n", sksl[kVertex_GrShaderType]->c_str());
+            if (!sksl[kGeometry_GrShaderType]->empty()) {
+                allShaders.appendf("// Geometry SKSL\n%s\n", sksl[kGeometry_GrShaderType]->c_str());
+            }
+            allShaders.appendf("// Fragment SKSL\n%s\n", sksl[kFragment_GrShaderType]->c_str());
+        }
+        if (glsl) {
+            allShaders.appendf("// Vertex GLSL\n%s\n", glsl[kVertex_GrShaderType].c_str());
+            if (!glsl[kGeometry_GrShaderType].empty()) {
+                allShaders.appendf("// Geometry GLSL\n%s\n", glsl[kGeometry_GrShaderType].c_str());
+            }
+            allShaders.appendf("// Fragment GLSL\n%s\n", glsl[kFragment_GrShaderType].c_str());
+        }
         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
@@ -426,12 +435,9 @@
             // retrieve length even though we don't need it to workaround
             // bug in chrome cmd buffer param validation.
             GrGLsizei length = GR_GL_INIT_ZERO;
-            GL_CALL(GetProgramInfoLog(programID,
-                                      infoLen+1,
-                                      &length,
-                                      (char*)log.get()));
-            SkDebugf("%s", (char*)log.get());
+            GL_CALL(GetProgramInfoLog(programID, infoLen+1, &length, (char*)log.get()));
         }
+        errorHandler->compileError(allShaders.c_str(), infoLen > 0 ? (const char*)log.get() : "");
     }
     return SkToBool(linked);
 }
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index cde80ae..1d216b5 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -56,7 +56,8 @@
                                  GrGLuint programId,
                                  GrGLenum type,
                                  SkTDArray<GrGLuint>* shaderIds,
-                                 const SkSL::Program::Inputs& inputs);
+                                 const SkSL::Program::Inputs& inputs,
+                                 GrContextOptions::ShaderErrorHandler* errorHandler);
 
     void computeCountsAndStrides(GrGLuint programID, const GrPrimitiveProcessor& primProc,
                                  bool bindAttribLocations);
@@ -64,7 +65,8 @@
                             const SkSL::String shaders[], bool isSkSL);
     GrGLProgram* finalize();
     void bindProgramResourceLocations(GrGLuint programID);
-    bool checkLinkStatus(GrGLuint programID);
+    bool checkLinkStatus(GrGLuint programID, GrContextOptions::ShaderErrorHandler* errorHandler,
+                         SkSL::String* sksl[], const SkSL::String glsl[]);
     void resolveProgramResourceLocations(GrGLuint programID);
     void cleanupProgram(GrGLuint programID, const SkTDArray<GrGLuint>& shaderIDs);
     void cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs);
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
index 473414e..1bd0396 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
@@ -32,15 +32,14 @@
                                             SkSL::Program::Kind programKind,
                                             const SkSL::String& sksl,
                                             const SkSL::Program::Settings& settings,
-                                            SkSL::String* glsl) {
+                                            SkSL::String* glsl,
+                                            GrContextOptions::ShaderErrorHandler* errorHandler) {
     SkSL::Compiler* compiler = context.compiler();
     std::unique_ptr<SkSL::Program> program;
     program = compiler->convertProgram(programKind, sksl, settings);
     if (!program || !compiler->toGLSL(*program, glsl)) {
-        SkDebugf("SKSL compilation error\n----------------------\n");
-        GrShaderUtils::PrintLineByLine("SKSL:", sksl);
-        SkDebugf("\nErrors:\n%s\n", compiler->errorText().c_str());
-        SkDEBUGFAIL("SKSL compilation failed!\n");
+        errorHandler->compileError(GrShaderUtils::PrettyPrint(sksl).c_str(),
+                                   compiler->errorText().c_str());
         return nullptr;
     }
 
@@ -62,7 +61,7 @@
                                     GrGLenum type,
                                     const SkSL::String& glsl,
                                     GrGpu::Stats* stats,
-                                    bool assertOnFailure) {
+                                    GrContextOptions::ShaderErrorHandler* errorHandler) {
     const GrGLInterface* gli = glCtx.interface();
 
     // Specify GLSL source to the driver.
@@ -88,8 +87,6 @@
         GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled));
 
         if (!compiled) {
-            SkDebugf("GLSL compilation error\n----------------------\n");
-            GrShaderUtils::PrintLineByLine("GLSL:", glsl);
             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
@@ -98,13 +95,8 @@
                 // buffer param validation.
                 GrGLsizei length = GR_GL_INIT_ZERO;
                 GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
-                SkDebugf("Errors:\n%s\n", (const char*) log.get());
             }
-            // In Chrome we may have failed due to context-loss. So we should just continue along
-            // wihthout asserting until the GrContext gets abandoned.
-            if (assertOnFailure && kChromium_GrGLDriver != glCtx.driver()) {
-                SkDEBUGFAIL("GLSL compilation failed!");
-            }
+            errorHandler->compileError(glsl.c_str(), infoLen > 0 ? (const char*)log.get() : "");
             GR_GL_CALL(gli, DeleteShader(shaderId));
             return 0;
         }
@@ -117,13 +109,3 @@
     GR_GL_CALL(gli, AttachShader(programId, shaderId));
     return shaderId;
 }
-
-void GrGLPrintShader(const GrGLContext& context, SkSL::Program::Kind programKind,
-                     const SkSL::String& sksl, const SkSL::Program::Settings& settings) {
-    print_shader_banner(programKind);
-    GrShaderUtils::PrintLineByLine("SKSL:", sksl);
-    SkSL::String glsl;
-    if (GrSkSLtoGLSL(context, programKind, sksl, settings, &glsl)) {
-        GrShaderUtils::PrintLineByLine("GLSL:", glsl);
-    }
-}
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.h b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
index e65d995..12bebc3 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
@@ -9,6 +9,7 @@
 #define GrGLShaderStringBuilder_DEFINED
 
 #include "include/core/SkTypes.h"
+#include "include/gpu/GrContextOptions.h"
 #include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/gl/GrGLContext.h"
@@ -18,16 +19,14 @@
                                             SkSL::Program::Kind programKind,
                                             const SkSL::String& sksl,
                                             const SkSL::Program::Settings& settings,
-                                            SkSL::String* glsl);
+                                            SkSL::String* glsl,
+                                            GrContextOptions::ShaderErrorHandler* errorHandler);
 
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
                                     const SkSL::String& glsl,
                                     GrGpu::Stats*,
-                                    bool assertOnFailure);
-
-void GrGLPrintShader(const GrGLContext&, SkSL::Program::Kind programKind, const SkSL::String& sksl,
-                     const SkSL::Program::Settings&);
+                                    GrContextOptions::ShaderErrorHandler* errorHandler);
 
 #endif
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index fcd885e..58516e8 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -106,9 +106,7 @@
 static constexpr SkFourByteTag kSKSL_Tag = SkSetFourByteTag('S', 'K', 'S', 'L');
 
 int GrVkPipelineStateBuilder::loadShadersFromCache(const SkData& cached,
-                                                   VkShaderModule* outVertShaderModule,
-                                                   VkShaderModule* outFragShaderModule,
-                                                   VkShaderModule* outGeomShaderModule,
+                                                   VkShaderModule outShaderModules[],
                                                    VkPipelineShaderStageCreateInfo* outStageInfo) {
     SkSL::String shaders[kGrShaderTypeCount];
     SkSL::Program::Inputs inputs[kGrShaderTypeCount];
@@ -120,14 +118,14 @@
 
     SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT,
                                                fVS,
-                                               outVertShaderModule,
+                                               &outShaderModules[kVertex_GrShaderType],
                                                &outStageInfo[0],
                                                shaders[kVertex_GrShaderType],
                                                inputs[kVertex_GrShaderType]));
 
     SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT,
                                                fFS,
-                                               outFragShaderModule,
+                                               &outShaderModules[kFragment_GrShaderType],
                                                &outStageInfo[1],
                                                shaders[kFragment_GrShaderType],
                                                inputs[kFragment_GrShaderType]));
@@ -135,7 +133,7 @@
     if (!shaders[kGeometry_GrShaderType].empty()) {
         SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT,
                                                    fGS,
-                                                   outGeomShaderModule,
+                                                   &outShaderModules[kGeometry_GrShaderType],
                                                    &outStageInfo[2],
                                                    shaders[kGeometry_GrShaderType],
                                                    inputs[kGeometry_GrShaderType]));
@@ -162,9 +160,9 @@
                                                       Desc* desc) {
     VkDescriptorSetLayout dsLayout[2];
     VkPipelineLayout pipelineLayout;
-    VkShaderModule vertShaderModule = VK_NULL_HANDLE;
-    VkShaderModule geomShaderModule = VK_NULL_HANDLE;
-    VkShaderModule fragShaderModule = VK_NULL_HANDLE;
+    VkShaderModule shaderModules[kGrShaderTypeCount] = { VK_NULL_HANDLE,
+                                                         VK_NULL_HANDLE,
+                                                         VK_NULL_HANDLE };
 
     GrVkResourceProvider& resourceProvider = fGpu->resourceProvider();
     // These layouts are not owned by the PipelineStateBuilder and thus should not be destroyed
@@ -221,8 +219,7 @@
 #endif
     int numShaderStages = 0;
     if (cached && binaryCache) {
-        numShaderStages = this->loadShadersFromCache(*cached, &vertShaderModule, &fragShaderModule,
-                                                     &geomShaderModule, shaderStageInfo);
+        numShaderStages = this->loadShadersFromCache(*cached, shaderModules, shaderStageInfo);
     }
 
     if (!numShaderStages) {
@@ -247,35 +244,48 @@
         }
 #endif
 
-        SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT,
+        bool success = this->createVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT,
                                                   *sksl[kVertex_GrShaderType],
-                                                  &vertShaderModule,
+                                                  &shaderModules[kVertex_GrShaderType],
                                                   &shaderStageInfo[0],
                                                   settings,
                                                   desc,
                                                   &shaders[kVertex_GrShaderType],
-                                                  &inputs[kVertex_GrShaderType]));
+                                                  &inputs[kVertex_GrShaderType]);
 
-        SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT,
-                                                  *sksl[kFragment_GrShaderType],
-                                                  &fragShaderModule,
-                                                  &shaderStageInfo[1],
-                                                  settings,
-                                                  desc,
-                                                  &shaders[kFragment_GrShaderType],
-                                                  &inputs[kFragment_GrShaderType]));
+        success = success && this->createVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT,
+                                                        *sksl[kFragment_GrShaderType],
+                                                        &shaderModules[kFragment_GrShaderType],
+                                                        &shaderStageInfo[1],
+                                                        settings,
+                                                        desc,
+                                                        &shaders[kFragment_GrShaderType],
+                                                        &inputs[kFragment_GrShaderType]);
 
         if (this->primitiveProcessor().willUseGeoShader()) {
-            SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT,
-                                                      *sksl[kGeometry_GrShaderType],
-                                                      &geomShaderModule,
-                                                      &shaderStageInfo[2],
-                                                      settings,
-                                                      desc,
-                                                      &shaders[kGeometry_GrShaderType],
-                                                      &inputs[kGeometry_GrShaderType]));
+            success = success && this->createVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT,
+                                                            *sksl[kGeometry_GrShaderType],
+                                                            &shaderModules[kGeometry_GrShaderType],
+                                                            &shaderStageInfo[2],
+                                                            settings,
+                                                            desc,
+                                                            &shaders[kGeometry_GrShaderType],
+                                                            &inputs[kGeometry_GrShaderType]);
             ++numShaderStages;
         }
+
+        if (!success) {
+            for (int i = 0; i < kGrShaderTypeCount; ++i) {
+                if (shaderModules[i]) {
+                    GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(),
+                                                                        shaderModules[i], nullptr));
+                }
+            }
+            GR_VK_CALL(fGpu->vkInterface(), DestroyPipelineLayout(fGpu->device(), pipelineLayout,
+                                                                  nullptr));
+            return nullptr;
+        }
+
         if (persistentCache && !cached) {
             bool isSkSL = false;
 #if GR_TEST_UTILS
@@ -292,15 +302,13 @@
     GrVkPipeline* pipeline = resourceProvider.createPipeline(
             this->renderTarget()->numColorSamples(), fPrimProc, fPipeline, stencil, this->origin(),
             shaderStageInfo, numShaderStages, primitiveType, compatibleRenderPass, pipelineLayout);
-    GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), vertShaderModule,
-                                                        nullptr));
-    GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), fragShaderModule,
-                                                        nullptr));
-    // This if check should not be needed since calling destroy on a VK_NULL_HANDLE is allowed.
-    // However this is causing a crash in certain drivers (e.g. NVidia).
-    if (this->primitiveProcessor().willUseGeoShader()) {
-        GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), geomShaderModule,
-                                                            nullptr));
+    for (int i = 0; i < kGrShaderTypeCount; ++i) {
+        // This if check should not be needed since calling destroy on a VK_NULL_HANDLE is allowed.
+        // However this is causing a crash in certain drivers (e.g. NVidia).
+        if (shaderModules[i]) {
+            GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), shaderModules[i],
+                                                                nullptr));
+        }
     }
 
     if (!pipeline) {
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.h b/src/gpu/vk/GrVkPipelineStateBuilder.h
index 5a68801..7b79f73 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.h
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.h
@@ -91,10 +91,7 @@
                                 Desc*);
 
     // returns number of shader stages
-    int loadShadersFromCache(const SkData& cached,
-                             VkShaderModule* outVertShaderModule,
-                             VkShaderModule* outFragShaderModule,
-                             VkShaderModule* outGeomShaderModule,
+    int loadShadersFromCache(const SkData& cached, VkShaderModule outShaderModules[],
                              VkPipelineShaderStageCreateInfo* outStageInfo);
 
     void storeShadersInCache(const SkSL::String shaders[], const SkSL::Program::Inputs inputs[],
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index d1de474..6738bc9 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/vk/GrVkUtil.h"
 
+#include "src/gpu/GrContextPriv.h"
 #include "src/gpu/vk/GrVkGpu.h"
 #include "src/sksl/SkSLCompiler.h"
 
@@ -207,16 +208,18 @@
                              const SkSL::Program::Settings& settings,
                              SkSL::String* outSPIRV,
                              SkSL::Program::Inputs* outInputs) {
+    auto errorHandler = gpu->getContext()->priv().getShaderErrorHandler();
     std::unique_ptr<SkSL::Program> program = gpu->shaderCompiler()->convertProgram(
             vk_shader_stage_to_skiasl_kind(stage), shaderString, settings);
     if (!program) {
-        printf("%s\n", shaderString.c_str());
-        SkDebugf("SkSL error:\n%s\n", gpu->shaderCompiler()->errorText().c_str());
-        SkASSERT(false);
+        errorHandler->compileError(shaderString.c_str(),
+                                   gpu->shaderCompiler()->errorText().c_str());
+        return false;
     }
     *outInputs = program->fInputs;
     if (!gpu->shaderCompiler()->toSPIRV(*program, outSPIRV)) {
-        SkDebugf("%s\n", gpu->shaderCompiler()->errorText().c_str());
+        errorHandler->compileError(shaderString.c_str(),
+                                   gpu->shaderCompiler()->errorText().c_str());
         return false;
     }