Squash State dirty bits to below 64.

This will dramatically speed up performance on 64-bit systems due to
the efficiency of angle::BitSet64. Improves performance of the GL
back-end on benchmarks quite a bit.

Squashes the Pack and Unpack states together. Also moves the current
value dirty bits to a single dirty bit, with a separate set for each
attribute. Also squashes a multisample dirty bit.

Also fixes an incorrect dirty bit in StateManagerGL.

We may want to implement a semi-fast version of BitSet64 for 32-bit
systems if we find performance is not satisfactory.

BUG=angleproject:2188

Change-Id: I4616782bf75413252ede3e3ac752b9ccdb9aab49
Reviewed-on: https://chromium-review.googlesource.com/722423
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index eb0b424..476a139 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -389,23 +389,11 @@
     }
 
     // Initialize dirty bit masks
-    // TODO(jmadill): additional ES3 state
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_ALIGNMENT);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_ROW_LENGTH);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_IMAGE_HEIGHT);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_SKIP_IMAGES);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_SKIP_ROWS);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_SKIP_PIXELS);
-    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_BUFFER_BINDING);
+    mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_STATE);
     // No dirty objects.
 
     // Readpixels uses the pack state and read FBO
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_ALIGNMENT);
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_REVERSE_ROW_ORDER);
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_ROW_LENGTH);
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_SKIP_ROWS);
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_SKIP_PIXELS);
-    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_BUFFER_BINDING);
+    mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_STATE);
     mReadPixelsDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER);
 
     mClearDirtyBits.set(State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED);
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 1cd0bb7..eca86b0 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -585,7 +585,8 @@
 {
     ASSERT(maskNumber < mMaxSampleMaskWords);
     mSampleMaskValues[maskNumber] = mask;
-    mDirtyBits.set(DIRTY_BIT_SAMPLE_MASK_WORD_0 + maskNumber);
+    // TODO(jmadill): Use a child dirty bit if we ever use more than two words.
+    mDirtyBits.set(DIRTY_BIT_SAMPLE_MASK);
 }
 
 GLbitfield State::getSampleMaskWord(GLuint maskNumber) const
@@ -1340,13 +1341,13 @@
 void State::setPixelPackBufferBinding(const Context *context, Buffer *buffer)
 {
     mPack.pixelBuffer.set(context, buffer);
-    mDirtyBits.set(DIRTY_BIT_PACK_BUFFER_BINDING);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 void State::setPixelUnpackBufferBinding(const Context *context, Buffer *buffer)
 {
     mUnpack.pixelBuffer.set(context, buffer);
-    mDirtyBits.set(DIRTY_BIT_UNPACK_BUFFER_BINDING);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 Buffer *State::getTargetBuffer(GLenum target) const
@@ -1406,21 +1407,24 @@
 {
     ASSERT(static_cast<size_t>(index) < mVertexAttribCurrentValues.size());
     mVertexAttribCurrentValues[index].setFloatValues(values);
-    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_0 + index);
+    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
+    mDirtyCurrentValues.set(index);
 }
 
 void State::setVertexAttribu(GLuint index, const GLuint values[4])
 {
     ASSERT(static_cast<size_t>(index) < mVertexAttribCurrentValues.size());
     mVertexAttribCurrentValues[index].setUnsignedIntValues(values);
-    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_0 + index);
+    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
+    mDirtyCurrentValues.set(index);
 }
 
 void State::setVertexAttribi(GLuint index, const GLint values[4])
 {
     ASSERT(static_cast<size_t>(index) < mVertexAttribCurrentValues.size());
     mVertexAttribCurrentValues[index].setIntValues(values);
-    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_0 + index);
+    mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
+    mDirtyCurrentValues.set(index);
 }
 
 void State::setVertexAttribPointer(const Context *context,
@@ -1458,7 +1462,7 @@
 void State::setPackAlignment(GLint alignment)
 {
     mPack.alignment = alignment;
-    mDirtyBits.set(DIRTY_BIT_PACK_ALIGNMENT);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 GLint State::getPackAlignment() const
@@ -1469,7 +1473,7 @@
 void State::setPackReverseRowOrder(bool reverseRowOrder)
 {
     mPack.reverseRowOrder = reverseRowOrder;
-    mDirtyBits.set(DIRTY_BIT_PACK_REVERSE_ROW_ORDER);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 bool State::getPackReverseRowOrder() const
@@ -1480,7 +1484,7 @@
 void State::setPackRowLength(GLint rowLength)
 {
     mPack.rowLength = rowLength;
-    mDirtyBits.set(DIRTY_BIT_PACK_ROW_LENGTH);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 GLint State::getPackRowLength() const
@@ -1491,7 +1495,7 @@
 void State::setPackSkipRows(GLint skipRows)
 {
     mPack.skipRows = skipRows;
-    mDirtyBits.set(DIRTY_BIT_PACK_SKIP_ROWS);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 GLint State::getPackSkipRows() const
@@ -1502,7 +1506,7 @@
 void State::setPackSkipPixels(GLint skipPixels)
 {
     mPack.skipPixels = skipPixels;
-    mDirtyBits.set(DIRTY_BIT_PACK_SKIP_PIXELS);
+    mDirtyBits.set(DIRTY_BIT_PACK_STATE);
 }
 
 GLint State::getPackSkipPixels() const
@@ -1523,7 +1527,7 @@
 void State::setUnpackAlignment(GLint alignment)
 {
     mUnpack.alignment = alignment;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_ALIGNMENT);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackAlignment() const
@@ -1534,7 +1538,7 @@
 void State::setUnpackRowLength(GLint rowLength)
 {
     mUnpack.rowLength = rowLength;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_ROW_LENGTH);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackRowLength() const
@@ -1545,7 +1549,7 @@
 void State::setUnpackImageHeight(GLint imageHeight)
 {
     mUnpack.imageHeight = imageHeight;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_IMAGE_HEIGHT);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackImageHeight() const
@@ -1556,7 +1560,7 @@
 void State::setUnpackSkipImages(GLint skipImages)
 {
     mUnpack.skipImages = skipImages;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_SKIP_IMAGES);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackSkipImages() const
@@ -1567,7 +1571,7 @@
 void State::setUnpackSkipRows(GLint skipRows)
 {
     mUnpack.skipRows = skipRows;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_SKIP_ROWS);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackSkipRows() const
@@ -1578,7 +1582,7 @@
 void State::setUnpackSkipPixels(GLint skipPixels)
 {
     mUnpack.skipPixels = skipPixels;
-    mDirtyBits.set(DIRTY_BIT_UNPACK_SKIP_PIXELS);
+    mDirtyBits.set(DIRTY_BIT_UNPACK_STATE);
 }
 
 GLint State::getUnpackSkipPixels() const
@@ -2436,4 +2440,11 @@
     return NoError();
 }
 
+AttributesMask State::getAndResetDirtyCurrentValues() const
+{
+    AttributesMask retVal = mDirtyCurrentValues;
+    mDirtyCurrentValues.reset();
+    return retVal;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 14c45d6..0ab877d 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -390,9 +390,8 @@
         DIRTY_BIT_SAMPLE_COVERAGE_ENABLED,
         DIRTY_BIT_SAMPLE_COVERAGE,
         DIRTY_BIT_SAMPLE_MASK_ENABLED,
-        DIRTY_BIT_SAMPLE_MASK_WORD_0,
-        DIRTY_BIT_SAMPLE_MASK_WORD_MAX = DIRTY_BIT_SAMPLE_MASK_WORD_0 + MAX_SAMPLE_MASK_WORDS,
-        DIRTY_BIT_DEPTH_TEST_ENABLED   = DIRTY_BIT_SAMPLE_MASK_WORD_MAX,
+        DIRTY_BIT_SAMPLE_MASK,
+        DIRTY_BIT_DEPTH_TEST_ENABLED,
         DIRTY_BIT_DEPTH_FUNC,
         DIRTY_BIT_DEPTH_MASK,
         DIRTY_BIT_STENCIL_TEST_ENABLED,
@@ -413,19 +412,8 @@
         DIRTY_BIT_CLEAR_COLOR,
         DIRTY_BIT_CLEAR_DEPTH,
         DIRTY_BIT_CLEAR_STENCIL,
-        DIRTY_BIT_UNPACK_ALIGNMENT,
-        DIRTY_BIT_UNPACK_ROW_LENGTH,
-        DIRTY_BIT_UNPACK_IMAGE_HEIGHT,
-        DIRTY_BIT_UNPACK_SKIP_IMAGES,
-        DIRTY_BIT_UNPACK_SKIP_ROWS,
-        DIRTY_BIT_UNPACK_SKIP_PIXELS,
-        DIRTY_BIT_UNPACK_BUFFER_BINDING,
-        DIRTY_BIT_PACK_ALIGNMENT,
-        DIRTY_BIT_PACK_REVERSE_ROW_ORDER,
-        DIRTY_BIT_PACK_ROW_LENGTH,
-        DIRTY_BIT_PACK_SKIP_ROWS,
-        DIRTY_BIT_PACK_SKIP_PIXELS,
-        DIRTY_BIT_PACK_BUFFER_BINDING,
+        DIRTY_BIT_UNPACK_STATE,
+        DIRTY_BIT_PACK_STATE,
         DIRTY_BIT_DITHER_ENABLED,
         DIRTY_BIT_GENERATE_MIPMAP_HINT,
         DIRTY_BIT_SHADER_DERIVATIVE_HINT,
@@ -446,12 +434,13 @@
         DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ,  // CHROMIUM_path_rendering path projection matrix
         DIRTY_BIT_PATH_RENDERING_STENCIL_STATE,
         DIRTY_BIT_FRAMEBUFFER_SRGB,  // GL_EXT_sRGB_write_control
-        DIRTY_BIT_CURRENT_VALUE_0,
-        DIRTY_BIT_CURRENT_VALUE_MAX = DIRTY_BIT_CURRENT_VALUE_0 + MAX_VERTEX_ATTRIBS,
-        DIRTY_BIT_INVALID           = DIRTY_BIT_CURRENT_VALUE_MAX,
-        DIRTY_BIT_MAX               = DIRTY_BIT_INVALID,
+        DIRTY_BIT_CURRENT_VALUES,
+        DIRTY_BIT_INVALID,
+        DIRTY_BIT_MAX = DIRTY_BIT_INVALID,
     };
 
+    static_assert(DIRTY_BIT_MAX <= 64, "State dirty bits must be capped at 64");
+
     // TODO(jmadill): Consider storing dirty objects in a list instead of by binding.
     enum DirtyObjectType
     {
@@ -479,6 +468,10 @@
     void syncDirtyObject(const Context *context, GLenum target);
     void setObjectDirty(GLenum target);
 
+    // This actually clears the current value dirty bits.
+    // TODO(jmadill): Pass mutable dirty bits into Impl.
+    AttributesMask getAndResetDirtyCurrentValues() const;
+
     void setImageUnit(const Context *context,
                       GLuint unit,
                       Texture *texture,
@@ -629,6 +622,7 @@
 
     DirtyBits mDirtyBits;
     DirtyObjects mDirtyObjects;
+    mutable AttributesMask mDirtyCurrentValues;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index b1f68ed..33c56ca 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -969,16 +969,16 @@
                     }
                     vao11->markAllAttributeDivisorsForAdjustment(numViews);
                 }
+                break;
             }
-            break;
-            default:
-                if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
-                    dirtyBit < gl::State::DIRTY_BIT_CURRENT_VALUE_MAX)
+            case gl::State::DIRTY_BIT_CURRENT_VALUES:
+            {
+                for (auto attribIndex : state.getAndResetDirtyCurrentValues())
                 {
-                    size_t attribIndex =
-                        static_cast<size_t>(dirtyBit - gl::State::DIRTY_BIT_CURRENT_VALUE_0);
                     invalidateCurrentValueAttrib(attribIndex);
                 }
+            }
+            default:
                 break;
         }
     }
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index c606dbb..aef7a4a 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -514,7 +514,7 @@
         mUnpackAlignment = alignment;
         mFunctions->pixelStorei(GL_UNPACK_ALIGNMENT, mUnpackAlignment);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_ALIGNMENT);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     if (mUnpackRowLength != rowLength)
@@ -522,7 +522,7 @@
         mUnpackRowLength = rowLength;
         mFunctions->pixelStorei(GL_UNPACK_ROW_LENGTH, mUnpackRowLength);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_ROW_LENGTH);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     if (mUnpackSkipRows != skipRows)
@@ -530,7 +530,7 @@
         mUnpackSkipRows = skipRows;
         mFunctions->pixelStorei(GL_UNPACK_SKIP_ROWS, mUnpackSkipRows);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_SKIP_ROWS);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     if (mUnpackSkipPixels != skipPixels)
@@ -538,7 +538,7 @@
         mUnpackSkipPixels = skipPixels;
         mFunctions->pixelStorei(GL_UNPACK_SKIP_PIXELS, mUnpackSkipPixels);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_SKIP_PIXELS);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     if (mUnpackImageHeight != imageHeight)
@@ -546,7 +546,7 @@
         mUnpackImageHeight = imageHeight;
         mFunctions->pixelStorei(GL_UNPACK_IMAGE_HEIGHT, mUnpackImageHeight);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_IMAGE_HEIGHT);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     if (mUnpackSkipImages != skipImages)
@@ -554,7 +554,7 @@
         mUnpackSkipImages = skipImages;
         mFunctions->pixelStorei(GL_UNPACK_SKIP_IMAGES, mUnpackSkipImages);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_SKIP_IMAGES);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE);
     }
 
     bindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
@@ -582,7 +582,7 @@
         mPackAlignment = alignment;
         mFunctions->pixelStorei(GL_PACK_ALIGNMENT, mPackAlignment);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_ALIGNMENT);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE);
     }
 
     if (mPackRowLength != rowLength)
@@ -590,7 +590,7 @@
         mPackRowLength = rowLength;
         mFunctions->pixelStorei(GL_PACK_ROW_LENGTH, mPackRowLength);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_ROW_LENGTH);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE);
     }
 
     if (mPackSkipRows != skipRows)
@@ -598,7 +598,7 @@
         mPackSkipRows = skipRows;
         mFunctions->pixelStorei(GL_PACK_SKIP_ROWS, mPackSkipRows);
 
-        // TODO: set dirty bit once one exists
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE);
     }
 
     if (mPackSkipPixels != skipPixels)
@@ -606,7 +606,7 @@
         mPackSkipPixels = skipPixels;
         mFunctions->pixelStorei(GL_PACK_SKIP_PIXELS, mPackSkipPixels);
 
-        // TODO: set dirty bit once one exists
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE);
     }
 
     bindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer);
@@ -1069,7 +1069,8 @@
                 UNREACHABLE();
         }
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_CURRENT_VALUE_0 + index);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_CURRENT_VALUES);
+        mLocalDirtyCurrentValues.set(index);
     }
 }
 
@@ -1362,7 +1363,7 @@
         mSampleMaskValues[maskNumber] = mask;
         mFunctions->sampleMaski(maskNumber, mask);
 
-        mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_0 + maskNumber);
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_MASK);
     }
 }
 
@@ -1673,7 +1674,7 @@
 {
     const gl::State &state = context->getGLState();
 
-    // The the current framebuffer binding sometimes requires resetting the srgb blending
+    // Changing the draw framebuffer binding sometimes requires resetting srgb blending.
     if (glDirtyBits[gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING])
     {
         if (mFunctions->standard == STANDARD_GL_DESKTOP)
@@ -1897,56 +1898,10 @@
             case gl::State::DIRTY_BIT_CLEAR_STENCIL:
                 setClearStencil(state.getStencilClearValue());
                 break;
-            case gl::State::DIRTY_BIT_UNPACK_ALIGNMENT:
-                // TODO(jmadill): split this
+            case gl::State::DIRTY_BIT_UNPACK_STATE:
                 setPixelUnpackState(state.getUnpackState());
                 break;
-            case gl::State::DIRTY_BIT_UNPACK_ROW_LENGTH:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_IMAGE_HEIGHT:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_IMAGES:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_ROWS:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_PIXELS:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_BUFFER_BINDING:
-                // TODO(jmadill): split this
-                setPixelUnpackState(state.getUnpackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_ALIGNMENT:
-                // TODO(jmadill): split this
-                setPixelPackState(state.getPackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_REVERSE_ROW_ORDER:
-                // TODO(jmadill): split this
-                setPixelPackState(state.getPackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_ROW_LENGTH:
-                // TODO(jmadill): split this
-                setPixelPackState(state.getPackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_SKIP_ROWS:
-                // TODO(jmadill): split this
-                setPixelPackState(state.getPackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_SKIP_PIXELS:
-                // TODO(jmadill): split this
-                setPixelPackState(state.getPackState());
-                break;
-            case gl::State::DIRTY_BIT_PACK_BUFFER_BINDING:
-                // TODO(jmadill): split this
+            case gl::State::DIRTY_BIT_PACK_STATE:
                 setPixelPackState(state.getPackState());
                 break;
             case gl::State::DIRTY_BIT_DITHER_ENABLED:
@@ -2025,34 +1980,31 @@
             case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED:
                 setSampleMaskEnabled(state.isSampleMaskEnabled());
                 break;
-            default:
+            case gl::State::DIRTY_BIT_SAMPLE_MASK:
             {
-                if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
-                    dirtyBit < gl::State::DIRTY_BIT_CURRENT_VALUE_MAX)
+                for (GLuint maskNumber = 0; maskNumber < state.getMaxSampleMaskWords();
+                     ++maskNumber)
                 {
-                    size_t attribIndex =
-                        static_cast<size_t>(dirtyBit) - gl::State::DIRTY_BIT_CURRENT_VALUE_0;
+                    setSampleMaski(maskNumber, state.getSampleMaskWord(maskNumber));
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_CURRENT_VALUES:
+            {
+                gl::AttributesMask combinedMask =
+                    (state.getAndResetDirtyCurrentValues() | mLocalDirtyCurrentValues);
+                mLocalDirtyCurrentValues.reset();
+
+                for (auto attribIndex : combinedMask)
+                {
                     setAttributeCurrentData(attribIndex,
                                             state.getVertexAttribCurrentValue(attribIndex));
-                    break;
                 }
-                else if (dirtyBit >= gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_0 &&
-                         dirtyBit < gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_MAX)
-                {
-                    GLuint maskNumber =
-                        static_cast<GLuint>(dirtyBit) - gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_0;
-                    // Only set the available sample mask values.
-                    if (maskNumber < state.getMaxSampleMaskWords())
-                    {
-                        setSampleMaski(maskNumber, state.getSampleMaskWord(maskNumber));
-                    }
-                    break;
-                }
-                else
-                {
-                    UNREACHABLE();
-                }
+                break;
             }
+            default:
+                UNREACHABLE();
+                break;
         }
 
         mLocalDirtyBits.reset();
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.h b/src/libANGLE/renderer/gl/StateManagerGL.h
index d581505..7d5298b 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.h
+++ b/src/libANGLE/renderer/gl/StateManagerGL.h
@@ -353,6 +353,7 @@
     const bool mIsMultiviewEnabled;
 
     gl::State::DirtyBits mLocalDirtyBits;
+    gl::AttributesMask mLocalDirtyCurrentValues;
 
     // ANGLE_multiview dirty bits.
     angle::BitSet<MULTIVIEW_DIRTY_BIT_MAX> mMultiviewDirtyBits;
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index e93309f..20dde85 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -572,9 +572,8 @@
             case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED:
                 WARN() << "DIRTY_BIT_SAMPLE_MASK_ENABLED unimplemented";
                 break;
-            case gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_0:
-            case gl::State::DIRTY_BIT_SAMPLE_MASK_WORD_0 + 1:
-                WARN() << "DIRTY_BIT_SAMPLE_MASK_WORD unimplemented";
+            case gl::State::DIRTY_BIT_SAMPLE_MASK:
+                WARN() << "DIRTY_BIT_SAMPLE_MASK unimplemented";
                 break;
             case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED:
                 WARN() << "DIRTY_BIT_DEPTH_TEST_ENABLED unimplemented";
@@ -638,44 +637,11 @@
             case gl::State::DIRTY_BIT_CLEAR_STENCIL:
                 WARN() << "DIRTY_BIT_CLEAR_STENCIL unimplemented";
                 break;
-            case gl::State::DIRTY_BIT_UNPACK_ALIGNMENT:
-                WARN() << "DIRTY_BIT_UNPACK_ALIGNMENT unimplemented";
+            case gl::State::DIRTY_BIT_UNPACK_STATE:
+                WARN() << "DIRTY_BIT_UNPACK_STATE unimplemented";
                 break;
-            case gl::State::DIRTY_BIT_UNPACK_ROW_LENGTH:
-                WARN() << "DIRTY_BIT_UNPACK_ROW_LENGTH unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_IMAGE_HEIGHT:
-                WARN() << "DIRTY_BIT_UNPACK_IMAGE_HEIGHT unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_IMAGES:
-                WARN() << "DIRTY_BIT_UNPACK_SKIP_IMAGES unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_ROWS:
-                WARN() << "DIRTY_BIT_UNPACK_SKIP_ROWS unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_SKIP_PIXELS:
-                WARN() << "DIRTY_BIT_UNPACK_SKIP_PIXELS unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_UNPACK_BUFFER_BINDING:
-                WARN() << "DIRTY_BIT_UNPACK_BUFFER_BINDING unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_ALIGNMENT:
-                WARN() << "DIRTY_BIT_PACK_ALIGNMENT unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_REVERSE_ROW_ORDER:
-                WARN() << "DIRTY_BIT_PACK_REVERSE_ROW_ORDER unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_ROW_LENGTH:
-                WARN() << "DIRTY_BIT_PACK_ROW_LENGTH unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_SKIP_ROWS:
-                WARN() << "DIRTY_BIT_PACK_SKIP_ROWS unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_SKIP_PIXELS:
-                WARN() << "DIRTY_BIT_PACK_SKIP_PIXELS unimplemented";
-                break;
-            case gl::State::DIRTY_BIT_PACK_BUFFER_BINDING:
-                WARN() << "DIRTY_BIT_PACK_BUFFER_BINDING unimplemented";
+            case gl::State::DIRTY_BIT_PACK_STATE:
+                WARN() << "DIRTY_BIT_PACK_STATE unimplemented";
                 break;
             case gl::State::DIRTY_BIT_DITHER_ENABLED:
                 WARN() << "DIRTY_BIT_DITHER_ENABLED unimplemented";
@@ -745,16 +711,11 @@
             case gl::State::DIRTY_BIT_FRAMEBUFFER_SRGB:
                 WARN() << "DIRTY_BIT_FRAMEBUFFER_SRGB unimplemented";
                 break;
+            case gl::State::DIRTY_BIT_CURRENT_VALUES:
+                WARN() << "DIRTY_BIT_CURRENT_VALUES unimplemented";
+                break;
             default:
-                if (dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
-                    dirtyBit < gl::State::DIRTY_BIT_CURRENT_VALUE_MAX)
-                {
-                    WARN() << "DIRTY_BIT_CURRENT_VALUE unimplemented";
-                }
-                else
-                {
-                    UNREACHABLE();
-                }
+                UNREACHABLE();
                 break;
         }
     }