Towards issue #106

Adds notion of texture multiple stages but currently just uses 1.


git-svn-id: http://skia.googlecode.com/svn/trunk@694 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 942afa3..e5e378f 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -138,9 +138,9 @@
         if (NULL != texture) {

             GrGpu::AutoStateRestore asr(fGpu);

             fGpu->setRenderTarget(texture->asRenderTarget());

-            fGpu->setTexture(clampEntry->texture());

+            fGpu->setTexture(0, clampEntry->texture());

             fGpu->setStencilPass(GrGpu::kNone_StencilPass);

-            fGpu->setTextureMatrix(GrMatrix::I());

+            fGpu->setTextureMatrix(0, GrMatrix::I());

             fGpu->setViewMatrix(GrMatrix::I());

             fGpu->setAlpha(0xff);

             fGpu->setBlendFunc(GrGpu::kOne_BlendCoeff, GrGpu::kZero_BlendCoeff);

@@ -150,10 +150,10 @@
             GrSamplerState stretchSampler(GrSamplerState::kClamp_WrapMode,

                                           GrSamplerState::kClamp_WrapMode,

                                           sampler.isFilter());

-            fGpu->setSamplerState(stretchSampler);

+            fGpu->setSamplerState(0, stretchSampler);

 

             static const GrVertexLayout layout =

-                                GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;

+                                GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);

             GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);

 

             if (arg.succeeded()) {

@@ -303,7 +303,7 @@
 

 void GrContext::drawRect(const GrRect& rect, bool useTexture, GrScalar width) {

     GrVertexLayout layout = useTexture ?

-                            GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :

+                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :

                             0;

 

     static const int worstCaseVertCount = 10;

@@ -582,7 +582,7 @@
                                         tol);

     GrVertexLayout layout = 0;

     if (useTexture) {

-        layout = GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit;

+        layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);

     }

     // add 4 to hold the bounding rect

     GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, maxPts + 4, 0);

@@ -808,14 +808,14 @@
     fGpu->setViewMatrix(matrix);

     matrix.setScale(GR_Scalar1 / texture->allocWidth(),

                     GR_Scalar1 / texture->allocHeight());

-    fGpu->setTextureMatrix(matrix);

+    fGpu->setTextureMatrix(0, matrix);

 

     fGpu->disableState(GrDrawTarget::kClip_StateBit);

     fGpu->setAlpha(0xFF);

     fGpu->setBlendFunc(GrDrawTarget::kOne_BlendCoeff,

                        GrDrawTarget::kZero_BlendCoeff);

-    fGpu->setTexture(texture);

-    fGpu->setSamplerState(GrSamplerState::ClampNoFilter());

+    fGpu->setTexture(0, texture);

+    fGpu->setSamplerState(0, GrSamplerState::ClampNoFilter());

 

     this->fillRect(GrRect(0, 0, GrIntToScalar(width), GrIntToScalar(height)),

                    true);

@@ -841,8 +841,8 @@
     return fGpu->createIndexBuffer(size, dynamic);

 }

 

-void GrContext::setTexture(GrTexture* texture) {

-    fGpu->setTexture(texture);

+void GrContext::setTexture(int stage, GrTexture* texture) {

+    fGpu->setTexture(stage, texture);

 }

 

 void GrContext::setRenderTarget(GrRenderTarget* target) {

@@ -858,12 +858,12 @@
     fGpu->setDefaultRenderTargetSize(width, height);

 }

 

-void GrContext::setSamplerState(const GrSamplerState& samplerState) {

-    fGpu->setSamplerState(samplerState);

+void GrContext::setSamplerState(int stage, const GrSamplerState& samplerState) {

+    fGpu->setSamplerState(stage, samplerState);

 }

 

-void GrContext::setTextureMatrix(const GrMatrix& m) {

-    fGpu->setTextureMatrix(m);

+void GrContext::setTextureMatrix(int stage, const GrMatrix& m) {

+    fGpu->setTextureMatrix(stage, m);

 }

 

 void GrContext::getViewMatrix(GrMatrix* m) const {

diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 82f94a3..7ea9ff2 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -18,93 +18,265 @@
 #include "GrDrawTarget.h"
 #include "GrGpuVertex.h"
 
-#define VERTEX_LAYOUT_ASSERTS \
-    GrAssert(!(vertexLayout & kTextFormat_VertexLayoutBit) ||           \
-             vertexLayout == kTextFormat_VertexLayoutBit);              \
-    GrAssert(!(vertexLayout & kSeparateTexCoord_VertexLayoutBit) ||     \
-             !(vertexLayout & kPositionAsTexCoord_VertexLayoutBit));
+// recursive helper for creating mask with all the tex coord bits set for
+// one stage
+template <int N>
+static int stage_mask_recur(int stage) {
+    return GrDrawTarget::StageTexCoordVertexLayoutBit(stage, N) | 
+           stage_mask_recur<N+1>(stage);
+}
+template<>
+static int stage_mask_recur<GrDrawTarget::kNumStages>(int) { return 0; }
 
-size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
-    VERTEX_LAYOUT_ASSERTS
-    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
-        return 2 * sizeof(GrGpuTextVertex);
-    } else {
-        size_t size = sizeof(GrPoint);
-        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
-            size += sizeof(GrPoint);
-        }
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            size += sizeof(GrColor);
-        }
-        return size;
-    }
+// mask of all tex coord indices for one stage
+static int stage_tex_coord_mask(int stage) {
+    return stage_mask_recur<0>(stage);
 }
 
-int GrDrawTarget::VertexTexCoordOffset(GrVertexLayout vertexLayout) {
-    VERTEX_LAYOUT_ASSERTS
-    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
-        return sizeof(GrGpuTextVertex);
-    } else if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
-        return sizeof(GrPoint);
-    } else if (vertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+// mask of all bits relevant to one stage
+static int stage_mask(int stage) {
+    return stage_tex_coord_mask(stage) | 
+           GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(stage);
+}
+
+// recursive helper for creating mask of with all bits set relevant to one
+// texture coordinate index
+template <int N>
+static int tex_coord_mask_recur(int texCoordIdx) {
+    return GrDrawTarget::StageTexCoordVertexLayoutBit(N, texCoordIdx) | 
+           tex_coord_mask_recur<N+1>(texCoordIdx);
+}
+template<>
+static int tex_coord_mask_recur<GrDrawTarget::kMaxTexCoords>(int) { return 0; }
+
+// mask of all bits relevant to one texture coordinate index
+static int tex_coord_idx_mask(int texCoordIdx) {
+    return tex_coord_mask_recur<0>(texCoordIdx);
+}
+
+bool check_layout(GrVertexLayout layout) {
+    // can only have 1 or 0 bits set for each stage.
+    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+        int stageBits = layout & stage_mask(s);
+        if (stageBits && !GrIsPow2(stageBits)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+    
+    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? 
+                        sizeof(GrGpuTextVertex) :
+                        sizeof(GrPoint);
+
+    size_t size = vecSize; // position
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (tex_coord_idx_mask(t) & vertexLayout) {
+            size += vecSize;
+        }
+    }
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    return size;
+}
+
+int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+    if (StagePosAsTexCoordVertexLayoutBit(stage) & vertexLayout) {
         return 0;
     }
+    int tcIdx = VertexTexCoordsForStage(stage, vertexLayout);
+    if (tcIdx >= 0) {
+        
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? 
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize; // position
+        // figure out how many tex coordinates are present and precede this one.
+        for (int t = 0; t < tcIdx; ++t) {
+            if (tex_coord_idx_mask(t) & vertexLayout) {
+                offset += vecSize;
+            }
+        }
+        return offset;
+    }
+
     return -1;
 }
 
 int  GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
-    VERTEX_LAYOUT_ASSERTS
+    GrAssert(check_layout(vertexLayout));
+    
     if (vertexLayout & kColor_VertexLayoutBit) {
-        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
-            return 2 * sizeof(GrPoint);
-        } else {
-            return sizeof(GrPoint);
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? 
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize; // position
+        // figure out how many tex coordinates are present and precede this one.
+        for (int t = 0; t < kMaxTexCoords; ++t) {
+            if (tex_coord_idx_mask(t) & vertexLayout) {
+                offset += vecSize;
+            }
         }
+        return offset;
     }
     return -1;
 }
 
-int GrDrawTarget::VertexSizeAndOffsets(GrVertexLayout vertexLayout,
-                                       int* texCoordOffset,
-                                       int* colorOffset) {
-    VERTEX_LAYOUT_ASSERTS
-
-    GrAssert(NULL != texCoordOffset);
+int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
+                                             int texCoordOffsetsByIdx[kMaxTexCoords],
+                                             int* colorOffset) {
+    GrAssert(check_layout(vertexLayout));
+    
+    GrAssert(NULL != texCoordOffsetsByIdx);
     GrAssert(NULL != colorOffset);
 
-    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
-        *texCoordOffset = sizeof(GrGpuTextVertex);
-        *colorOffset = 0;
-        return 2 * sizeof(GrGpuTextVertex);
-    } else {
-        size_t size = sizeof(GrPoint);
-        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
-            *texCoordOffset = sizeof(GrPoint);
-            size += sizeof(GrPoint);
-        } else if (vertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
-            *texCoordOffset = 0;
+    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? 
+                                                    sizeof(GrGpuTextVertex) :
+                                                    sizeof(GrPoint);
+    int size = vecSize; // position
+    
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (tex_coord_idx_mask(t) & vertexLayout) {
+            texCoordOffsetsByIdx[t] = size;
+            size += vecSize;
         } else {
-            *texCoordOffset = -1;
+            texCoordOffsetsByIdx[t] = -1;
         }
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            *colorOffset = size;
-            size += sizeof(GrColor);
-        } else {
-            *colorOffset = -1;
-        }
-        return size;
     }
+    if (kColor_VertexLayoutBit & vertexLayout) {
+        *colorOffset = size;
+        size += sizeof(GrColor);
+    } else {
+        *colorOffset = -1;
+    }
+    return size;
 }
 
-bool GrDrawTarget::VertexHasTexCoords(GrVertexLayout vertexLayout) {
-    return !!(vertexLayout & (kSeparateTexCoord_VertexLayoutBit   |
-                              kPositionAsTexCoord_VertexLayoutBit |
-                              kTextFormat_VertexLayoutBit));
+int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
+                                              int texCoordOffsetsByStage[kNumStages],
+                                              int* colorOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    GrAssert(NULL != texCoordOffsetsByStage);
+    GrAssert(NULL != colorOffset);
+    
+    int texCoordOffsetsByIdx[kMaxTexCoords];
+    int size = VertexSizeAndOffsetsByIdx(vertexLayout, 
+                                         texCoordOffsetsByIdx, 
+                                         colorOffset);
+    for (int s = 0; s < kNumStages; ++s) {
+        int tcIdx;
+        if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
+            texCoordOffsetsByStage[s] = 0;
+        } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) {
+            texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx];
+        } else {
+            texCoordOffsetsByStage[s] = -1;
+        }
+    }
+    return size;    
+}
+
+bool GrDrawTarget::VertexUsesStage(int stage, GrVertexLayout vertexLayout) {
+    GrAssert(stage < kNumStages);
+    GrAssert(check_layout(vertexLayout));
+    return !!(stage_mask(stage) & vertexLayout);
+}
+
+bool GrDrawTarget::VertexUsesTexCoordIdx(int coordIndex, 
+                                         GrVertexLayout vertexLayout) {
+    GrAssert(coordIndex < kMaxTexCoords);     
+    GrAssert(check_layout(vertexLayout));
+    return !!(tex_coord_idx_mask(coordIndex) & vertexLayout);
+}
+
+int GrDrawTarget::VertexTexCoordsForStage(int stage, GrVertexLayout vertexLayout) {
+    GrAssert(stage < kNumStages);
+    GrAssert(check_layout(vertexLayout));
+    int bit = vertexLayout & stage_tex_coord_mask(stage);
+    if (bit) {
+        // figure out which set of texture coordates is used
+        // bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
+        // and start at bit 0.
+        GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
+        return (32 - Gr_clz(bit) - 1) / kNumStages;
+    }
+    return -1;
+}
+
+void GrDrawTarget::VertexLayoutUnitTest() {
+    // not necessarily exhaustive
+    static bool run;
+    if (!run) {
+        run = true;
+        for (int s = 0; s < kNumStages; ++s) {
+
+            GrAssert(!VertexUsesStage(s, 0));
+            GrAssert(-1 == VertexStageCoordOffset(s, 0));
+            GrVertexLayout stageMask = 0;
+            for (int t = 0; t < kMaxTexCoords; ++t) {
+                stageMask |= StageTexCoordVertexLayoutBit(s,t);
+            }
+            GrAssert(1 == kMaxTexCoords || !check_layout(stageMask));            
+            GrAssert(stage_tex_coord_mask(s) == stageMask);
+            stageMask |= StagePosAsTexCoordVertexLayoutBit(s);
+            GrAssert(stage_mask(s) == stageMask);
+            GrAssert(!check_layout(stageMask));
+        }
+        for (int t = 0; t < kMaxTexCoords; ++t) {
+            GrVertexLayout tcMask = 0;
+            GrAssert(!VertexUsesTexCoordIdx(t, 0));
+            for (int s = 0; s < kNumStages; ++s) {
+                tcMask |= StageTexCoordVertexLayoutBit(s,t);
+                GrAssert(VertexUsesStage(s, tcMask));
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
+                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
+                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
+                for (int s2 = s + 1; s2 < kNumStages; ++s2) {
+                    GrAssert(-1 == VertexStageCoordOffset(s2, tcMask));
+                    GrAssert(!VertexUsesStage(s2, tcMask));
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
+                    
+                    GrVertexLayout posAsTex = tcMask | StagePosAsTexCoordVertexLayoutBit(s2);
+                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
+                    GrAssert(VertexUsesStage(s2, posAsTex));
+                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
+                }
+                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
+            }
+            GrAssert(tex_coord_idx_mask(t) == tcMask);
+            GrAssert(check_layout(tcMask));
+            
+            int stageOffsets[kNumStages];
+            int colorOffset;
+            int size;
+            size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset);
+            GrAssert(2*sizeof(GrPoint) == size);
+            GrAssert(-1 == colorOffset);
+            for (int s = 0; s < kNumStages; ++s) {
+                GrAssert(VertexUsesStage(s, tcMask));
+                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+            }
+        }
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 GrDrawTarget::GrDrawTarget() {
+#if GR_DEBUG
+    VertexLayoutUnitTest();
+#endif
     fReservedGeometry.fLocked = false;
 #if GR_DEBUG
     fReservedGeometry.fVertexCount  = ~0;
@@ -123,12 +295,14 @@
     return fClip;
 }
 
-void GrDrawTarget::setTexture(GrTexture* tex) {
-    fCurrDrawState.fTexture = tex;
+void GrDrawTarget::setTexture(int stage, GrTexture* tex) {
+    GrAssert(stage >= 0 && stage < kNumStages);
+    fCurrDrawState.fTextures[stage] = tex;
 }
 
-GrTexture* GrDrawTarget::currentTexture() const {
-    return fCurrDrawState.fTexture;
+GrTexture* GrDrawTarget::currentTexture(int stage) const {
+    GrAssert(stage >= 0 && stage < kNumStages);
+    return fCurrDrawState.fTextures[stage];
 }
 
 void GrDrawTarget::setRenderTarget(GrRenderTarget* target) {
@@ -139,21 +313,25 @@
     return fCurrDrawState.fRenderTarget;
 }
 
-void GrDrawTarget::concatViewMatrix(const GrMatrix& matrix) {
-    GrMatrix mv;
-    mv.setConcat(fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode], matrix);
-    this->loadMatrix(mv, kModelView_MatrixMode);
+void GrDrawTarget::setViewMatrix(const GrMatrix& m) {
+    fCurrDrawState.fViewMatrix = m;
 }
 
+void GrDrawTarget::concatViewMatrix(const GrMatrix& matrix) {
+    fCurrDrawState.fViewMatrix.preConcat(matrix);
+}
+
+// Can't this just return a const&
 void GrDrawTarget::getViewMatrix(GrMatrix* matrix) const {
-    *matrix = fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    *matrix = fCurrDrawState.fViewMatrix;
 }
 
 bool GrDrawTarget::getViewInverse(GrMatrix* matrix) const {
-    // Can we cache this somewhere?
+    // Mike:  Can we cache this somewhere? 
+    // Brian: Sure, do we use it often?
 
     GrMatrix inverse;
-    if (fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode].invert(&inverse)) {
+    if (fCurrDrawState.fViewMatrix.invert(&inverse)) {
         if (matrix) {
             *matrix = inverse;
         }
@@ -162,8 +340,14 @@
     return false;
 }
 
-void GrDrawTarget::setSamplerState(const GrSamplerState& state) {
-    fCurrDrawState.fSamplerState = state;
+void GrDrawTarget::setSamplerState(int stage, const GrSamplerState& state) {
+    GrAssert(stage >= 0 && stage < kNumStages);
+    fCurrDrawState.fSamplerStates[stage] = state;
+}
+
+void GrDrawTarget::setTextureMatrix(int stage, const GrMatrix& m) {
+    GrAssert(stage >= 0 && stage < kNumStages);
+    fCurrDrawState.fTextureMatrices[stage] = m;
 }
 
 void GrDrawTarget::setStencilPass(StencilPass pass) {
@@ -182,10 +366,6 @@
     fCurrDrawState.fFlagBits &= ~(bits);
 }
 
-void GrDrawTarget::loadMatrix(const GrMatrix& matrix, MatrixMode m) {
-    fCurrDrawState.fMatrixModeCache[m] = matrix;
-}
-
 void GrDrawTarget::setPointSize(float size) {
     fCurrDrawState.fPointSize = size;
 }
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
index 4e9d922..9460086 100644
--- a/gpu/src/GrGLTexture.cpp
+++ b/gpu/src/GrGLTexture.cpp
@@ -144,6 +144,9 @@
                                     uint32_t width,
                                     uint32_t height,
                                     const void* srcData) {
+    
+    fGpuGL->setSpareTextureUnit();
+
     // glCompressedTexSubImage2D doesn't support any formats
     // (at least without extensions)
     GrAssert(fUploadFormat != GR_PALETTE8_RGBA8);
@@ -152,7 +155,6 @@
     // then we have to modify this code to flip the srcData
     GrAssert(kTopDown_Orientation == fOrientation);
     GR_GL(BindTexture(GL_TEXTURE_2D, fTextureID));
-    fGpuGL->notifyTextureBind(this);
     GR_GL(PixelStorei(GL_UNPACK_ALIGNMENT, fUploadByteCount));
     GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 
                         fUploadFormat, fUploadType, srcData));
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index f9ee217..fe6d0c3 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -92,20 +92,32 @@
     }
 
     // If the src coef will always be 1...
-    bool fullSrc = kSA_BlendCoeff == fCurrDrawState.fSrcBlend ||
-                   kOne_BlendCoeff == fCurrDrawState.fSrcBlend;
+    if (kSA_BlendCoeff != fCurrDrawState.fSrcBlend &&
+        kOne_BlendCoeff != fCurrDrawState.fSrcBlend) {
+        return false;
+    }
 
     // ...and the dst coef is always 0...
-    bool noDst = kISA_BlendCoeff == fCurrDrawState.fDstBlend ||
-                 kZero_BlendCoeff == fCurrDrawState.fDstBlend;
+    if (kISA_BlendCoeff != fCurrDrawState.fDstBlend &&
+        kZero_BlendCoeff != fCurrDrawState.fDstBlend) {
+        return false;
+    }
 
     // ...and there isn't a texture with an alpha channel...
-    bool noTexAlpha = !VertexHasTexCoords(fGeometrySrc.fVertexLayout)  ||
-        fCurrDrawState.fTexture->config() == GrTexture::kRGB_565_PixelConfig ||
-        fCurrDrawState.fTexture->config() == GrTexture::kRGBX_8888_PixelConfig;
+    for (int s = 0; s < kNumStages; ++s) {
+        if (VertexUsesStage(s, fGeometrySrc.fVertexLayout)) {
+            GrAssert(NULL != fCurrDrawState.fTextures[s]);
+            GrTexture::PixelConfig config = fCurrDrawState.fTextures[s]->config();
+            
+            if (GrTexture::kRGB_565_PixelConfig != config &&
+                GrTexture::kRGBX_8888_PixelConfig != config) {
+                return false;
+            }
+        }
+    }
 
     // ...then we disable blend.
-    return fullSrc && noDst && noTexAlpha;
+    return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 4d539d2..6de6db3 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -29,6 +29,10 @@
 static const GLuint GR_MAX_GLUINT = ~0;
 static const GLint  GR_INVAL_GLINT = ~0;
 
+// we use a spare texture unit to avoid 
+// mucking with the state of any of the stages.
+static const int SPARE_TEX_UNIT = GrGpuGL::kNumStages; 
+
 #define SKIP_CACHE_CHECK    true
 
 static const GLenum gXfermodeCoeff2Blend[] = {
@@ -69,7 +73,7 @@
         *minor = 0;
         return;
     }
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     int n = sscanf(v, "%d.%d", major, minor);
     if (n != 2) {
         GrAssert(0);
@@ -96,6 +100,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool fbo_test(GrGLExts exts, int w, int h) {
+
+    GLint savedFBO;
+    GLint savedTexUnit;
+    GR_GL(GetIntegerv(GL_ACTIVE_TEXTURE, &savedTexUnit));
+    GR_GL(GetIntegerv(GR_FRAMEBUFFER_BINDING, &savedFBO));
+    
+    GR_GL(ActiveTexture(GL_TEXTURE0 + SPARE_TEX_UNIT));
+    
     GLuint testFBO;
     GR_GLEXT(exts, GenFramebuffers(1, &testFBO));
     GR_GLEXT(exts, BindFramebuffer(GR_FRAMEBUFFER, testFBO));
@@ -113,6 +125,10 @@
     GLenum status = GR_GLEXT(exts, CheckFramebufferStatus(GR_FRAMEBUFFER));
     GR_GLEXT(exts, DeleteFramebuffers(1, &testFBO));
     GR_GL(DeleteTextures(1, &testRTTex));
+    
+    GR_GL(ActiveTexture(savedTexUnit));
+    GR_GLEXT(exts, BindFramebuffer(GR_FRAMEBUFFER, savedFBO));
+    
     return status == GR_FRAMEBUFFER_COMPLETE;
 }
 
@@ -155,7 +171,20 @@
                                                 this);
     fHWDrawState.fRenderTarget = fDefaultRenderTarget;
     fRenderTargetChanged = true;
-
+    
+    GLint maxTextureUnits;
+    // check FS and fixed-function texture unit limits
+    // we only use textures in the fragment stage currently.
+    // checks are > to make sure we have a spare unit.
+#if GR_SUPPORT_GLDESKTOP || GR_SUPPORT_GLES2
+    GR_GL(GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits));
+    GrAssert(maxTextureUnits > kNumStages);
+#endif
+#if GR_SUPPORT_GLDESKTOP || GR_SUPPORT_GLES1
+    GR_GL(GetIntegerv(GL_MAX_TEXTURE_UNITS, &maxTextureUnits));
+    GrAssert(maxTextureUnits > kNumStages);
+#endif
+    
     fCurrDrawState = fHWDrawState;
 
     ////////////////////////////////////////////////////////////////////////////
@@ -198,7 +227,7 @@
             GrPrintf("MSAA Support: APPLE ES EXT.\n");
         }
     }
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     else if ((major >= 3) ||
              has_gl_extension("GL_ARB_framebuffer_object") ||
              (has_gl_extension("GL_EXT_framebuffer_multisample") &&
@@ -236,7 +265,7 @@
         }
     }
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     fHasStencilWrap = (major >= 2 || (major == 1 && minor >= 4)) ||
                       has_gl_extension("GL_EXT_stencil_wrap");
 #else
@@ -246,7 +275,7 @@
         GrPrintf("Stencil Wrap: %s\n", (fHasStencilWrap ? "YES" : "NO"));
     }
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     // we could also look for GL_ATI_separate_stencil extension or
     // GL_EXT_stencil_two_side but they use different function signatures
     // than GL2.0+ (and than each other).
@@ -261,7 +290,7 @@
     }
 
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     fRGBA8Renderbuffer = true;
 #else
     fRGBA8Renderbuffer = has_gl_extension("GL_OES_rgb8_rgba8");
@@ -271,7 +300,7 @@
     }
 
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     fBufferLockSupport = true; // we require VBO support and the desktop VBO
                                // extension includes glMapBuffer.
 #else
@@ -281,7 +310,7 @@
         GrPrintf("Map Buffer: %s\n", (fBufferLockSupport ? "YES" : "NO"));
     }
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     fNPOTTextureSupport =
         (major >= 2 || has_gl_extension("GL_ARB_texture_non_power_of_two")) ?
             kFull_NPOTTextureType :
@@ -433,7 +462,7 @@
     GR_GL(Disable(GL_CULL_FACE));
 
     GR_GL(Disable(GL_DITHER));
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     GR_GL(Disable(GL_LINE_SMOOTH));
     GR_GL(Disable(GL_POINT_SMOOTH));
     GR_GL(Disable(GL_MULTISAMPLE));
@@ -442,7 +471,8 @@
     // we only ever use lines in hairline mode
     GR_GL(LineWidth(1));
 
-    GR_GL(ActiveTexture(GL_TEXTURE0));
+    // invalid
+    fActiveTextureUnitIdx = -1;
 
     fHWDrawState.fFlagBits = 0;
 
@@ -451,21 +481,22 @@
     fHWDrawState.fDstBlend = (BlendCoeff)-1;
     fHWDrawState.fColor = GrColor_ILLEGAL;
     fHWDrawState.fPointSize = -1;
-    fHWDrawState.fTexture = NULL;
-
+    
+    fHWDrawState.fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax); // illegal
+    
+    for (int s = 0; s < kNumStages; ++s) {
+        fHWDrawState.fTextures[s] = NULL;
+        fHWDrawState.fSamplerStates[s].setRadial2Params(-GR_ScalarMax,
+                                                        -GR_ScalarMax,
+                                                        true);
+        fHWDrawState.fTextureMatrices[s].setScale(GR_ScalarMax, GR_ScalarMax); 
+    }
+    
     GR_GL(Scissor(0,0,0,0));
     fHWBounds.fScissorRect.setLTRB(0,0,0,0);
     fHWBounds.fScissorEnabled = false;
     GR_GL(Disable(GL_SCISSOR_TEST));
 
-    fHWDrawState.fSamplerState.setRadial2Params(-GR_ScalarMax,
-                                                -GR_ScalarMax,
-                                                true);
-
-    for (int i = 0; i < kMatrixModeCount; i++) {
-        fHWDrawState.fMatrixModeCache[i].setScale(GR_ScalarMax, GR_ScalarMax); // illegal
-    }
-
     // disabling the stencil test also disables
     // stencil buffer writes
     GR_GL(Disable(GL_STENCIL_TEST));
@@ -490,7 +521,7 @@
 
 
 // defines stencil formats from more to less preferred
-#if GR_GL_ES
+#if GR_SUPPORT_GLES
     GLenum GR_GL_STENCIL_FORMAT_ARRAY[] = {
         GR_STENCIL_INDEX8,
     };
@@ -544,6 +575,8 @@
     ++fStats.fTextureCreateCnt;
 #endif
 
+    setSpareTextureUnit();
+    
     static const GrGLTexture::TexParams DEFAULT_PARAMS = {
         GL_NEAREST,
         GL_CLAMP_TO_EDGE,
@@ -585,7 +618,7 @@
      *  to trim those off here, since GL doesn't let us pass the rowBytes as
      *  a parameter to glTexImage2D
      */
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     if (srcData) {
         GR_GL(PixelStorei(GL_UNPACK_ROW_LENGTH,
                           rowBytes / glDesc.fUploadByteCount));
@@ -636,10 +669,6 @@
     GR_GL(TexParameteri(GL_TEXTURE_2D,
                         GL_TEXTURE_WRAP_T,
                         DEFAULT_PARAMS.fWrapT));
-#if GR_COLLECT_STATS
-    ++fStats.fTextureChngCnt;
-#endif
-    fHWDrawState.fTexture = NULL;
 
     GR_GL(PixelStorei(GL_UNPACK_ALIGNMENT, glDesc.fUploadByteCount));
     if (GrTexture::kIndex_8_PixelConfig == desc.fFormat &&
@@ -763,7 +792,6 @@
                 GR_GL(DeleteTextures(1, &glDesc.fTextureID));
                 GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fTexFBOID));
                 GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fRTFBOID));
-                fHWDrawState.fTexture = NULL;
                 return return_null_texture();
             }
         } else {
@@ -776,12 +804,10 @@
             attempts = GR_ARRAY_COUNT(GR_GL_STENCIL_FORMAT_ARRAY);
         }
 
-        // need to unbind the texture before we call FramebufferTexture2D
+        // someone suggested that some systems might require
+        // unbinding the texture before we call FramebufferTexture2D 
+        // (seems unlikely)
         GR_GL(BindTexture(GL_TEXTURE_2D, 0));
-#if GR_COLLECT_STATS
-        ++fStats.fTextureChngCnt;
-#endif
-        GrAssert(NULL == fHWDrawState.fTexture);
 
         err = ~GL_NO_ERROR;
         for (int i = 0; i < attempts; ++i) {
@@ -869,7 +895,7 @@
             }
             status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER));
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
             // On some implementations you have to be bound as DEPTH_STENCIL.
             // (Even binding to DEPTH and STENCIL separately with the same
             // buffer doesn't work.)
@@ -890,7 +916,7 @@
             if (status != GR_FRAMEBUFFER_COMPLETE) {
                 GrPrintf("-- glCheckFramebufferStatus %x %d %d\n",
                          status, desc.fWidth, desc.fHeight);
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
                 if (rtIDs.fStencilRenderbufferID) {
                     GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER,
                                                      GR_DEPTH_STENCIL_ATTACHMENT,
@@ -1428,65 +1454,75 @@
 
 void GrGpuGL::flushGLStateCommon(PrimitiveType type) {
 
-    bool usingTexture = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
+    for (int s = 0; s < kNumStages; ++s) {
+        bool usingTexture = VertexUsesStage(s, fGeometrySrc.fVertexLayout);
 
-    // bind texture and set sampler state
-    if (usingTexture) {
-        GrGLTexture* nextTexture = (GrGLTexture*)fCurrDrawState.fTexture;
+        // bind texture and set sampler state
+        if (usingTexture) {
+            GrGLTexture* nextTexture = (GrGLTexture*)fCurrDrawState.fTextures[s];
 
-        if (NULL != nextTexture) {
-            // if we created a rt/tex and rendered to it without using a texture
-            // and now we're texuring from the rt it will still be the last bound
-            // texture, but it needs resolving. So keep this out of the last
-            // != next check.
-            resolveTextureRenderTarget(nextTexture);
+            if (NULL != nextTexture) {
+                // if we created a rt/tex and rendered to it without using a 
+                // texture and now we're texuring from the rt it will still be 
+                // the last bound texture, but it needs resolving. So keep this
+                // out of the "last != next" check.
+                resolveTextureRenderTarget(nextTexture);
 
-            if (fHWDrawState.fTexture != nextTexture) {
+                if (fHWDrawState.fTextures[s] != nextTexture) {
+                    setTextureUnit(s);
+                    GR_GL(BindTexture(GL_TEXTURE_2D, nextTexture->textureID()));
+                #if GR_COLLECT_STATS
+                    ++fStats.fTextureChngCnt;
+                #endif
+                    //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
+                    fHWDrawState.fTextures[s] = nextTexture;
+                }
+                
+                const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
+                const GrGLTexture::TexParams& oldTexParams = 
+                                                    nextTexture->getTexParams();
+                GrGLTexture::TexParams newTexParams;
+                
+                newTexParams.fFilter = sampler.isFilter() ? GL_LINEAR : 
+                                                            GL_NEAREST;
+                newTexParams.fWrapS = 
+                            GrGLTexture::gWrapMode2GLWrap[sampler.getWrapX()];
+                newTexParams.fWrapT = 
+                            GrGLTexture::gWrapMode2GLWrap[sampler.getWrapY()];
 
-                GR_GL(BindTexture(GL_TEXTURE_2D, nextTexture->textureID()));
-            #if GR_COLLECT_STATS
-                ++fStats.fTextureChngCnt;
-            #endif
-                //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
-                fHWDrawState.fTexture = nextTexture;
-            }
-
-            const GrGLTexture::TexParams& oldTexParams = nextTexture->getTexParams();
-            GrGLTexture::TexParams newTexParams;
-            newTexParams.fFilter = fCurrDrawState.fSamplerState.isFilter() ?
-                                                                GL_LINEAR :
-                                                                GL_NEAREST;
-            newTexParams.fWrapS = GrGLTexture::gWrapMode2GLWrap[fCurrDrawState.fSamplerState.getWrapX()];
-            newTexParams.fWrapT = GrGLTexture::gWrapMode2GLWrap[fCurrDrawState.fSamplerState.getWrapY()];
-
-            if (newTexParams.fFilter != oldTexParams.fFilter) {
-                GR_GL(TexParameteri(GL_TEXTURE_2D,
-                                    GL_TEXTURE_MAG_FILTER,
-                                    newTexParams.fFilter));
-                GR_GL(TexParameteri(GL_TEXTURE_2D,
-                                    GL_TEXTURE_MIN_FILTER,
-                                    newTexParams.fFilter));
-            }
-            if (newTexParams.fWrapS != oldTexParams.fWrapS) {
-                GR_GL(TexParameteri(GL_TEXTURE_2D,
-                                    GL_TEXTURE_WRAP_S,
-                                    newTexParams.fWrapS));
-            }
-            if (newTexParams.fWrapT != oldTexParams.fWrapT) {
-                GR_GL(TexParameteri(GL_TEXTURE_2D,
-                                    GL_TEXTURE_WRAP_T,
-                                    newTexParams.fWrapT));
-            }
-            nextTexture->setTexParams(newTexParams);
-        } else {
-            GrAssert(!"Rendering with texture vert flag set but no texture");
-            if (NULL != fHWDrawState.fTexture) {
-                GR_GL(BindTexture(GL_TEXTURE_2D, 0));
-                //            GrPrintf("---- bindtexture 0\n");
-            #if GR_COLLECT_STATS
-                ++fStats.fTextureChngCnt;
-            #endif
-                fHWDrawState.fTexture = NULL;
+                if (newTexParams.fFilter != oldTexParams.fFilter) {
+                    setTextureUnit(s);
+                    GR_GL(TexParameteri(GL_TEXTURE_2D, 
+                                        GL_TEXTURE_MAG_FILTER, 
+                                        newTexParams.fFilter));
+                    GR_GL(TexParameteri(GL_TEXTURE_2D, 
+                                        GL_TEXTURE_MIN_FILTER, 
+                                        newTexParams.fFilter));
+                }
+                if (newTexParams.fWrapS != oldTexParams.fWrapS) {
+                    setTextureUnit(s);
+                    GR_GL(TexParameteri(GL_TEXTURE_2D, 
+                                        GL_TEXTURE_WRAP_S,
+                                        newTexParams.fWrapS));
+                }
+                if (newTexParams.fWrapT != oldTexParams.fWrapT) {
+                    setTextureUnit(s);
+                    GR_GL(TexParameteri(GL_TEXTURE_2D, 
+                                        GL_TEXTURE_WRAP_T, 
+                                        newTexParams.fWrapT));
+                }
+                nextTexture->setTexParams(newTexParams);
+            } else {
+                GrAssert(!"Rendering with texture vert flag set but no texture");
+                if (NULL != fHWDrawState.fTextures[s]) {
+                    setTextureUnit(s);
+                    GR_GL(BindTexture(GL_TEXTURE_2D, 0));
+                    //            GrPrintf("---- bindtexture 0\n");
+                #if GR_COLLECT_STATS
+                    ++fStats.fTextureChngCnt;
+                #endif
+                    fHWDrawState.fTextures[s] = NULL;
+                }
             }
         }
     }
@@ -1502,7 +1538,7 @@
         }
     }
 
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     // ES doesn't support toggling GL_MULTISAMPLE and doesn't have
     // smooth lines.
     if (fRenderTargetChanged ||
@@ -1549,13 +1585,18 @@
             fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
         }
     }
-
+    
+#if GR_DEBUG
     // check for circular rendering
-    GrAssert(!usingTexture ||
-             NULL == fCurrDrawState.fRenderTarget ||
-             NULL == fCurrDrawState.fTexture ||
-             fCurrDrawState.fTexture->asRenderTarget() != fCurrDrawState.fRenderTarget);
-
+    for (int s = 0; s < kNumStages; ++s) {
+        GrAssert(!VertexUsesStage(s, fGeometrySrc.fVertexLayout) ||
+                 NULL == fCurrDrawState.fRenderTarget ||
+                 NULL == fCurrDrawState.fTextures[s] ||
+                 fCurrDrawState.fTextures[s]->asRenderTarget() != 
+                    fCurrDrawState.fRenderTarget);
+    }
+#endif
+    
     flushStencil();
 
     fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits;
@@ -1589,13 +1630,6 @@
     }
 }
 
-void GrGpuGL::notifyTextureBind(GrGLTexture* texture) {
-    fHWDrawState.fTexture = texture;
-#if GR_COLLECT_STATS
-    ++fStats.fTextureChngCnt;
-#endif
-}
-
 void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
     GrAssert(NULL != renderTarget);
 
@@ -1615,13 +1649,15 @@
 }
 
 void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
-    if (fCurrDrawState.fTexture == texture) {
-        fCurrDrawState.fTexture = NULL;
+    for (int s = 0; s < kNumStages; ++s) {
+        if (fCurrDrawState.fTextures[s] == texture) {
+            fCurrDrawState.fTextures[s] = NULL;
+        }
+        if (fHWDrawState.fTextures[s] == texture) {
+            // deleting bound texture does implied bind to 0
+            fHWDrawState.fTextures[s] = NULL;
+       }
     }
-    if (fHWDrawState.fTexture == texture) {
-        // deleting bound texture does implied bind to 0
-        fHWDrawState.fTexture = NULL;
-   }
 }
 
 void GrGpuGL::notifyTextureRemoveRenderTarget(GrGLTexture* texture) {
@@ -1672,6 +1708,21 @@
     return true;
 }
 
+void GrGpuGL::setTextureUnit(int unit) {
+    GrAssert(unit >= 0 && unit < kNumStages);
+    if (fActiveTextureUnitIdx != unit) {
+        GR_GL(ActiveTexture(GL_TEXTURE0 + unit));
+        fActiveTextureUnitIdx = unit;
+    }
+}
+              
+void GrGpuGL::setSpareTextureUnit() {
+    if (fActiveTextureUnitIdx != (GL_TEXTURE0 + SPARE_TEX_UNIT)) {
+        GR_GL(ActiveTexture(GL_TEXTURE0 + SPARE_TEX_UNIT));
+        fActiveTextureUnitIdx = SPARE_TEX_UNIT;
+    }
+}
+
 /* On ES the internalFormat and format must match for TexImage and we use
    GL_RGB, GL_RGBA for color formats. We also generally like having the driver
    decide the internalFormat. However, on ES internalFormat for
@@ -1688,8 +1739,8 @@
             } else {
                 return false;
             }
-#if GR_GL_ES // ES2 supports 565. ES1 supports it with FBO extension
-             // desktop GL has no such internal format
+#if GR_SUPPORT_GLES // ES2 supports 565. ES1 supports it with FBO extension
+                    // desktop GL has no such internal format
         case GrTexture::kRGB_565_PixelConfig:
             *format = GR_RGB565;
             return true;
@@ -1799,7 +1850,7 @@
 #else
     GLint major, minor;
     gl_version(&major, &minor);
-    #if GR_GL_DESKTOP
+    #if GR_SUPPORT_GLDESKTOP
     if (major >= 3) {// FBO, FBOMS, and FBOBLIT part of 3.0
         exts->GenFramebuffers                   = glGenFramebuffers;
         exts->BindFramebuffer                   = glBindFramebuffer;
@@ -1849,7 +1900,7 @@
     // we assume we have at least GL 1.5 or higher (VBOs introduced in 1.5)
     exts->MapBuffer     = glMapBuffer;
     exts->UnmapBuffer   = glUnmapBuffer;
-    #else // !GR_GL_DESKTOP
+    #else // !GR_SUPPORT_GLDESKTOP
     if (major >= 2) {// ES 2.0 supports FBO
         exts->GenFramebuffers                   = glGenFramebuffers;
         exts->BindFramebuffer                   = glBindFramebuffer;
@@ -1886,7 +1937,7 @@
         GET_PROC(exts, MapBuffer, OES);
         GET_PROC(exts, UnmapBuffer, OES);
     }
-    #endif // !GR_GL_DESKTOP
+    #endif // !GR_SUPPORT_GLDESKTOP
 #endif // BUILD
 }
 
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index 728a5d3..8c2cd80 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -87,6 +87,8 @@
 
     void eraseStencil(uint32_t value, uint32_t mask);
     virtual void eraseStencilClip();
+    
+    void setTextureUnit(int unitIdx);
 
     // flushes state that is common to fixed and programmable GL
     // dither
@@ -109,9 +111,9 @@
     // last scissor / viewport scissor state seen by the GL.
     BoundsState fHWBounds;
 
-private:
     GrGLExts fExts;
 
+private:
     GrGLRenderTarget* fDefaultRenderTarget;
 
     void resetContextHelper();
@@ -122,10 +124,11 @@
     void notifyVertexBufferDelete(const GrGLVertexBuffer* buffer);
     void notifyIndexBufferBind(const GrGLIndexBuffer* buffer);
     void notifyIndexBufferDelete(const GrGLIndexBuffer* buffer);
-    void notifyTextureBind(GrGLTexture* texture);
     void notifyTextureDelete(GrGLTexture* texture);
     void notifyRenderTargetDelete(GrRenderTarget* renderTarget);
     void notifyTextureRemoveRenderTarget(GrGLTexture* texture);
+    
+    void setSpareTextureUnit();
 
     void flushRenderTarget();
     void flushStencil();
@@ -157,7 +160,9 @@
 
     // ES requires an extension to support RGBA8 in RenderBufferStorage
     bool fRGBA8Renderbuffer;
-
+    
+    int fActiveTextureUnitIdx;
+    
     typedef GrGpu INHERITED;
 };
 
@@ -168,7 +173,7 @@
  *  GrGL_RestoreResetRowLength() will reset GL_UNPACK_ROW_LENGTH to 0. We write
  *  this wrapper, since GL_UNPACK_ROW_LENGTH is not available on all GL versions
  */
-#if GR_GL_DESKTOP
+#if GR_SUPPORT_GLDESKTOP
     static inline void GrGL_RestoreResetRowLength() {
         GR_GL(PixelStorei(GL_UNPACK_ROW_LENGTH, 0));
     }
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index 77bec40..d142b66 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -72,25 +72,27 @@
 void GrGpuGLFixed::resetContextHelper() {
     GR_GL(Disable(GL_TEXTURE_2D));
 
-    GR_GL(EnableClientState(GL_VERTEX_ARRAY));    
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,   GL_MODULATE));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,      GL_TEXTURE0));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,      GL_PRIMARY_COLOR));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB,  GL_SRC_COLOR));
+    for (int s = 0; s < kNumStages; ++s) {
+        setTextureUnit(s);
+        GR_GL(EnableClientState(GL_VERTEX_ARRAY));    
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,   GL_MODULATE));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,      GL_TEXTURE0+s));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,      GL_PREVIOUS));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB,  GL_SRC_COLOR));
 
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR));
-    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA));
-    
-    // this changes between GL_SRC_COLR and GL_SRC_ALPHA depending upon
-    // whether we have a (premultiplied) RGBA texture or just an ALPHA texture
-    //glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB,  GL_SRC_COLOR);
-    fHWRGBOperand0 = (TextureEnvRGBOperands) -1;    
-
-    GR_GL(ClientActiveTexture(GL_TEXTURE0));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0+s));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS));
+        GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA));
+        
+        // color oprand0 changes between GL_SRC_COLR and GL_SRC_ALPHA depending 
+        // upon whether we have a (premultiplied) RGBA texture or just an ALPHA 
+        // texture, e.g.:
+        //glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB,  GL_SRC_COLOR);
+        fHWRGBOperand0[s] = (TextureEnvRGBOperands) -1;    
+    }
 
     fHWGeometryState.fVertexLayout = 0;
     fHWGeometryState.fPositionPtr  = (void*) ~0;
@@ -127,11 +129,15 @@
 
 bool GrGpuGLFixed::flushGraphicsState(PrimitiveType type) {
     
-    bool usingTexture = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
+    bool usingTextures[kNumStages];
+    
+    for (int s = 0; s < kNumStages; ++s) {
+        usingTextures[s] = VertexUsesStage(s, fGeometrySrc.fVertexLayout);
 
-    if (usingTexture && fCurrDrawState.fSamplerState.isGradient()) {
-        unimpl("Fixed pipe doesn't support radial/sweep gradients");
-        return false;
+        if (usingTextures[s] && fCurrDrawState.fSamplerStates[s].isGradient()) {
+            unimpl("Fixed pipe doesn't support radial/sweep gradients");
+            return false;
+        }
     }
     
     flushGLStateCommon(type);
@@ -140,24 +146,25 @@
         flushProjectionMatrix();
         fRenderTargetChanged = false;
     }
-
-    bool wasUsingTexture = VertexHasTexCoords(fHWGeometryState.fVertexLayout);
-    if (usingTexture != wasUsingTexture) {
-        if (usingTexture) {
-            GR_GL(Enable(GL_TEXTURE_2D));
-        } else {
-            GR_GL(Disable(GL_TEXTURE_2D));
+    
+    for (int s = 0; s < kNumStages; ++s) {
+        bool wasUsingTexture = VertexUsesStage(s, fHWGeometryState.fVertexLayout);
+        if (usingTextures[s] != wasUsingTexture) {
+            setTextureUnit(s);
+            if (usingTextures[s]) {
+                GR_GL(Enable(GL_TEXTURE_2D));
+            } else {
+                GR_GL(Disable(GL_TEXTURE_2D));
+            }
         }
     }
-    
+        
     uint32_t vertColor = (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit);
     uint32_t prevVertColor = (fHWGeometryState.fVertexLayout & 
                               kColor_VertexLayoutBit);
     
     if (vertColor != prevVertColor) {
         if (vertColor) {
-            GrAssert(fCurrDrawState.fSamplerState.getSampleMode() != 
-                     GrSamplerState::kAlphaMod_SampleMode);
             GR_GL(ShadeModel(GL_SMOOTH));
             // invalidate the immediate mode color
             fHWDrawState.fColor = GrColor_ILLEGAL;
@@ -181,57 +188,61 @@
     }
 
     // set texture environment, decide whether we are modulating by RGB or A.
-    if (usingTexture) {
-        GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTexture;
-        if (NULL != texture) {
-            TextureEnvRGBOperands nextRGBOperand0 = 
-                (texture->uploadFormat() == GL_ALPHA) ? 
-                    kAlpha_TextureEnvRGBOperand : 
-                    kColor_TextureEnvRGBOperand;
-            if (fHWRGBOperand0 != nextRGBOperand0) {
-                GR_GL(TexEnvi(GL_TEXTURE_ENV, 
-                              GL_OPERAND0_RGB,
-                              (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ? 
-                                GL_SRC_ALPHA : 
-                                GL_SRC_COLOR));
-                fHWRGBOperand0 = nextRGBOperand0;
-            }
-            
-            if (fHWTextureOrientation != texture->orientation() ||
-                fHWDrawState.fMatrixModeCache[kTexture_MatrixMode] != 
-                fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]) {
-                GrGpuMatrix glm;
-                if (GrGLTexture::kBottomUp_Orientation == texture->orientation()) {
-                    GrMatrix m(
-                        GR_Scalar1, 0, 0,
-                        0, -GR_Scalar1, GR_Scalar1,
-                        0, 0, GrMatrix::I()[8]
-                    );
-                    m.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
-                    glm.set(m);
-                } else {
-                    glm.set(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+    for (int s = 0; s < kNumStages; ++s) {
+        if (usingTextures[s]) {
+            GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTextures[s];
+            if (NULL != texture) {
+                TextureEnvRGBOperands nextRGBOperand0 = 
+                    (texture->config() == GrTexture::kAlpha_8_PixelConfig) ? 
+                        kAlpha_TextureEnvRGBOperand : 
+                        kColor_TextureEnvRGBOperand;
+                if (fHWRGBOperand0[s] != nextRGBOperand0) {
+                    setTextureUnit(s);
+                    GR_GL(TexEnvi(GL_TEXTURE_ENV, 
+                                  GL_OPERAND0_RGB,
+                                  (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ? 
+                                    GL_SRC_ALPHA : 
+                                    GL_SRC_COLOR));
+                    fHWRGBOperand0[s] = nextRGBOperand0;
                 }
-                GR_GL(MatrixMode(gMatrixMode2Enum[kTexture_MatrixMode]));
-                GR_GL(LoadMatrixf(glm.fMat));
-                fHWDrawState.fMatrixModeCache[kTexture_MatrixMode] = 
-                    fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
-                fHWTextureOrientation = texture->orientation();
+                
+                if (fHWTextureOrientation != texture->orientation() ||
+                    fHWDrawState.fTextureMatrices[s] != 
+                    fCurrDrawState.fTextureMatrices[s]) {
+                    GrGpuMatrix glm;
+                    if (GrGLTexture::kBottomUp_Orientation == 
+                        texture->orientation()) {
+                        GrMatrix m(
+                            GR_Scalar1, 0, 0,
+                            0, -GR_Scalar1, GR_Scalar1,
+                            0, 0, GrMatrix::I()[8]
+                        );
+                        m.preConcat(fCurrDrawState.fTextureMatrices[s]);
+                        glm.set(m);
+                    } else {
+                        glm.set(fCurrDrawState.fTextureMatrices[s]);
+                    }
+                    setTextureUnit(s);
+                    GR_GL(MatrixMode(GL_TEXTURE));
+                    GR_GL(LoadMatrixf(glm.fMat));
+                    fHWDrawState.fTextureMatrices[s] = 
+                                            fCurrDrawState.fTextureMatrices[s];
+                    fHWTextureOrientation = texture->orientation();
+                }
+            } else {
+                GrAssert(!"Rendering with texture vert flag set but no bound texture");
+                return false;
             }
-        } else {
-            GrAssert(!"Rendering with texture vert flag set but no bound texture");
-            return false;
         }
     }
 
-    if (fHWDrawState.fMatrixModeCache[kModelView_MatrixMode] != 
-        fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+    if (fHWDrawState.fViewMatrix != fCurrDrawState.fViewMatrix) {
         GrGpuMatrix glm;
-        glm.set(fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
-        GR_GL(MatrixMode(gMatrixMode2Enum[kModelView_MatrixMode]));
+        glm.set(fCurrDrawState.fViewMatrix);
+        GR_GL(MatrixMode(GL_MODELVIEW));
         GR_GL(LoadMatrixf(glm.fMat));
-        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode] = 
-            fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+        fHWDrawState.fViewMatrix = 
+        fCurrDrawState.fViewMatrix;
     }
     return true;
 }
@@ -241,15 +252,17 @@
                                  uint32_t vertexCount,
                                  uint32_t indexCount) {
     
-    int newColorOffset, newTexCoordOffset;
+    int newColorOffset;
+    int newTexCoordOffsets[kNumStages];
     
-    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
-                                             &newTexCoordOffset, 
-                                             &newColorOffset);
-    int oldColorOffset, oldTexCoordOffset;
-    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
-                                             &oldTexCoordOffset, 
-                                             &oldColorOffset);
+    GLsizei newStride = VertexSizeAndOffsetsByStage(fGeometrySrc.fVertexLayout,
+                                                    newTexCoordOffsets, 
+                                                    &newColorOffset);
+    int oldColorOffset;
+    int oldTexCoordOffsets[kNumStages];
+    GLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout,
+                                                    oldTexCoordOffsets, 
+                                                    &oldColorOffset);
     
     const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
     
@@ -258,7 +271,7 @@
         GrAssert(!fGeometrySrc.fVertexBuffer->isLocked());
         if (fHWGeometryState.fVertexBuffer != fGeometrySrc.fVertexBuffer) {
             GrGLVertexBuffer* buf = 
-            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
+                            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
             GR_GL(BindBuffer(GL_ARRAY_BUFFER, buf->bufferID()));
             fHWGeometryState.fVertexBuffer = fGeometrySrc.fVertexBuffer;
         }
@@ -310,17 +323,23 @@
         fHWGeometryState.fPositionPtr = posPtr;
     }
     
-    // need to enable array if tex coord offset is 0 (using positions as coords)
-    if (newTexCoordOffset >= 0) {
-        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
-        if (oldTexCoordOffset < 0) {
-            GR_GL(EnableClientState(GL_TEXTURE_COORD_ARRAY));
+    for (int s = 0; s < kNumStages; ++s) {
+        // need to enable array if tex coord offset is 0 
+        // (using positions as coords)
+        if (newTexCoordOffsets[s] >= 0) {
+            GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffsets[s];
+            if (oldTexCoordOffsets[s] < 0) {
+                GR_GL(ClientActiveTexture(GL_TEXTURE0+s));
+                GR_GL(EnableClientState(GL_TEXTURE_COORD_ARRAY));
+            }
+            if (posChange || newTexCoordOffsets[s] != oldTexCoordOffsets[s]) {
+                GR_GL(ClientActiveTexture(GL_TEXTURE0+s));
+                GR_GL(TexCoordPointer(2, scalarType, newStride, texCoordPtr));
+            }
+        } else if (oldTexCoordOffsets[s] >= 0) {
+            GR_GL(ClientActiveTexture(GL_TEXTURE0+s));
+            GR_GL(DisableClientState(GL_TEXTURE_COORD_ARRAY));
         }
-        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
-            GR_GL(TexCoordPointer(2, scalarType, newStride, texCoordPtr));
-        }
-    } else if (oldTexCoordOffset >= 0) {
-        GR_GL(DisableClientState(GL_TEXTURE_COORD_ARRAY));
     }
     
     if (newColorOffset > 0) {
diff --git a/gpu/src/GrGpuGLFixed.h b/gpu/src/GrGpuGLFixed.h
index f3a0332..85db830 100644
--- a/gpu/src/GrGpuGLFixed.h
+++ b/gpu/src/GrGpuGLFixed.h
@@ -40,15 +40,15 @@
     void resetContextHelper();
 
     // when the texture is GL_RGBA we set the GL_COMBINE texture
-    // environment rgb operand 0 to be GL_COLOR to modulate each incoming frag's
-    // RGB by the texture's RGB. When the texture is GL_ALPHA we set
-    // the operand to GL_ALPHA so that the incoming frag's RGB is modulated
-    // by the texture's alpha.
+    // environment rgb operand 0 to be GL_COLOR to modulate each incoming
+    // R,G, & B by the texture's R, G, & B. When the texture is alpha-only we 
+    // set the operand to GL_ALPHA so that the incoming frag's R, G, &B are all 
+    // modulated by the texture's A.
     enum TextureEnvRGBOperands {
         kAlpha_TextureEnvRGBOperand,
         kColor_TextureEnvRGBOperand,
     };
-    TextureEnvRGBOperands fHWRGBOperand0;
+    TextureEnvRGBOperands fHWRGBOperand0[kNumStages];
 
     void flushProjectionMatrix();
 
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index b9db90c..2ace2ad 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -512,8 +512,9 @@
                       &fPrograms[p]);
         GR_DEBUGASSERT(result);
 
-        for (int m = 0; m < kMatrixModeCount; ++m) {
-            fPrograms[p].fMatrixModeCache[m].setScale(GR_ScalarMax,
+        fPrograms[p].fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax);
+        for (int s = 0; s < kNumStages; ++s) {
+            fPrograms[p].fTextureMatrices[s].setScale(GR_ScalarMax, 
                                                       GR_ScalarMax); // illegal
         };
         fPrograms[p].fColor = GrColor_ILLEGAL;
@@ -559,7 +560,7 @@
         GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
         0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
         0, 0, GrMatrix::I()[8]);
-    m.setConcat(m, fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+    m.setConcat(m, fCurrDrawState.fViewMatrix);
 
     // ES doesn't allow you to pass true to the transpose param,
     // so do our own transpose
@@ -593,11 +594,11 @@
             0, -GR_Scalar1, GR_Scalar1,
             0, 0, GrMatrix::I()[8]
         );
-        temp.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+        temp.preConcat(fCurrDrawState.fTextureMatrices[0]);
         m = &temp;
     } else {
         GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
-        m = &fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+        m = &fCurrDrawState.fTextureMatrices[0];
     }
 
     // ES doesn't allow you to pass true to the transpose param,
@@ -644,74 +645,77 @@
 
     Programs nextProgram = kNoTexture_Program;
 
-    if (!VertexHasTexCoords(fGeometrySrc.fVertexLayout)) {
+    GrTexture* texture = fCurrDrawState.fTextures[0];
+    bool posAsTex = 
+            StagePosAsTexCoordVertexLayoutBit(0) & fGeometrySrc.fVertexLayout;
+
+    if (!VertexUsesStage(0, fGeometrySrc.fVertexLayout)) {
         goto HAVE_NEXT_PROGRAM;
     }
 
-    GrAssert(fCurrDrawState.fTexture);
+    GrAssert(NULL != texture);
 
-    switch (fCurrDrawState.fSamplerState.getSampleMode()) {
+    switch (fCurrDrawState.fSamplerStates[0].getSampleMode()) {
         case GrSamplerState::kRadial_SampleMode:
-            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
-            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+            GrAssert(!fCurrDrawState.fTextureMatrices[0].hasPerspective());
+            GrAssert(GrTexture::kAlpha_8_PixelConfig != texture->config());
+            if (posAsTex) {
                 nextProgram = kRadialTextureVertCoords_Program;
             } else {
                 nextProgram = kRadialTextureTexCoords_Program;
             }
             break;
         case GrSamplerState::kSweep_SampleMode:
-            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
-            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+            GrAssert(!fCurrDrawState.fTextureMatrices[0].hasPerspective());
+            GrAssert(GrTexture::kAlpha_8_PixelConfig != texture->config());
+            if (posAsTex) {
                 nextProgram = kSweepTextureVertCoords_Program;
             } else {
                 nextProgram = kSweepTextureTexCoords_Program;
             }
             break;
         case GrSamplerState::kRadial2_SampleMode:
-            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
-            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+            GrAssert(!fCurrDrawState.fTextureMatrices[0].hasPerspective());
+            GrAssert(GrTexture::kAlpha_8_PixelConfig != texture->config());
+            if (posAsTex) {
                 nextProgram = kTwoPointRadialTextureVertCoords_Program;
             } else {
                 nextProgram = kTwoPointRadialTextureTexCoords_Program;
             }
             break;
-        case GrSamplerState::kAlphaMod_SampleMode:
-            GrAssert(((GrGLTexture*)fCurrDrawState.fTexture)->orientation() ==
-                     GrGLTexture::kTopDown_Orientation);
-            (((GrGLTexture*)fCurrDrawState.fTexture)->uploadFormat() == GL_ALPHA);
-
-            nextProgram = kText_Program;
-            break;
-        case GrSamplerState::kNormal_SampleMode: {
-            GR_DEBUGCODE(GrGLTexture* tex = (GrGLTexture*)fCurrDrawState.fTexture;)
-            GrAssert(tex);
-
-            bool persp = fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective();
-
-            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
-                nextProgram = persp ? kTextureVertCoordsProj_Program :
-                                      kTextureVertCoords_Program;
+        case GrSamplerState::kNormal_SampleMode:
+            if (GrTexture::kAlpha_8_PixelConfig == texture->config()) {
+                GrAssert(((GrGLTexture*)texture)->orientation() == 
+                         GrGLTexture::kTopDown_Orientation);
+                GrAssert(!posAsTex);
+                nextProgram = kText_Program;
             } else {
-                nextProgram = persp ? kTextureTexCoordsProj_Program :
-                                      kTextureTexCoords_Program;
-            }
-            // check for case when frag shader can skip the color modulation
-            if (!persp && !(fGeometrySrc.fVertexLayout
-                            & kColor_VertexLayoutBit) &&
-                0xffffffff == fCurrDrawState.fColor) {
-                switch (nextProgram) {
-                case kTextureVertCoords_Program:
-                    nextProgram = kTextureVertCoordsNoColor_Program;
-                    break;
-                case kTextureTexCoords_Program:
-                    nextProgram = kTextureTexCoordsNoColor_Program;
-                    break;
-                default:
-                    GrAssert("Unexpected");
-                    break;
+                bool persp = fCurrDrawState.fTextureMatrices[0].hasPerspective();
+                if (posAsTex) {
+                    nextProgram = persp ? kTextureVertCoordsProj_Program : 
+                                          kTextureVertCoords_Program;
+                } else {
+                    nextProgram = persp ? kTextureTexCoordsProj_Program : 
+                                          kTextureTexCoords_Program;
+                }
+                // check for case when frag shader can skip the color modulation
+                if (!persp && !(fGeometrySrc.fVertexLayout
+                                & kColor_VertexLayoutBit) &&
+                    0xffffffff == fCurrDrawState.fColor) {
+                    switch (nextProgram) {
+                    case kTextureVertCoords_Program:
+                        nextProgram = kTextureVertCoordsNoColor_Program;
+                        break;
+                    case kTextureTexCoords_Program:
+                        nextProgram = kTextureTexCoordsNoColor_Program;
+                        break;
+                    default:
+                        GrAssert("Unexpected");
+                        break;
+                    }
                 }
             }
-        } break;
+            break;
         default:
             GrAssert(!"Unknown samplemode");
             break;
@@ -729,20 +733,26 @@
 
 bool GrGpuGLShaders::flushGraphicsState(PrimitiveType type) {
 
+   for (int s = 1; s < kNumStages; ++s) {
+        if (VertexUsesStage(s, fGeometrySrc.fVertexLayout)) {
+            unimpl("the hard-coded shaders used by this " 
+                   "class only support 1 stage");
+            return false;
+        }
+    }
+
     flushGLStateCommon(type);
 
     if (fRenderTargetChanged) {
         // our coords are in pixel space and the GL matrices map to NDC
         // so if the viewport changed, our matrix is now wrong.
 #if ATTRIBUTE_MATRIX
-        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax,
-                                                                      GR_ScalarMax);
+        fHWDrawState.fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax);
 #else
         // we assume all shader matrices may be wrong after viewport changes
         for (int p = 0; p < kProgramCount; ++p) {
             // set to illegal matrix
-            fPrograms[p].fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax,
-                                                                          GR_ScalarMax);
+            fPrograms[p].fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax); 
         }
 #endif
         fRenderTargetChanged = false;
@@ -783,36 +793,35 @@
     }
 
 #if ATTRIBUTE_MATRIX
-    GrMatrix* currentMats = fHWDrawState.fMatrixModeCache;
+    GrMatrix& currentViewMatrix = fHWDrawState.fViewMatrix;
+    GrMatrix& currentTexMatrix = fHWDrawState.fTextureMatrices[0];
     GrGLTexture::Orientation& orientation = fTextureOrientation;
 #else
-    GrMatrix* currentMats = fPrograms[fHWProgram].fMatrixModeCache;
+    GrMatrix& currentViewMatrix = fPrograms[fHWProgram].fViewMatrix;
+    GrMatrix& currentTexMatrix = fPrograms[fHWProgram].fTextureMatrices[0];   
     GrGLTexture::Orientation& orientation =
                                     fPrograms[fHWProgram].fTextureOrientation;
 #endif
 
-    if (currentMats[kModelView_MatrixMode] !=
-          fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+    if (currentViewMatrix !=
+          fCurrDrawState.fViewMatrix) {
         flushMatrix(fPrograms[fHWProgram].fMatrixLocation);
-        currentMats[kModelView_MatrixMode] =
-            fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+        currentViewMatrix = fCurrDrawState.fViewMatrix;
     }
 
-    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
+    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[0];
     if (NULL != texture) {
         if (-1 != fPrograms[fHWProgram].fTexMatrixLocation &&
-            (currentMats[kTexture_MatrixMode] !=
-             fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode] ||
+            (currentTexMatrix != fCurrDrawState.fTextureMatrices[0] ||
              orientation != texture->orientation())) {
             flushTexMatrix(fPrograms[fHWProgram].fTexMatrixLocation,
                            texture->orientation());
-            currentMats[kTexture_MatrixMode] =
-                        fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+            currentTexMatrix = fCurrDrawState.fTextureMatrices[0];
             orientation = texture->orientation();
         }
     }
 
-    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
+    const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[0];
     if (-1 != fPrograms[fHWProgram].fTwoPointParamsLocation &&
         (fPrograms[fHWProgram].fRadial2CenterX1 != sampler.getRadial2CenterX1() ||
          fPrograms[fHWProgram].fRadial2Radius0  != sampler.getRadial2Radius0()  ||
@@ -833,15 +842,17 @@
                                    uint32_t vertexCount,
                                    uint32_t indexCount) {
 
-    int newColorOffset, newTexCoordOffset;
+    int newColorOffset;
+    int newTexCoordOffsets[kNumStages];
 
-    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
-                                             &newTexCoordOffset,
-                                             &newColorOffset);
-    int oldColorOffset, oldTexCoordOffset;
-    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
-                                             &oldTexCoordOffset,
-                                             &oldColorOffset);
+    GLsizei newStride = VertexSizeAndOffsetsByStage(fGeometrySrc.fVertexLayout,
+                                                    newTexCoordOffsets, 
+                                                    &newColorOffset);
+    int oldColorOffset;
+    int oldTexCoordOffsets[kNumStages];
+    GLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout,
+                                                    oldTexCoordOffsets, 
+                                                    &oldColorOffset);
 
     const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
 
@@ -906,16 +917,17 @@
         fHWGeometryState.fPositionPtr = posPtr;
     }
 
-    if (newTexCoordOffset > 0) {
-        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
-        if (oldTexCoordOffset <= 0) {
+    // this class only supports one stage.
+    if (newTexCoordOffsets[0] > 0) {
+        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffsets[0];
+        if (oldTexCoordOffsets[0] <= 0) {
             GR_GL(EnableVertexAttribArray(GR_GL_TEX_ATTR_LOCATION));
         }
-        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
+        if (posChange || newTexCoordOffsets[0] != oldTexCoordOffsets[0]) {
             GR_GL(VertexAttribPointer(GR_GL_TEX_ATTR_LOCATION, 2, scalarType,
                                       texCoordNorm, newStride, texCoordPtr));
         }
-    } else if (oldTexCoordOffset > 0) {
+    } else if (oldTexCoordOffsets[0] > 0) {
         GR_GL(DisableVertexAttribArray(GR_GL_TEX_ATTR_LOCATION));
     }
 
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
index cba387b..e5a5665 100644
--- a/gpu/src/GrGpuGLShaders.h
+++ b/gpu/src/GrGpuGLShaders.h
@@ -134,7 +134,8 @@
 
         // these reflect the current values of uniforms
         // (GL uniform values travel with program)
-        GrMatrix                    fMatrixModeCache[kMatrixModeCount];
+        GrMatrix                    fViewMatrix;
+        GrMatrix                    fTextureMatrices[kNumStages];
         GrColor                     fColor;
         GrGLTexture::Orientation    fTextureOrientation;
         GrScalar                    fRadial2CenterX1;
diff --git a/gpu/src/GrGpuGLShaders2.cpp b/gpu/src/GrGpuGLShaders2.cpp
index c7b3cb9..92df809 100644
--- a/gpu/src/GrGpuGLShaders2.cpp
+++ b/gpu/src/GrGpuGLShaders2.cpp
@@ -41,17 +41,16 @@
     const char GR_SHADER_PRECISION[] =  "";
 #endif
 
+
 #define POS_ATTR_LOCATION 0
-#define TEX_ATTR_LOCATION 1
-#define COL_ATTR_LOCATION 2
+#define TEX_ATTR_LOCATION(X) (1 + X)
+#define COL_ATTR_LOCATION (2 + GrDrawTarget::kMaxTexCoords)
 #if ATTRIBUTE_MATRIX
-#define VIEWMAT_ATTR_LOCATION 3
-#define TEXMAT_ATTR_LOCATION(X) (6 + 3 * (X))
+#define VIEWMAT_ATTR_LOCATION (3 + GrDrawTarget::kMaxTexCoords)
+#define TEXMAT_ATTR_LOCATION(X) (6 + GrDrawTarget::kMaxTexCoords + 3 * (X))
 #define BOGUS_MATRIX_UNI_LOCATION 1000
 #endif
 
-const int GrGpuGLShaders2::NUM_STAGES = 1;
-
 struct GrGpuGLShaders2::StageUniLocations {
     GLint fTextureMatrixUni;
     GLint fSamplerUni;
@@ -60,7 +59,7 @@
 
 struct GrGpuGLShaders2::UniLocations {
     GLint fViewMatrixUni;
-    StageUniLocations fStages[NUM_STAGES];
+    StageUniLocations fStages[kNumStages];
 };
 
 // Records per-program information
@@ -82,11 +81,11 @@
     // these reflect the current values of uniforms
     // (GL uniform values travel with program)
     GrMatrix                    fViewMatrix;
-    GrMatrix                    fTextureMatrix[NUM_STAGES];
-    GrGLTexture::Orientation    fTextureOrientation[NUM_STAGES];
-    GrScalar                    fRadial2CenterX1[NUM_STAGES];
-    GrScalar                    fRadial2Radius0[NUM_STAGES];
-    bool                        fRadial2PosRoot[NUM_STAGES];
+    GrMatrix                    fTextureMatrix[kNumStages];
+    GrGLTexture::Orientation    fTextureOrientation[kNumStages];
+    GrScalar                    fRadial2CenterX1[kNumStages];
+    GrScalar                    fRadial2Radius0[kNumStages];
+    bool                        fRadial2PosRoot[kNumStages];
 
 };
 
@@ -127,12 +126,12 @@
     // for this to pack
     unsigned short fOptFlags : 16;
 
-    StageDesc fStages[NUM_STAGES];
+    StageDesc fStages[kNumStages];
 
     bool operator == (const ProgramDesc& desc) const {
         // keep 4-byte aligned and tightly packed
         GR_STATIC_ASSERT(4 == sizeof(StageDesc));
-        GR_STATIC_ASSERT(2 + 2 + 4 * NUM_STAGES == sizeof(ProgramDesc));
+        GR_STATIC_ASSERT(2 + 2 + 4 * kNumStages == sizeof(ProgramDesc));
         return 0 == memcmp(this, &desc, sizeof(ProgramDesc));
     }
 };
@@ -237,7 +236,6 @@
     return (x >> 8) | (x << 24);
 }
 
-
 GrGpuGLShaders2::ProgramCache::HashKey::HashKey(const ProgramDesc& desc) {
     fDesc = desc;
     // if you change the size of the desc, need to update the hash function
@@ -289,7 +287,11 @@
 
 #define POS_ATTR_NAME "aPosition"
 #define COL_ATTR_NAME "aColor"
-#define TEX_ATTR_NAME "aTexture"
+
+static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
+    *s = "aTexCoord";
+    s->appendInt(coordIdx);
+}
 
 static inline const char* float_vector_type(int count) {
     static const char* FLOAT_VECS[] = {"ERROR", "float", "vec2", "vec3", "vec4"};
@@ -347,14 +349,6 @@
 #include "GrRandom.h"
 
 void GrGpuGLShaders2::ProgramUnitTest() {
-    static const uint16_t VFORMATS[] = {
-        0,
-        kSeparateTexCoord_VertexLayoutBit,
-        kPositionAsTexCoord_VertexLayoutBit,
-        kSeparateTexCoord_VertexLayoutBit | kColor_VertexLayoutBit,
-        kPositionAsTexCoord_VertexLayoutBit | kColor_VertexLayoutBit,
-        kTextFormat_VertexLayoutBit
-    };
     static const int PROG_OPTS[] = {
         0,
         ProgramDesc::kNotPoints_OptFlagBit,
@@ -379,19 +373,35 @@
     ProgramDesc pdesc;
     memset(&pdesc, 0, sizeof(pdesc));
 
-    static const int NUM_TESTS = 1024;
+    static const int NUM_TESTS = 512;
 
     // GrRandoms nextU() values have patterns in the low bits
     // So using nextU() % array_count might never take some values.
     GrRandom random;
     for (int t = 0; t < NUM_TESTS; ++t) {
-        int x = (int)(random.nextF() * GR_ARRAY_COUNT(VFORMATS));
-        pdesc.fVertexLayout = VFORMATS[x];
-        x = (int)(random.nextF() * GR_ARRAY_COUNT(PROG_OPTS));
+        
+        pdesc.fVertexLayout = 0;
+        for (int s = 0; s < kNumStages; ++s) {
+            // enable the stage?
+            if (random.nextF() > .5f) {
+                // use separate tex coords?
+                if (random.nextF() > .5f) {
+                    int t = (int)(random.nextF() * kMaxTexCoords);
+                    pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
+                } else {
+                    pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
+                }
+            }
+            // use text-formatted verts?
+            if (random.nextF() > .5f) {
+                pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
+            }
+        }
+
+        int x = (int)(random.nextF() * GR_ARRAY_COUNT(PROG_OPTS));
         pdesc.fOptFlags = PROG_OPTS[x];
-        for (int s = 0; s < NUM_STAGES; ++s) {
-            x = (int)(random.nextF() * 2.f);
-            pdesc.fStages[s].fEnabled = x;
+        for (int s = 0; s < kNumStages; ++s) {
+            pdesc.fStages[s].fEnabled = VertexUsesStage(s, pdesc.fVertexLayout);
             x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
             pdesc.fStages[s].fOptFlags = STAGE_OPTS[x];
             x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
@@ -696,30 +706,46 @@
     }
     segments.fFSCode   = "void main() {\n";
 
-    bool textureCoordAttr = false;
-    static const char* IN_COORDS[] = {POS_ATTR_NAME, TEX_ATTR_NAME};
-    const char* inCoords = NULL;
-    if ((kSeparateTexCoord_VertexLayoutBit | kTextFormat_VertexLayoutBit) &
-        layout) {
-        segments.fVSAttrs += "attribute vec2 " TEX_ATTR_NAME ";\n";
-        inCoords = IN_COORDS[1];
-        textureCoordAttr = true;
-    } else if (kPositionAsTexCoord_VertexLayoutBit & layout) {
-        inCoords = IN_COORDS[0];
+    // add texture coordinates that are used to the list of vertex attr decls
+    GrTokenString texCoordAttrs[kMaxTexCoords];
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (VertexUsesTexCoordIdx(t, layout)) {
+            tex_attr_name(t, texCoordAttrs + t);
+
+            segments.fVSAttrs += "attribute vec2 ";
+            segments.fVSAttrs += texCoordAttrs[t];
+            segments.fVSAttrs += ";\n";
+        }
     }
 
-    GrTokenString inColor = "vColor";
-    GR_STATIC_ASSERT(NUM_STAGES <= 9);
+    // for each enabled stage figure out what the input coordinates are
+    // and count the number of stages in use.
+    const char* stageInCoords[kNumStages];
     int numActiveStages = 0;
-    for (int i = 0; i < NUM_STAGES; ++i) {
-        if (desc.fStages[i].fEnabled) {
+
+    for (int s = 0; s < kNumStages; ++s) {
+        if (desc.fStages[s].fEnabled) {
+            if (StagePosAsTexCoordVertexLayoutBit(s) & layout) {
+                stageInCoords[s] = POS_ATTR_NAME;
+            } else {
+                int tcIdx = VertexTexCoordsForStage(s, layout);
+                 // we better have input tex coordinates if stage is enabled.
+                GrAssert(tcIdx >= 0);
+                GrAssert(texCoordAttrs[tcIdx].length());
+                stageInCoords[s] = texCoordAttrs[tcIdx].cstr();
+            }
             ++numActiveStages;
         }
     }
-    if (NULL != inCoords && numActiveStages) {
+    
+    GrTokenString inColor = "vColor";
+
+    // if we have active stages string them together, feeding the output color
+    // of each to the next and generating code for each stage.
+    if (numActiveStages) {
         int currActiveStage = 0;
-        for (int i = 0; i < NUM_STAGES; ++i) {
-            if (desc.fStages[i].fEnabled) {
+        for (int s = 0; s < kNumStages; ++s) {
+            if (desc.fStages[s].fEnabled) {
                 GrTokenString outColor;
                 if (currActiveStage < (numActiveStages - 1)) {
                     outColor = "color";
@@ -730,13 +756,13 @@
                 } else {
                     outColor = "gl_FragColor";
                 }
-                GenStageCode(i,
-                             desc.fStages[i],
+                GenStageCode(s,
+                             desc.fStages[s],
                              haveColor ? inColor.cstr() : NULL,
                              outColor.cstr(),
-                             inCoords,
+                             stageInCoords[s],
                              &segments,
-                             &program->fUniLocations.fStages[i]);
+                             &program->fUniLocations.fStages[s]);
                 ++currActiveStage;
                 inColor = outColor;
                 haveColor = true;
@@ -835,8 +861,12 @@
 
     // Bind the attrib locations to same values for all shaders
     GR_GL(BindAttribLocation(progID, POS_ATTR_LOCATION, POS_ATTR_NAME));
-    if (textureCoordAttr) {
-        GR_GL(BindAttribLocation(progID, TEX_ATTR_LOCATION, TEX_ATTR_NAME));
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (texCoordAttrs[t].length()) {
+            GR_GL(BindAttribLocation(progID, 
+                                     TEX_ATTR_LOCATION(t), 
+                                     texCoordAttrs[t].cstr()));
+        }
     }
 
 #if ATTRIBUTE_MATRIX
@@ -848,12 +878,12 @@
 
     program->fUniLocations.fViewMatrixUni = BOGUS_MATRIX_UNI_LOCATION;
 
-    for (int i = 0; i < NUM_STAGES; ++i) {
-        if (desc.fStages[i].fEnabled) {
+    for (int s = 0; s < kNumStages; ++s) {
+        if (desc.fStages[s].fEnabled) {
             GR_GL(BindAttribLocation(progID,
-                                     TEXMAT_ATTR_LOCATION(i),
+                                     TEXMAT_ATTR_LOCATION(s),
                                      tex_matrix_name(i).cstr()));
-            program->fUniLocations.fStages[i].fTextureMatrixUni =
+            program->fUniLocations.fStages[s].fTextureMatrixUni =
                                                     BOGUS_MATRIX_UNI_LOCATION;
         }
     }
@@ -888,13 +918,13 @@
                     GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME));
     GrAssert(-1 != program->fUniLocations.fViewMatrixUni);
 #endif
-    for (int i = 0; i < NUM_STAGES; ++i) {
-        StageUniLocations& locations = program->fUniLocations.fStages[i];
-        if (desc.fStages[i].fEnabled) {
+    for (int s = 0; s < kNumStages; ++s) {
+        StageUniLocations& locations = program->fUniLocations.fStages[s];
+        if (desc.fStages[s].fEnabled) {
 #if !ATTRIBUTE_MATRIX
             if (locations.fTextureMatrixUni) {
                 GrTokenString texMName;
-                tex_matrix_name(i, &texMName);
+                tex_matrix_name(s, &texMName);
                 locations.fTextureMatrixUni = GR_GL(GetUniformLocation(
                                                 progID,
                                                 texMName.cstr()));
@@ -907,7 +937,7 @@
 
             if (locations.fSamplerUni) {
                 GrTokenString samplerName;
-                sampler_name(i, &samplerName);
+                sampler_name(s, &samplerName);
                 locations.fSamplerUni = GR_GL(GetUniformLocation(
                                                      progID,
                                                      samplerName.cstr()));
@@ -918,7 +948,7 @@
 
             if (locations.fRadial2Uni) {
                 GrTokenString radial2ParamName;
-                radial2_param_name(i, &radial2ParamName);
+                radial2_param_name(s, &radial2ParamName);
                 locations.fRadial2Uni = GR_GL(GetUniformLocation(
                                              progID,
                                              radial2ParamName.cstr()));
@@ -935,13 +965,13 @@
     GR_GL(UseProgram(progID));
 
     // init sampler unis and set bogus values for state tracking
-    for (int i = 0; i < NUM_STAGES; ++i) {
-        if (-1 != program->fUniLocations.fStages[i].fSamplerUni) {
-            GR_GL(Uniform1i(program->fUniLocations.fStages[i].fSamplerUni, i));
+    for (int s = 0; s < kNumStages; ++s) {
+        if (-1 != program->fUniLocations.fStages[s].fSamplerUni) {
+            GR_GL(Uniform1i(program->fUniLocations.fStages[s].fSamplerUni, s));
         }
-        program->fTextureMatrix[i].setScale(GR_ScalarMax, GR_ScalarMax);
-        program->fRadial2CenterX1[i] = GR_ScalarMax;
-        program->fRadial2Radius0[i] = -GR_ScalarMax;
+        program->fTextureMatrix[s].setScale(GR_ScalarMax, GR_ScalarMax);
+        program->fRadial2CenterX1[s] = GR_ScalarMax;
+        program->fRadial2Radius0[s] = -GR_ScalarMax;
     }
     program->fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax);
 }
@@ -950,70 +980,63 @@
 
     // Must initialize all fields or cache will have false negatives!
     desc->fVertexLayout = fGeometrySrc.fVertexLayout;
-    desc->fStages[0].fEnabled = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
-    for (int i = 1; i < NUM_STAGES; ++i) {
-        desc->fStages[i].fEnabled       = false;
-        desc->fStages[i].fOptFlags      = 0;
-        desc->fStages[i].fCoordMapping  = (StageDesc::CoordMapping)0;
-        desc->fStages[i].fModulation    = (StageDesc::Modulation)0;
-    }
+    for (int s = 0; s < kNumStages; ++s) {
+        StageDesc& stage = desc->fStages[s];
+        
+        stage.fEnabled = VertexUsesStage(s, fGeometrySrc.fVertexLayout);
 
-    if (primType != kPoints_PrimitiveType) {
-        desc->fOptFlags = ProgramDesc::kNotPoints_OptFlagBit;
-    } else {
-        desc->fOptFlags = 0;
-    }
-#if SKIP_COLOR_MODULATE_OPT
-    if (!(desc->fVertexLayout & kColor_VertexLayoutBit) &&
-        (0xffffffff == fCurrDrawState.fColor)) {
-        desc->fOptFlags |= ProgramDesc::kVertexColorAllOnes_OptFlagBit;
-    }
-#endif
-
-    StageDesc& stage = desc->fStages[0];
-
-    if (stage.fEnabled) {
-        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
-        GrAssert(NULL != texture);
-        // we matrix to invert when orientation is TopDown, so make sure
-        // we aren't in that case before flagging as identity.
-        if (fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].isIdentity() &&
-            GrGLTexture::kTopDown_Orientation == texture->orientation()) {
-            stage.fOptFlags = StageDesc::kIdentityMatrix_OptFlagBit;
-        } else if (!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective()) {
-            stage.fOptFlags = StageDesc::kNoPerspective_OptFlagBit;
+        if (primType != kPoints_PrimitiveType) {
+            desc->fOptFlags = ProgramDesc::kNotPoints_OptFlagBit;
         } else {
-            stage.fOptFlags = 0;
+            desc->fOptFlags = 0;
         }
-        switch (fCurrDrawState.fSamplerState.getSampleMode()) {
-        case GrSamplerState::kNormal_SampleMode:
-            stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
-            stage.fModulation = StageDesc::kColor_Modulation;
-            break;
-        case GrSamplerState::kAlphaMod_SampleMode:
-            stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
-            stage.fModulation = StageDesc::kAlpha_Modulation;
-            break;
-        case GrSamplerState::kRadial_SampleMode:
-            stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
-            stage.fModulation = StageDesc::kColor_Modulation;
-            break;
-        case GrSamplerState::kRadial2_SampleMode:
-            stage.fCoordMapping = StageDesc::kRadial2Gradient_CoordMapping;
-            stage.fModulation = StageDesc::kColor_Modulation;
-            break;
-        case GrSamplerState::kSweep_SampleMode:
-            stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
-            stage.fModulation = StageDesc::kColor_Modulation;
-            break;
-        default:
-            GrAssert(!"Unexpected sample mode!");
-            break;
+    #if SKIP_COLOR_MODULATE_OPT
+        if (!(desc->fVertexLayout & kColor_VertexLayoutBit) &&
+            (0xffffffff == fCurrDrawState.fColor)) {
+            desc->fOptFlags |= ProgramDesc::kVertexColorAllOnes_OptFlagBit;
         }
-    } else {
-        stage.fOptFlags     = 0;
-        stage.fCoordMapping = (StageDesc::CoordMapping)0;
-        stage.fModulation   = (StageDesc::Modulation)0;
+    #endif
+
+        if (stage.fEnabled) {
+            GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
+            GrAssert(NULL != texture);
+            // we matrix to invert when orientation is TopDown, so make sure
+            // we aren't in that case before flagging as identity.
+            if (fCurrDrawState.fTextureMatrices[s].isIdentity() &&
+                GrGLTexture::kTopDown_Orientation == texture->orientation()) {
+                stage.fOptFlags = StageDesc::kIdentityMatrix_OptFlagBit;
+            } else if (!fCurrDrawState.fTextureMatrices[s].hasPerspective()) {
+                stage.fOptFlags = StageDesc::kNoPerspective_OptFlagBit;
+            } else {
+                stage.fOptFlags = 0;
+            }
+            switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
+            case GrSamplerState::kNormal_SampleMode:
+                stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
+                break;
+            case GrSamplerState::kRadial_SampleMode:
+                stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
+                break;
+            case GrSamplerState::kRadial2_SampleMode:
+                stage.fCoordMapping = StageDesc::kRadial2Gradient_CoordMapping;
+                break;
+            case GrSamplerState::kSweep_SampleMode:
+                stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
+                break;
+            default:
+                GrAssert(!"Unexpected sample mode!");
+                break;
+            }
+            if (GrTexture::kAlpha_8_PixelConfig == texture->config()) {
+                stage.fModulation = StageDesc::kAlpha_Modulation;
+            } else {
+                stage.fModulation = StageDesc::kColor_Modulation;
+            }
+        } else {
+            stage.fOptFlags     = 0;
+            stage.fCoordMapping = (StageDesc::CoordMapping)0;
+            stage.fModulation   = (StageDesc::Modulation)0;
+        }
     }
 }
 
@@ -1088,7 +1111,9 @@
     fHWGeometryState.fVertexLayout = 0;
     fHWGeometryState.fPositionPtr  = (void*) ~0;
     GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
-    GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION));
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
+    }
     GR_GL(EnableVertexAttribArray(POS_ATTR_LOCATION));
 
     fHWProgramID = 0;
@@ -1100,7 +1125,7 @@
         GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
         0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
         0, 0, GrMatrix::I()[8]);
-    m.setConcat(m, fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+    m.setConcat(m, fCurrDrawState.fViewMatrix);
 
     // ES doesn't allow you to pass true to the transpose param,
     // so do our own transpose
@@ -1124,11 +1149,11 @@
 #endif
 }
 
-void GrGpuGLShaders2::flushTextureMatrix() {
+void GrGpuGLShaders2::flushTextureMatrix(int stage) {
 
-    GrAssert(NULL != fCurrDrawState.fTexture);
+    GrAssert(NULL != fCurrDrawState.fTextures[stage]);
     GrGLTexture::Orientation orientation =
-         ((GrGLTexture*)fCurrDrawState.fTexture)->orientation();
+         ((GrGLTexture*)fCurrDrawState.fTextures[stage])->orientation();
 
     GrMatrix* m;
     GrMatrix temp;
@@ -1138,11 +1163,11 @@
             0, -GR_Scalar1, GR_Scalar1,
             0, 0, GrMatrix::I()[8]
         );
-        temp.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+        temp.preConcat(fCurrDrawState.fTextureMatrices[stage]);
         m = &temp;
     } else {
         GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
-        m = &fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+        m = &fCurrDrawState.fTextureMatrices[stage];
     }
 
     // ES doesn't allow you to pass true to the transpose param,
@@ -1163,16 +1188,16 @@
     glVertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+1, mt+3);
     glVertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+2, mt+6);
 #else
-    GR_GL(UniformMatrix3fv(fProgram->fUniLocations.fStages[0].fTextureMatrixUni,
+    GR_GL(UniformMatrix3fv(fProgram->fUniLocations.fStages[stage].fTextureMatrixUni,
                            1,
                            false,
                            mt));
 #endif
 }
 
-void GrGpuGLShaders2::flushRadial2() {
+void GrGpuGLShaders2::flushRadial2(int stage) {
 
-    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
+    const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[stage];
 
     GrScalar centerX1 = sampler.getRadial2CenterX1();
     GrScalar radius0 = sampler.getRadial2Radius0();
@@ -1187,7 +1212,9 @@
         GrScalarToFloat(GrMul(radius0, radius0)),
         sampler.isRadial2PosRoot() ? 1.f : -1.f
     };
-    GR_GL(Uniform1fv(fProgram->fUniLocations.fStages[0].fRadial2Uni, 6, unis));
+    GR_GL(Uniform1fv(fProgram->fUniLocations.fStages[stage].fRadial2Uni, 
+                     6, 
+                     unis));
 }
 
 void GrGpuGLShaders2::flushProgram(PrimitiveType type) {
@@ -1212,8 +1239,7 @@
         // our coords are in pixel space and the GL matrices map to NDC
         // so if the viewport changed, our matrix is now wrong.
 #if ATTRIBUTE_MATRIX
-        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax,
-                                                                      GR_ScalarMax);
+        fHWDrawState.fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax);
 #else
         // we assume all shader matrices may be wrong after viewport changes
         fProgramCache->invalidateViewMatrices();
@@ -1241,7 +1267,7 @@
     }
 
 #if ATTRIBUTE_MATRIX
-    GrMatrix& currViewMatrix = fHWDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    GrMatrix& currViewMatrix = fHWDrawState.fViewMatrix;
     GrMatrix& currTextureMatrix = fHWDrawState.fMatrixModeCache[kTexture_MatrixMode];
     GrGLTexture::Orientation& orientation = fTextureOrientation;
 #else
@@ -1250,53 +1276,56 @@
     GrGLTexture::Orientation& orientation =  fProgram->fTextureOrientation[0];
 #endif
 
-    if (currViewMatrix != fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+    if (currViewMatrix != fCurrDrawState.fViewMatrix) {
         flushViewMatrix();
-        currViewMatrix = fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+        currViewMatrix = fCurrDrawState.fViewMatrix;
     }
 
-    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
-    if (NULL != texture) {
-        if (-1 != fProgram->fUniLocations.fStages[0].fTextureMatrixUni &&
-            (currTextureMatrix !=
-                        fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode] ||
-             orientation != texture->orientation())) {
-            flushTextureMatrix();
-            currTextureMatrix = fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
-            orientation = texture->orientation();
+    for (int s = 0; s < kNumStages; ++s) { 
+        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
+        if (NULL != texture) {
+            if (-1 != fProgram->fUniLocations.fStages[s].fTextureMatrixUni &&
+                (currTextureMatrix != fCurrDrawState.fTextureMatrices[s] ||
+                 orientation != texture->orientation())) {
+                flushTextureMatrix(s);
+                currTextureMatrix = fCurrDrawState.fTextureMatrices[s];
+                orientation = texture->orientation();
+            }
         }
-    }
 
-    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
-    if (-1 != fProgram->fUniLocations.fStages[0].fRadial2Uni &&
-        (fProgram->fRadial2CenterX1[0] != sampler.getRadial2CenterX1() ||
-         fProgram->fRadial2Radius0[0]  != sampler.getRadial2Radius0()  ||
-         fProgram->fRadial2PosRoot[0]  != sampler.isRadial2PosRoot())) {
+        const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
+        if (-1 != fProgram->fUniLocations.fStages[s].fRadial2Uni &&
+            (fProgram->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
+             fProgram->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
+             fProgram->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {
 
-        flushRadial2();
+            flushRadial2(s);
 
-        fProgram->fRadial2CenterX1[0] = sampler.getRadial2CenterX1();
-        fProgram->fRadial2Radius0[0]  = sampler.getRadial2Radius0();
-        fProgram->fRadial2PosRoot[0]  = sampler.isRadial2PosRoot();
+            fProgram->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
+            fProgram->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
+            fProgram->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
+        }
     }
 
     return true;
 }
 
 void GrGpuGLShaders2::setupGeometry(uint32_t startVertex,
-                                   uint32_t startIndex,
-                                   uint32_t vertexCount,
-                                   uint32_t indexCount) {
+                                    uint32_t startIndex,
+                                    uint32_t vertexCount,
+                                    uint32_t indexCount) {
 
-    int newColorOffset, newTexCoordOffset;
+    int newColorOffset;
+    int newTexCoordOffsets[kMaxTexCoords];
 
-    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
-                                             &newTexCoordOffset,
-                                             &newColorOffset);
-    int oldColorOffset, oldTexCoordOffset;
-    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
-                                             &oldTexCoordOffset,
-                                             &oldColorOffset);
+    GLsizei newStride = VertexSizeAndOffsetsByIdx(fGeometrySrc.fVertexLayout,
+                                                  newTexCoordOffsets,
+                                                  &newColorOffset);
+    int oldColorOffset;
+    int oldTexCoordOffsets[kMaxTexCoords];
+    GLsizei oldStride = VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
+                                                  oldTexCoordOffsets,
+                                                  &oldColorOffset);
 
     const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
 
@@ -1305,7 +1334,7 @@
         GrAssert(!fGeometrySrc.fVertexBuffer->isLocked());
         if (fHWGeometryState.fVertexBuffer != fGeometrySrc.fVertexBuffer) {
             GrGLVertexBuffer* buf =
-            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
+                                (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
             GR_GL(BindBuffer(GL_ARRAY_BUFFER, buf->bufferID()));
             fHWGeometryState.fVertexBuffer = fGeometrySrc.fVertexBuffer;
         }
@@ -1327,8 +1356,7 @@
         GrAssert(NULL != fGeometrySrc.fIndexBuffer);
         GrAssert(!fGeometrySrc.fIndexBuffer->isLocked());
         if (fHWGeometryState.fIndexBuffer != fGeometrySrc.fIndexBuffer) {
-            GrGLIndexBuffer* buf =
-            (GrGLIndexBuffer*)fGeometrySrc.fIndexBuffer;
+            GrGLIndexBuffer* buf = (GrGLIndexBuffer*)fGeometrySrc.fIndexBuffer;
             GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf->bufferID()));
             fHWGeometryState.fIndexBuffer = fGeometrySrc.fIndexBuffer;
         }
@@ -1361,17 +1389,19 @@
         fHWGeometryState.fPositionPtr = posPtr;
     }
 
-    if (newTexCoordOffset > 0) {
-        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
-        if (oldTexCoordOffset <= 0) {
-            GR_GL(EnableVertexAttribArray(TEX_ATTR_LOCATION));
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (newTexCoordOffsets[t] > 0) {
+            GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffsets[t];
+            if (oldTexCoordOffsets[t] <= 0) {
+                GR_GL(EnableVertexAttribArray(TEX_ATTR_LOCATION(t)));
+            }
+            if (posChange || newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
+                GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION(t), 2, scalarType,
+                                          texCoordNorm, newStride, texCoordPtr));
+            }
+        } else if (oldTexCoordOffsets[t] > 0) {
+            GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
         }
-        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
-            GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION, 2, scalarType,
-                                      texCoordNorm, newStride, texCoordPtr));
-        }
-    } else if (oldTexCoordOffset > 0) {
-        GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION));
     }
 
     if (newColorOffset > 0) {
diff --git a/gpu/src/GrGpuGLShaders2.h b/gpu/src/GrGpuGLShaders2.h
index c484544..bba63f6 100644
--- a/gpu/src/GrGpuGLShaders2.h
+++ b/gpu/src/GrGpuGLShaders2.h
@@ -37,17 +37,17 @@
                                uint32_t indexCount);
 
 private:
-    static const int NUM_STAGES;
 
     void resetContextHelper();
 
     // sets the texture matrix uniform for currently bound program
-    void flushTextureMatrix();
+    void flushTextureMatrix(int stage);
+   
     // sets the MVP matrix uniform for currently bound program
     void flushViewMatrix();
 
     // flushes the parameters to two point radial gradient
-    void flushRadial2();
+    void flushRadial2(int stage);
 
     // called at flush time to setup the appropriate program
     void flushProgram(PrimitiveType type);
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 952ba83..494f227 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -156,9 +156,11 @@
     GrAssert(!fReservedGeometry.fLocked);
     uint32_t numStates = fStates.count();
     for (uint32_t i = 0; i < numStates; ++i) {
-        GrTexture* tex = accessSavedDrawState(fStates[i]).fTexture;
-        if (NULL != tex) {
-            tex->unref();
+        for (int s = 0; s < kNumStages; ++s) {
+            GrTexture* tex = accessSavedDrawState(fStates[i]).fTextures[s];
+            if (NULL != tex) {
+                tex->unref();
+            }
         }
     }
     fDraws.reset();
@@ -320,8 +322,10 @@
         newState = old != fCurrDrawState;
     }
     if (newState) {
-        if (NULL != fCurrDrawState.fTexture) {
-            fCurrDrawState.fTexture->ref();
+        for (int s = 0; s < kNumStages; ++s) {
+            if (NULL != fCurrDrawState.fTextures[s]) {
+                fCurrDrawState.fTextures[s]->ref();
+            }
         }
         saveCurrentDrawState(&fStates.push_back());
     }
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 0264856..0a777d6 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -23,7 +23,9 @@
 #include "GrTextStrike_impl.h"
 #include "GrFontScaler.h"
 
-static const GrVertexLayout VLAYOUT = GrDrawTarget::kTextFormat_VertexLayoutBit;
+static const GrVertexLayout VLAYOUT = 
+                                GrDrawTarget::kTextFormat_VertexLayoutBit |
+                                GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
 
 void GrTextContext::flushGlyphs() {
     if (fCurrVertex > 0) {
@@ -33,15 +35,14 @@
 
         GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
                                GrSamplerState::kRepeat_WrapMode,
-                               GrSamplerState::kAlphaMod_SampleMode,
                                !fExtMatrix.isIdentity());
-        fDrawTarget->setSamplerState(sampler);
+        fDrawTarget->setSamplerState(0, sampler);
 
         GrAssert(GrIsALIGN4(fCurrVertex));
         int nIndices = fCurrVertex + (fCurrVertex >> 1);
         GrAssert(fCurrTexture);
-        fDrawTarget->setTexture(fCurrTexture);
-        fDrawTarget->setTextureMatrix(GrMatrix::I());
+        fDrawTarget->setTexture(0, fCurrTexture);
+        fDrawTarget->setTextureMatrix(0, GrMatrix::I());
         fDrawTarget->setIndexSourceToBuffer(fContext->quadIndexBuffer());
 
         fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,