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,