Add a workaround for platforms with broken geo shader invocations

BUG=skia:

Change-Id: I9105b65b522d9ffac5a90ca7126bfd4ae88f8069
Reviewed-on: https://skia-review.googlesource.com/8422
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/include/gpu/GrShaderCaps.h b/include/gpu/GrShaderCaps.h
index 918138c..36ab3c3 100644
--- a/include/gpu/GrShaderCaps.h
+++ b/include/gpu/GrShaderCaps.h
@@ -176,6 +176,9 @@
 
     bool requiresLocalOutputColorForFBFetch() const { return fRequiresLocalOutputColorForFBFetch; }
 
+    // On MacBook, geometry shaders break if they have more than one invocation.
+    bool mustImplementGSInvocationsWithLoop() const { return fMustImplementGSInvocationsWithLoop; }
+
     // Returns the string of an extension that must be enabled in the shader to support
     // derivatives. If nullptr is returned then no extension needs to be enabled. Before calling
     // this function, the caller should check that shaderDerivativeSupport exists.
@@ -304,6 +307,7 @@
     bool fMustForceNegatedAtanParamToFloat : 1;
     bool fAtan2ImplementedAsAtanYOverX : 1;
     bool fRequiresLocalOutputColorForFBFetch : 1;
+    bool fMustImplementGSInvocationsWithLoop : 1;
 
     PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount];
     int fPixelLocalStorageSize;
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index 621ac3d..69af070 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -58,6 +58,7 @@
     fMustForceNegatedAtanParamToFloat = false;
     fAtan2ImplementedAsAtanYOverX = false;
     fRequiresLocalOutputColorForFBFetch = false;
+    fMustImplementGSInvocationsWithLoop = false;
     fFlatInterpolationSupport = false;
     fNoPerspectiveInterpolationSupport = false;
     fMultisampleInterpolationSupport = false;
@@ -144,6 +145,8 @@
                                                                "YES" : "NO"));
     r.appendf("Must use local out color for FBFetch: %s\n", (fRequiresLocalOutputColorForFBFetch ?
                                                              "YES" : "NO"));
+    r.appendf("Must implement geo shader invocations with loop : %s\n",
+              (fMustImplementGSInvocationsWithLoop ? "YES" : "NO"));
     r.appendf("Flat interpolation support: %s\n", (fFlatInterpolationSupport ?  "YES" : "NO"));
     r.appendf("No perspective interpolation support: %s\n", (fNoPerspectiveInterpolationSupport ?
                                                              "YES" : "NO"));
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index aea875f..4c25221 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -870,6 +870,13 @@
     if (shaderCaps->fFBFetchSupport && kQualcomm_GrGLVendor == ctxInfo.vendor()) {
         shaderCaps->fRequiresLocalOutputColorForFBFetch = true;
     }
+
+#ifdef SK_BUILD_FOR_MAC
+    // On at least some MacBooks, geometry shaders fall apart if we use more than one invocation. To
+    // work around this, we always use a single invocation and wrap the shader in a loop. The long-
+    // term plan for this WAR is for it to eventually be baked into SkSL.
+    shaderCaps->fMustImplementGSInvocationsWithLoop = true;
+#endif
 }
 
 bool GrGLCaps::hasPathRenderingSupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
diff --git a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp
index a8e746c..01f223f 100644
--- a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp
@@ -35,22 +35,39 @@
 
 GrGLSLGeometryBuilder::GrGLSLGeometryBuilder(GrGLSLProgramBuilder* program)
     : INHERITED(program)
-    , fIsConfigured(false) {
+    , fNumInvocations(0) {
 }
 
 void GrGLSLGeometryBuilder::configure(InputType inputType, OutputType outputType, int maxVertices,
                                       int numInvocations) {
-    SkASSERT(!fIsConfigured);
+    SkASSERT(!this->isConfigured());
+    fNumInvocations = numInvocations;
+    if (this->getProgramBuilder()->shaderCaps()->mustImplementGSInvocationsWithLoop()) {
+        maxVertices *= numInvocations;
+        numInvocations = 1;
+    }
     this->addLayoutQualifier(input_type_name(inputType), kIn_InterfaceQualifier);
     this->addLayoutQualifier(SkStringPrintf("invocations = %i", numInvocations).c_str(),
                              kIn_InterfaceQualifier);
     this->addLayoutQualifier(output_type_name(outputType), kOut_InterfaceQualifier);
     this->addLayoutQualifier(SkStringPrintf("max_vertices = %i", maxVertices).c_str(),
                              kOut_InterfaceQualifier);
-    fIsConfigured = true;
 }
 
 void GrGLSLGeometryBuilder::onFinalize() {
-    SkASSERT(fIsConfigured);
+    SkASSERT(this->isConfigured());
     fProgramBuilder->varyingHandler()->getGeomDecls(&this->inputs(), &this->outputs());
+    GrShaderVar sk_InvocationID("sk_InvocationID", kInt_GrSLType);
+    this->declareGlobal(sk_InvocationID);
+    SkASSERT(sk_InvocationID.getName() == SkString("sk_InvocationID"));
+    if (this->getProgramBuilder()->shaderCaps()->mustImplementGSInvocationsWithLoop()) {
+        SkString invokeFn;
+        this->emitFunction(kVoid_GrSLType, "invoke", 0, nullptr, this->code().c_str(), &invokeFn);
+        this->code().printf("for (sk_InvocationID = 0; sk_InvocationID < %i; ++sk_InvocationID) {"
+                                "%s();"
+                                "EndPrimitive();"
+                            "}", fNumInvocations, invokeFn.c_str());
+    } else {
+        this->codePrependf("sk_InvocationID = gl_InvocationID;");
+    }
 }
diff --git a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h
index feedcf2..04be530 100644
--- a/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLGeometryShaderBuilder.h
@@ -31,11 +31,12 @@
     };
 
     void configure(InputType, OutputType, int maxVertices, int numInvocations = 1);
+    bool isConfigured() const { return fNumInvocations; }
 
 private:
     void onFinalize() override;
 
-    bool fIsConfigured;
+    int fNumInvocations;
 
     friend class GrGLProgramBuilder;