Improve handling of FF vertex array state

R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

Review URL: https://chromiumcodereview.appspot.com/23542013

git-svn-id: http://skia.googlecode.com/svn/trunk/src@11154 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/gl/GrGLDefines.h b/gpu/gl/GrGLDefines.h
index a843953..0dbd425 100644
--- a/gpu/gl/GrGLDefines.h
+++ b/gpu/gl/GrGLDefines.h
@@ -559,6 +559,7 @@
 #define GR_GL_VERTEX_ARRAY                       0x8074
 #define GR_GL_NORMAL_ARRAY                       0x8075
 #define GR_GL_COLOR_ARRAY                        0x8076
+#define GR_GL_SECONDARY_COLOR_ARRAY              0x845E
 #define GR_GL_INDEX_ARRAY                        0x8077
 #define GR_GL_TEXTURE_COORD_ARRAY                0x8078
 #define GR_GL_EDGE_FLAG_ARRAY                    0x8079
diff --git a/gpu/gl/GrGLVertexArray.cpp b/gpu/gl/GrGLVertexArray.cpp
index bf3d3b5..605ec33 100644
--- a/gpu/gl/GrGLVertexArray.cpp
+++ b/gpu/gl/GrGLVertexArray.cpp
@@ -49,7 +49,39 @@
     }
 }
 
-void GrGLAttribArrayState::disableUnusedAttribArrays(const GrGpuGL* gpu, uint64_t usedMask) {
+void GrGLAttribArrayState::setFixedFunctionVertexArray(const GrGpuGL* gpu,
+                                                       GrGLVertexBuffer* buffer,
+                                                       GrGLint size,
+                                                       GrGLenum type,
+                                                       GrGLsizei stride,
+                                                       GrGLvoid* offset) {
+    SkASSERT(gpu->glCaps().fixedFunctionSupport());
+    AttribArrayState* array = &fFixedFunctionVertexArray;
+    if (!array->fEnableIsValid || !array->fEnabled) {
+        GR_GL_CALL(gpu->glInterface(), EnableClientState(GR_GL_VERTEX_ARRAY));
+        array->fEnableIsValid = true;
+        array->fEnabled = true;
+    }
+    if (!array->fAttribPointerIsValid ||
+        array->fVertexBufferID != buffer->bufferID() ||
+        array->fSize != size ||
+        array->fStride != stride ||
+        array->fOffset != offset) {
+
+        buffer->bind();
+        GR_GL_CALL(gpu->glInterface(), VertexPointer(size,
+                                                     type,
+                                                     stride,
+                                                     offset));
+        array->fAttribPointerIsValid = true;
+        array->fVertexBufferID = buffer->bufferID();
+        array->fSize = size;
+        array->fStride = stride;
+        array->fOffset = offset;
+    }
+}
+
+void GrGLAttribArrayState::disableUnusedArrays(const GrGpuGL* gpu, uint64_t usedMask, bool usingFFVertexArray) {
     int count = fAttribArrayStates.count();
     for (int i = 0; i < count; ++i) {
         if (!(usedMask & 0x1)) {
@@ -58,10 +90,41 @@
                 fAttribArrayStates[i].fEnableIsValid = true;
                 fAttribArrayStates[i].fEnabled = false;
             }
+        } else {
+            SkASSERT(fAttribArrayStates[i].fEnableIsValid && fAttribArrayStates[i].fEnabled);
         }
         // if the count is greater than 64 then this will become 0 and we will disable arrays 64+.
         usedMask >>= 1;
     }
+
+    // Deal with fixed-function vertex arrays.
+    if (gpu->glCaps().fixedFunctionSupport()) {
+        if (!usingFFVertexArray) {
+            if (!fFixedFunctionVertexArray.fEnableIsValid || fFixedFunctionVertexArray.fEnabled) {
+                GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_VERTEX_ARRAY));
+                fFixedFunctionVertexArray.fEnableIsValid = true;
+                fFixedFunctionVertexArray.fEnabled = false;
+            }
+        } else {
+            SkASSERT(fFixedFunctionVertexArray.fEnableIsValid && fFixedFunctionVertexArray.fEnabled);
+        }
+        // When we use fixed function vertex processing we always use the vertex array and none of
+        // the other arrays.
+        if (!fUnusedFixedFunctionArraysDisabled) {
+            GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_NORMAL_ARRAY));
+            GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_COLOR_ARRAY));
+            GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_SECONDARY_COLOR_ARRAY));
+            GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_INDEX_ARRAY));
+            GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_EDGE_FLAG_ARRAY));
+            for (int i = 0; i < gpu->glCaps().maxFixedFunctionTextureCoords(); ++i) {
+                GR_GL_CALL(gpu->glInterface(), ClientActiveTexture(GR_GL_TEXTURE0 + i));
+                GR_GL_CALL(gpu->glInterface(), DisableClientState(GR_GL_TEXTURE_COORD_ARRAY));
+            }
+            fUnusedFixedFunctionArraysDisabled = true;
+        }
+    } else {
+        SkASSERT(!usingFFVertexArray);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -115,9 +178,6 @@
  }
 
 void GrGLVertexArray::invalidateCachedState() {
-    int count = fAttribArrays.count();
-    for (int i = 0; i < count; ++i) {
-        fAttribArrays.invalidate();
-    }
+    fAttribArrays.invalidate();
     fIndexBufferIDIsValid = false;
 }
diff --git a/gpu/gl/GrGLVertexArray.h b/gpu/gl/GrGLVertexArray.h
index 7352caf..5cc7b5f 100644
--- a/gpu/gl/GrGLVertexArray.h
+++ b/gpu/gl/GrGLVertexArray.h
@@ -49,7 +49,12 @@
  */
 class GrGLAttribArrayState {
 public:
-    explicit GrGLAttribArrayState(int arrayCount = 0) { this->resize(arrayCount); }
+    explicit GrGLAttribArrayState(int arrayCount = 0) {
+        this->resize(arrayCount);
+        // glVertexPointer doesn't have a normalization param.
+        fFixedFunctionVertexArray.fNormalized = false;
+        fUnusedFixedFunctionArraysDisabled = false;
+    }
 
     void resize(int newCount) {
         fAttribArrayStates.resize_back(newCount);
@@ -72,17 +77,26 @@
              GrGLsizei stride,
              GrGLvoid* offset);
 
+    void setFixedFunctionVertexArray(const GrGpuGL*,
+                                     GrGLVertexBuffer*,
+                                     GrGLint size,
+                                     GrGLenum type,
+                                     GrGLsizei stride,
+                                     GrGLvoid* offset);
+
     /**
      * This function disables vertex attribs not present in the mask. It is assumed that the
      * GrGLAttribArrayState is tracking the state of the currently bound vertex array object.
      */
-    void disableUnusedAttribArrays(const GrGpuGL*, uint64_t usedAttribArrayMask);
+    void disableUnusedArrays(const GrGpuGL*, uint64_t usedAttribArrayMask, bool usingFFVertexArray);
 
     void invalidate() {
         int count = fAttribArrayStates.count();
         for (int i = 0; i < count; ++i) {
             fAttribArrayStates[i].invalidate();
         }
+        fFixedFunctionVertexArray.invalidate();
+        fUnusedFixedFunctionArraysDisabled = false;
     }
 
     void notifyVertexBufferDelete(GrGLuint id) {
@@ -93,6 +107,10 @@
                 fAttribArrayStates[i].invalidate();
             }
         }
+        if (fFixedFunctionVertexArray.fAttribPointerIsValid &&
+            id == fFixedFunctionVertexArray.fVertexBufferID) {
+            fFixedFunctionVertexArray.invalidate();
+        }
     }
 
     /**
@@ -122,6 +140,13 @@
     };
 
     SkSTArray<16, AttribArrayState, true> fAttribArrayStates;
+
+    // Tracks the array specified by glVertexPointer.
+    AttribArrayState fFixedFunctionVertexArray;
+
+    // Tracks whether we've disabled the other fixed function arrays that we don't
+    // use (e.g. glNormalPointer).
+    bool fUnusedFixedFunctionArraysDisabled;
 };
 
 /**
diff --git a/gpu/gl/GrGpuGL.cpp b/gpu/gl/GrGpuGL.cpp
index bc067e0..6f9a8ab 100644
--- a/gpu/gl/GrGpuGL.cpp
+++ b/gpu/gl/GrGpuGL.cpp
@@ -371,17 +371,7 @@
         GL_CALL(MatrixMode(GR_GL_MODELVIEW));
         GL_CALL(LoadIdentity());
 
-        // When we use fixed function vertex processing we always use the vertex array
-        // and none of the other arrays.
-        GL_CALL(EnableClientState(GR_GL_VERTEX_ARRAY));
-        GL_CALL(DisableClientState(GR_GL_NORMAL_ARRAY));
-        GL_CALL(DisableClientState(GR_GL_COLOR_ARRAY));
-        GL_CALL(DisableClientState(GR_GL_INDEX_ARRAY));
-        GL_CALL(DisableClientState(GR_GL_EDGE_FLAG_ARRAY));
         for (int i = 0; i < this->glCaps().maxFixedFunctionTextureCoords(); ++i) {
-            GL_CALL(ClientActiveTexture(GR_GL_TEXTURE0 + i));
-            GL_CALL(DisableClientState(GR_GL_TEXTURE_COORD_ARRAY));
-
             GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + i));
             GL_CALL(Disable(GR_GL_TEXTURE_GEN_S));
             GL_CALL(Disable(GR_GL_TEXTURE_GEN_T));
diff --git a/gpu/gl/GrGpuGL.h b/gpu/gl/GrGpuGL.h
index 669f21c..95ae726 100644
--- a/gpu/gl/GrGpuGL.h
+++ b/gpu/gl/GrGpuGL.h
@@ -303,6 +303,9 @@
             fDefaultVertexArrayBoundIndexBufferID = false;
             fDefaultVertexArrayBoundIndexBufferIDIsValid = false;
             fDefaultVertexArrayAttribState.invalidate();
+            if (NULL != fVBOVertexArray) {
+                fVBOVertexArray->invalidateCachedState();
+            }
         }
 
         void notifyVertexArrayDelete(GrGLuint id) {
diff --git a/gpu/gl/GrGpuGL_program.cpp b/gpu/gl/GrGpuGL_program.cpp
index ce74303..159d6d8 100644
--- a/gpu/gl/GrGpuGL_program.cpp
+++ b/gpu/gl/GrGpuGL_program.cpp
@@ -371,5 +371,5 @@
                          vertexOffsetInBytes + vertexAttrib->fOffset));
     }
 
-    attribState->disableUnusedAttribArrays(this, usedAttribArraysMask);
+    attribState->disableUnusedArrays(this, usedAttribArraysMask, false);
 }