Use dispatch table to optimize buffer binding.

Using a table of function pointers is faster than using a switch
followed by a function call. Also more aggressively inline binding
methods.

Based on contribution by mtavenrath@nvidia.com.

In total this patch sequence improves the performance of a buffer
binding perf test by up to 27%.

Test: BindingsBenchmark.Run/gl_100_objects_allocated_at_initialization
Bug: angleproject:2891
Change-Id: Iaab1e2a135b635bd72736d7d1d4271562c3a4ece
Reviewed-on: https://chromium-review.googlesource.com/c/1281783
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 0c29a3e..cc22879 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -24,6 +24,7 @@
 #include "libANGLE/formatutils.h"
 #include "libANGLE/queryconversions.h"
 #include "libANGLE/queryutils.h"
+#include "libANGLE/renderer/BufferImpl.h"
 #include "libANGLE/renderer/ContextImpl.h"
 #include "libANGLE/renderer/TextureImpl.h"
 
@@ -32,7 +33,6 @@
 
 namespace
 {
-
 bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType)
 {
     switch (type)
@@ -48,16 +48,49 @@
     }
 }
 
+// Mapping from a buffer binding type to a dirty bit type.
+constexpr angle::PackedEnumMap<BufferBinding, size_t> kBufferBindingDirtyBits = {{
+    0,                                                 /* Array */
+    State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING,    /* AtomicCounter */
+    0,                                                 /* CopyRead */
+    0,                                                 /* CopyWrite */
+    State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING, /* DispatchIndirect */
+    State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING,     /* DrawIndirect */
+    0,                                                 /* ElementArray */
+    State::DIRTY_BIT_PACK_BUFFER_BINDING,              /* PixelPack */
+    State::DIRTY_BIT_UNPACK_BUFFER_BINDING,            /* PixelUnpack */
+    State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING,    /* ShaderStorage */
+    0,                                                 /* TransformFeedback */
+    State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS,          /* Uniform */
+}};
+
+// Returns a buffer binding function depending on if a dirty bit is set.
+template <BufferBinding Target>
+constexpr State::BufferBindingSetter GetBufferBindingSetter()
+{
+    return kBufferBindingDirtyBits[Target] != 0 ? &State::setGenericBufferBindingWithBit<Target>
+                                                : &State::setGenericBufferBinding<Target>;
+}
 }  // anonymous namepace
 
 template <typename BindingT, typename... ArgsT>
-void UpdateNonTFBufferBinding(const Context *context, BindingT *binding, ArgsT... args)
+ANGLE_INLINE void UpdateNonTFBufferBinding(const Context *context,
+                                           BindingT *binding,
+                                           Buffer *buffer,
+                                           ArgsT... args)
 {
-    if (binding->get())
-        (*binding)->onNonTFBindingChanged(context, -1);
-    binding->set(context, args...);
-    if (binding->get())
-        (*binding)->onNonTFBindingChanged(context, 1);
+    Buffer *oldBuffer = binding->get();
+    if (oldBuffer)
+    {
+        oldBuffer->onNonTFBindingChanged(-1);
+        oldBuffer->release(context);
+    }
+    binding->assign(buffer, args...);
+    if (buffer)
+    {
+        buffer->addRef();
+        buffer->onNonTFBindingChanged(1);
+    }
 }
 
 template <typename BindingT, typename... ArgsT>
@@ -102,6 +135,67 @@
     }
 }
 
+// These template functions must be defined before they are instantiated in kBufferSetters.
+template <BufferBinding Target>
+void State::setGenericBufferBindingWithBit(const Context *context, Buffer *buffer)
+{
+    UpdateNonTFBufferBinding(context, &mBoundBuffers[Target], buffer);
+    mDirtyBits.set(kBufferBindingDirtyBits[Target]);
+}
+
+template <BufferBinding Target>
+void State::setGenericBufferBinding(const Context *context, Buffer *buffer)
+{
+    UpdateNonTFBufferBinding(context, &mBoundBuffers[Target], buffer);
+}
+
+template <>
+void State::setGenericBufferBinding<BufferBinding::TransformFeedback>(const Context *context,
+                                                                      Buffer *buffer)
+{
+    UpdateTFBufferBinding(context, &mBoundBuffers[BufferBinding::TransformFeedback], false, buffer);
+}
+
+template <>
+void State::setGenericBufferBinding<BufferBinding::ElementArray>(const Context *context,
+                                                                 Buffer *buffer)
+{
+    Buffer *oldBuffer = mVertexArray->mState.mElementArrayBuffer.get();
+    if (oldBuffer)
+    {
+        oldBuffer->onNonTFBindingChanged(-1);
+        oldBuffer->release(context);
+    }
+    mVertexArray->mState.mElementArrayBuffer.assign(buffer);
+    if (buffer)
+    {
+        mVertexArray->mElementArrayBufferObserverBinding.bind(buffer->getImplementation());
+        buffer->onNonTFBindingChanged(1);
+        buffer->addRef();
+    }
+    else
+    {
+        mVertexArray->mElementArrayBufferObserverBinding.bind(nullptr);
+    }
+    mVertexArray->mDirtyBits.set(VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
+    mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
+}
+
+const angle::PackedEnumMap<BufferBinding, State::BufferBindingSetter> State::kBufferSetters = {{
+    GetBufferBindingSetter<BufferBinding::Array>(),
+    GetBufferBindingSetter<BufferBinding::AtomicCounter>(),
+    GetBufferBindingSetter<BufferBinding::CopyRead>(),
+    GetBufferBindingSetter<BufferBinding::CopyWrite>(),
+    GetBufferBindingSetter<BufferBinding::DispatchIndirect>(),
+    GetBufferBindingSetter<BufferBinding::DrawIndirect>(),
+    GetBufferBindingSetter<BufferBinding::ElementArray>(),
+    GetBufferBindingSetter<BufferBinding::PixelPack>(),
+    GetBufferBindingSetter<BufferBinding::PixelUnpack>(),
+    GetBufferBindingSetter<BufferBinding::ShaderStorage>(),
+    GetBufferBindingSetter<BufferBinding::TransformFeedback>(),
+    GetBufferBindingSetter<BufferBinding::Uniform>(),
+}};
+
 State::State(bool debug,
              bool bindGeneratesResource,
              bool clientArraysEnabled,
@@ -1493,48 +1587,6 @@
     return mActiveQueries[type].get();
 }
 
-void State::setBufferBinding(const Context *context, BufferBinding target, Buffer *buffer)
-{
-    switch (target)
-    {
-        case BufferBinding::PixelPack:
-            UpdateNonTFBufferBinding(context, &mBoundBuffers[target], buffer);
-            mDirtyBits.set(DIRTY_BIT_PACK_BUFFER_BINDING);
-            break;
-        case BufferBinding::PixelUnpack:
-            UpdateNonTFBufferBinding(context, &mBoundBuffers[target], buffer);
-            mDirtyBits.set(DIRTY_BIT_UNPACK_BUFFER_BINDING);
-            break;
-        case BufferBinding::DrawIndirect:
-            UpdateNonTFBufferBinding(context, &mBoundBuffers[target], buffer);
-            mDirtyBits.set(DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING);
-            break;
-        case BufferBinding::DispatchIndirect:
-            UpdateNonTFBufferBinding(context, &mBoundBuffers[target], buffer);
-            mDirtyBits.set(DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING);
-            break;
-        case BufferBinding::ElementArray:
-            getVertexArray()->setElementArrayBuffer(context, buffer);
-            mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
-            break;
-        case BufferBinding::ShaderStorage:
-            UpdateNonTFBufferBinding(context, &mBoundBuffers[target], buffer);
-            mDirtyBits.set(DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING);
-            break;
-        case BufferBinding::Uniform:
-            UpdateBufferBinding(context, &mBoundBuffers[target], buffer, target);
-            mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFER_BINDINGS);
-            break;
-        case BufferBinding::AtomicCounter:
-            UpdateBufferBinding(context, &mBoundBuffers[target], buffer, target);
-            mDirtyBits.set(DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING);
-            break;
-        default:
-            UpdateBufferBinding(context, &mBoundBuffers[target], buffer, target);
-            break;
-    }
-}
-
 void State::setIndexedBufferBinding(const Context *context,
                                     BufferBinding target,
                                     GLuint index,