Hooks up the GrCustomStage/GrGLProgramStageFactory/GrGLProgramStage
classes from r3726 so they can be used. Does not implement any actual
effect stages.

Has one large known bug: if custom stages are provided, GrSamplerState
comparisons will break; this should preserve correct drawing, but decrease
performance - among other things, we'll break draw batching. To fix this
we'll need a RTTI system for GrCustomState objects, and we'll need to change
the GrSamplerState comparison from a memcmp to something that also does a
deep type-sensitive compare of any GrCustomState objects present.

http://codereview.appspot.com/6074043/



git-svn-id: http://skia.googlecode.com/svn/trunk@3742 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 16a60ce..fb98b4d 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -10,6 +10,8 @@
 #include "GrGLProgram.h"
 
 #include "../GrAllocator.h"
+#include "GrCustomStage.h"
+#include "GrGLProgramStage.h"
 #include "GrGLShaderVar.h"
 #include "SkTrace.h"
 #include "SkXfermode.h"
@@ -620,8 +622,17 @@
     }
 }
 
+// If this destructor is in the header file, we must include GrGLProgramStage
+// instead of just forward-declaring it.
+GrGLProgram::CachedData::~CachedData() {
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        delete fCustomStage[i];
+    }
+}
+
 
 bool GrGLProgram::genProgram(const GrGLContextInfo& gl,
+                             GrCustomStage** customStages,
                              GrGLProgram::CachedData* programData) const {
 
     ShaderCodeSegments segments;
@@ -733,6 +744,21 @@
     }
 
     ///////////////////////////////////////////////////////////////////////////
+    // Convert generic effect representation to GL-specific backend so they
+    // can be accesseed in genStageCode() and in subsequent uses of
+    // programData.
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrCustomStage* customStage = customStages[s];
+        if (NULL != customStage) {
+            GrGLProgramStageFactory* factory = customStage->getGLFactory();
+            programData->fCustomStage[s] =
+                factory->createGLInstance(customStage);
+        } else {
+            programData->fCustomStage[s] = NULL;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
     // compute the final color
 
     // if we have color stages string them together, feeding the output color
@@ -766,7 +792,8 @@
                                    outColor.c_str(),
                                    inCoords,
                                    &segments,
-                                   &programData->fUniLocations.fStages[s]);
+                                   &programData->fUniLocations.fStages[s],
+                                   programData->fCustomStage[s]);
                 inColor = outColor;
             }
         }
@@ -878,13 +905,14 @@
                         inCoords = texCoordAttrs[tcIdx].c_str();
                     }
 
-                    genStageCode(gl, s,
-                                 fProgramDesc.fStages[s],
-                                 inCoverage.size() ? inCoverage.c_str() : NULL,
-                                 outCoverage.c_str(),
-                                 inCoords,
-                                 &segments,
-                                 &programData->fUniLocations.fStages[s]);
+                    this->genStageCode(gl, s,
+                        fProgramDesc.fStages[s],
+                        inCoverage.size() ? inCoverage.c_str() : NULL,
+                        outCoverage.c_str(),
+                        inCoords,
+                        &segments,
+                        &programData->fUniLocations.fStages[s],
+                        programData->fCustomStage[s]);
                     inCoverage = outCoverage;
                 }
             }
@@ -1348,6 +1376,11 @@
                                                imageIncrementName.c_str()));
                 GrAssert(kUnusedUniform != locations.fImageIncrementUni);
             }
+
+            if (NULL != programData->fCustomStage[s]) {
+                programData->fCustomStage[s]->
+                    initUniforms(gl.interface(), progID);
+            }
         }
     }
     GL_CALL(UseProgram(progID));
@@ -1363,6 +1396,7 @@
         programData->fTextureWidth[s] = -1;
         programData->fTextureHeight[s] = -1;
         programData->fTextureDomain[s].setEmpty();
+        // Must not reset fStageOverride[] here.
     }
     programData->fViewMatrix = GrMatrix::InvalidMatrix();
     programData->fColor = GrColor_ILLEGAL;
@@ -1712,7 +1746,8 @@
                                const char* fsOutColor,
                                const char* vsInCoord,
                                ShaderCodeSegments* segments,
-                               StageUniLocations* locations) const {
+                               StageUniLocations* locations,
+                               GrGLProgramStage* customStage) const {
 
     GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
     GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
@@ -1723,8 +1758,13 @@
     // gradients.
     static const int coordDims = 2;
     int varyingDims;
+
     /// Vertex Shader Stuff
 
+    if (NULL != customStage) {
+        customStage->setupVSUnis(segments->fVSUnis, stageNum);
+    }
+
     // decide whether we need a matrix to transform texture coords
     // and whether the varying needs a perspective coord.
     const char* matName = NULL;
@@ -1808,7 +1848,20 @@
                         &imageIncrementName, varyingVSName);
     }
 
+    if (NULL != customStage) {
+        GrStringBuilder vertexShader;
+        customStage->emitVS(&vertexShader, varyingVSName);
+        segments->fVSCode.appendf("{\n");
+        segments->fVSCode.append(vertexShader);
+        segments->fVSCode.appendf("}\n");
+    }
+
     /// Fragment Shader Stuff
+
+    if (NULL != customStage) {
+        customStage->setupFSUnis(segments->fFSUnis, stageNum);
+    }
+
     GrStringBuilder fsCoordName;
     // function used to access the shader, may be made projective
     GrStringBuilder texFunc("texture2D");
@@ -1959,6 +2012,31 @@
                                       swizzle, modulate.c_str());
         }
     }
+
+    if (NULL != customStage) {
+        if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
+                              StageDesc::kNoPerspective_OptFlagBit)) {
+            customStage->setSamplerMode(GrGLProgramStage::kDefault_SamplerMode);
+        } else if (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
+                   StageDesc::kSingle_FetchMode == desc.fFetchMode) {
+            customStage->setSamplerMode(GrGLProgramStage::kProj_SamplerMode);
+        } else {
+            customStage->setSamplerMode(
+                GrGLProgramStage::kExplicitDivide_SamplerMode);
+        }
+
+        GrStringBuilder fragmentShader;
+        fsCoordName = customStage->emitTextureSetup(
+                          &fragmentShader, varyingFSName,
+                          stageNum, coordDims, varyingDims);
+        customStage->emitFS(&fragmentShader, fsOutColor, fsInColor,
+                            samplerName, fsCoordName.c_str());
+      
+        // Enclose custom code in a block to avoid namespace conflicts
+        segments->fFSCode.appendf("{\n");
+        segments->fFSCode.append(fragmentShader);
+        segments->fFSCode.appendf("}\n");
+    }
 }
 
 
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 0d8c39a..73bfa94 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -19,6 +19,7 @@
 #include "SkXfermode.h"
 
 class GrBinHashKeyBuilder;
+class GrGLProgramStage;
 
 struct ShaderCodeSegments;
 
@@ -49,6 +50,7 @@
      *  but in a separate cacheable container.
      */
     bool genProgram(const GrGLContextInfo& gl,
+                    GrCustomStage** customStages,
                     CachedData* programData) const;
 
      /**
@@ -180,6 +182,10 @@
             uint8_t fCoordMapping;  // casts to enum CoordMapping
             uint8_t fKernelWidth;
 
+            /** Non-zero if user-supplied code will write the stage's
+                contribution to the fragment shader. */
+            uint16_t fCustomStageKey;
+
             GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
                              kInConfigBitMask);
 
@@ -305,10 +311,12 @@
     class CachedData : public ::GrNoncopyable {
     public:
         CachedData() {
+            for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+                fCustomStage[i] = NULL;
+            }
         }
 
-        ~CachedData() {
-        }
+        ~CachedData();
 
         void copyAndTakeOwnership(CachedData& other) {
             memcpy(this, &other, sizeof(*this));
@@ -340,6 +348,8 @@
         bool                        fRadial2PosRoot[GrDrawState::kNumStages];
         GrRect                      fTextureDomain[GrDrawState::kNumStages];
 
+        GrGLProgramStage*           fCustomStage[GrDrawState::kNumStages];
+
     private:
         enum Constants {
             kUniLocationPreAllocSize = 8
@@ -366,7 +376,8 @@
                       const char* fsOutColor,
                       const char* vsInCoord,
                       ShaderCodeSegments* segments,
-                      StageUniLocations* locations) const;
+                      StageUniLocations* locations,
+                      GrGLProgramStage* override) const;
 
     void genGeometryShader(const GrGLContextInfo& gl,
                            ShaderCodeSegments* segments) const;
diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
index 83c736d..a564399 100644
--- a/src/gpu/gl/GrGLProgramStage.h
+++ b/src/gpu/gl/GrGLProgramStage.h
@@ -96,8 +96,6 @@
 
     void setSamplerMode(SamplerMode shaderMode) { fSamplerMode = shaderMode; }
 
-protected:
-
     /** Returns the *effective* coord name after any perspective divide
         or other transform. */
     GrStringBuilder emitTextureSetup(GrStringBuilder* code,
@@ -106,6 +104,8 @@
                                      int coordDims,
                                      int varyingDims);
 
+protected:
+
     /** Convenience function for subclasses to write texture2D() or
         texture2DProj(), depending on fSamplerMode. */
     void emitTextureLookup(GrStringBuilder* code,
@@ -130,16 +130,19 @@
     /** Returns a short unique identifier for this subclass x its
         parameters. If the key differs, different shader code must
         be generated; if the key matches, shader code can be reused.
-        0 == no custom stage.  */
+        0 == no custom stage. */
     virtual uint16_t stageKey(const GrCustomStage*);
 
+    /** Returns a new instance of the appropriate implementation class
+        for the given GrCustomStage; caller is responsible for deleting
+        the object. */
     virtual GrGLProgramStage* createGLInstance(GrCustomStage*) = 0;
 
 protected:
 
     /** Disable default constructor - instances should be singletons
         with static factory functions: our test examples are all stateless,
-        but we suspect that future implementations may want to cache data?  */
+        but we suspect that future implementations may want to cache data? */
     GrGLProgramStageFactory() { }
 };
 
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
index 9627107..1fcfbbe 100644
--- a/src/gpu/gl/GrGpuGLShaders.cpp
+++ b/src/gpu/gl/GrGpuGLShaders.cpp
@@ -8,7 +8,9 @@
 
 
 #include "../GrBinHashKey.h"
+#include "GrCustomStage.h"
 #include "GrGLProgram.h"
+#include "GrGLProgramStage.h"
 #include "GrGLSL.h"
 #include "GrGpuGLShaders.h"
 #include "../GrGpuVertex.h"
@@ -82,13 +84,14 @@
         }
     }
 
-    GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc) {
+    GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc,
+                                            GrCustomStage** stages) {
         Entry newEntry;
         newEntry.fKey.setKeyData(desc.keyData());
         
         Entry* entry = fHashCache.find(newEntry.fKey);
         if (NULL == entry) {
-            if (!desc.genProgram(fGL, &newEntry.fProgramData)) {
+            if (!desc.genProgram(fGL, stages, &newEntry.fProgramData)) {
                 return NULL;
             }
             if (fCount < kMaxEntries) {
@@ -242,6 +245,8 @@
             pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
         }
 
+        GrCustomStage* customStages[GrDrawState::kNumStages];
+
         for (int s = 0; s < GrDrawState::kNumStages; ++s) {
             // enable the stage?
             if (random_bool(&random)) {
@@ -288,9 +293,13 @@
                     stage.fInConfigFlags &= ~kMulByAlphaMask;
                     break;
             }
+
+            stage.fCustomStageKey = 0;
+            customStages[s] = NULL;
         }
         CachedData cachedData;
-        if (!program.genProgram(this->glContextInfo(), &cachedData)) {
+        if (!program.genProgram(this->glContextInfo(), customStages,
+                                &cachedData)) {
             return false;
         }
         DeleteProgram(this->glInterface(), &cachedData);
@@ -772,8 +781,10 @@
         return false;
     }
 
-    this->buildProgram(type, blendOpts, dstCoeff);
-    fProgramData = fProgramCache->getProgramData(fCurrentProgram);
+    GrCustomStage* customStages [GrDrawState::kNumStages];
+    this->buildProgram(type, blendOpts, dstCoeff, customStages);
+    fProgramData = fProgramCache->getProgramData(fCurrentProgram,
+                                                 customStages);
     if (NULL == fProgramData) {
         GrAssert(!"Failed to create program!");
         return false;
@@ -814,6 +825,13 @@
             this->flushTexelSize(s);
 
             this->flushTextureDomain(s);
+
+            if (NULL != fProgramData->fCustomStage[s]) {
+                const GrSamplerState& sampler =
+                    this->getDrawState().getSampler(s);
+                fProgramData->fCustomStage[s]->setData(
+                    this->glInterface(), sampler.getCustomStage());
+            }
         }
     }
     this->flushEdgeAAData();
@@ -962,9 +980,29 @@
     fHWGeometryState.fArrayPtrsDirty = false;
 }
 
+namespace {
+
+void setup_custom_stage(GrGLProgram::ProgramDesc::StageDesc* stage,
+                        const GrSamplerState& sampler,
+                        GrCustomStage** customStages,
+                        GrGLProgram* program, int index) {
+    GrCustomStage* customStage = sampler.getCustomStage();
+    if (customStage) {
+        GrGLProgramStageFactory* factory = customStage->getGLFactory();
+        stage->fCustomStageKey = factory->stageKey(customStage);
+        customStages[index] = customStage;
+    } else {
+        stage->fCustomStageKey = 0;
+        customStages[index] = NULL;
+    }
+}
+
+}
+
 void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
                                   BlendOptFlags blendOpts,
-                                  GrBlendCoeff dstCoeff) {
+                                  GrBlendCoeff dstCoeff,
+                                  GrCustomStage** customStages) {
     ProgramDesc& desc = fCurrentProgram.fProgramDesc;
     const GrDrawState& drawState = this->getDrawState();
 
@@ -1170,12 +1208,18 @@
             } else {
                 stage.fKernelWidth = 0;
             }
+
+            setup_custom_stage(&stage, sampler, customStages,
+                               &fCurrentProgram, s);
+
         } else {
             stage.fOptFlags         = 0;
             stage.fCoordMapping     = (StageDesc::CoordMapping) 0;
             stage.fInConfigFlags    = 0;
             stage.fFetchMode        = (StageDesc::FetchMode) 0;
             stage.fKernelWidth      = 0;
+            stage.fCustomStageKey   = 0;
+            customStages[s] = NULL;
         }
     }
 
diff --git a/src/gpu/gl/GrGpuGLShaders.h b/src/gpu/gl/GrGpuGLShaders.h
index 39bc974..2ce95eb 100644
--- a/src/gpu/gl/GrGpuGLShaders.h
+++ b/src/gpu/gl/GrGpuGLShaders.h
@@ -14,6 +14,7 @@
 #include "GrGpuGL.h"
 #include "GrGLProgram.h"
 
+class GrCustomStage;
 class GrGpuGLProgram;
 
 // Programmable OpenGL or OpenGL ES 2.0
@@ -86,7 +87,8 @@
 
     void buildProgram(GrPrimitiveType typeBlend,
                       BlendOptFlags blendOpts,
-                      GrBlendCoeff dstCoeff);
+                      GrBlendCoeff dstCoeff,
+                      GrCustomStage** customStages);
 
     ProgramCache*               fProgramCache;
     CachedData*                 fProgramData;