Commit ca8eda41 by Jamie Madill Committed by Commit Bot

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: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent e4c64c3d
...@@ -116,7 +116,7 @@ class PackedEnumMap ...@@ -116,7 +116,7 @@ class PackedEnumMap
return mPrivateData[static_cast<UnderlyingType>(n)]; return mPrivateData[static_cast<UnderlyingType>(n)];
} }
const_reference operator[](E n) const constexpr const_reference operator[](E n) const
{ {
ASSERT(static_cast<size_t>(n) < mPrivateData.size()); ASSERT(static_cast<size_t>(n) < mPrivateData.size());
return mPrivateData[static_cast<UnderlyingType>(n)]; return mPrivateData[static_cast<UnderlyingType>(n)];
......
...@@ -119,7 +119,7 @@ class Buffer final : public RefCountObject, public LabeledObject ...@@ -119,7 +119,7 @@ class Buffer final : public RefCountObject, public LabeledObject
bool isBound() const; bool isBound() const;
bool isBoundForTransformFeedbackAndOtherUse() const; bool isBoundForTransformFeedbackAndOtherUse() const;
void onTFBindingChanged(const Context *context, bool bound, bool indexed); void onTFBindingChanged(const Context *context, bool bound, bool indexed);
void onNonTFBindingChanged(const Context *context, int incr) { mState.mBindingCount += incr; } void onNonTFBindingChanged(int incr) { mState.mBindingCount += incr; }
private: private:
BufferState mState; BufferState mState;
......
...@@ -110,6 +110,8 @@ class BindingPointer ...@@ -110,6 +110,8 @@ class BindingPointer
} }
} }
void assign(ObjectType *object) { mObject = object; }
ObjectType *get() const { return mObject; } ObjectType *get() const { return mObject; }
ObjectType *operator->() const { return mObject; } ObjectType *operator->() const { return mObject; }
...@@ -201,6 +203,13 @@ class OffsetBindingPointer : public gl::BindingPointer<ObjectType> ...@@ -201,6 +203,13 @@ class OffsetBindingPointer : public gl::BindingPointer<ObjectType>
return !(*this == other); return !(*this == other);
} }
void assign(ObjectType *object, GLintptr offset, GLsizeiptr size)
{
BindingPointer<ObjectType>::assign(object);
mOffset = offset;
mSize = size;
}
private: private:
GLintptr mOffset; GLintptr mOffset;
GLsizeiptr mSize; GLsizeiptr mSize;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/queryconversions.h" #include "libANGLE/queryconversions.h"
#include "libANGLE/queryutils.h" #include "libANGLE/queryutils.h"
#include "libANGLE/renderer/BufferImpl.h"
#include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/TextureImpl.h" #include "libANGLE/renderer/TextureImpl.h"
...@@ -32,7 +33,6 @@ namespace gl ...@@ -32,7 +33,6 @@ namespace gl
namespace namespace
{ {
bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType) bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType)
{ {
switch (type) switch (type)
...@@ -48,16 +48,49 @@ bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType) ...@@ -48,16 +48,49 @@ bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType)
} }
} }
// 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 } // anonymous namepace
template <typename BindingT, typename... ArgsT> 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()) Buffer *oldBuffer = binding->get();
(*binding)->onNonTFBindingChanged(context, -1); if (oldBuffer)
binding->set(context, args...); {
if (binding->get()) oldBuffer->onNonTFBindingChanged(-1);
(*binding)->onNonTFBindingChanged(context, 1); oldBuffer->release(context);
}
binding->assign(buffer, args...);
if (buffer)
{
buffer->addRef();
buffer->onNonTFBindingChanged(1);
}
} }
template <typename BindingT, typename... ArgsT> template <typename BindingT, typename... ArgsT>
...@@ -102,6 +135,67 @@ void UpdateIndexedBufferBinding(const Context *context, ...@@ -102,6 +135,67 @@ void UpdateIndexedBufferBinding(const Context *context,
} }
} }
// 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, State::State(bool debug,
bool bindGeneratesResource, bool bindGeneratesResource,
bool clientArraysEnabled, bool clientArraysEnabled,
...@@ -1493,48 +1587,6 @@ Query *State::getActiveQuery(QueryType type) const ...@@ -1493,48 +1587,6 @@ Query *State::getActiveQuery(QueryType type) const
return mActiveQueries[type].get(); 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, void State::setIndexedBufferBinding(const Context *context,
BufferBinding target, BufferBinding target,
GLuint index, GLuint index,
......
...@@ -268,7 +268,11 @@ class State : angle::NonCopyable ...@@ -268,7 +268,11 @@ class State : angle::NonCopyable
void detachProgramPipeline(const Context *context, GLuint pipeline); void detachProgramPipeline(const Context *context, GLuint pipeline);
//// Typed buffer binding point manipulation //// //// Typed buffer binding point manipulation ////
void setBufferBinding(const Context *context, BufferBinding target, Buffer *buffer); ANGLE_INLINE void setBufferBinding(const Context *context, BufferBinding target, Buffer *buffer)
{
(this->*(kBufferSetters[target]))(context, buffer);
}
Buffer *getTargetBuffer(BufferBinding target) const; Buffer *getTargetBuffer(BufferBinding target) const;
void setIndexedBufferBinding(const Context *context, void setIndexedBufferBinding(const Context *context,
BufferBinding target, BufferBinding target,
...@@ -516,6 +520,16 @@ class State : angle::NonCopyable ...@@ -516,6 +520,16 @@ class State : angle::NonCopyable
GLES1State &gles1() { return mGLES1State; } GLES1State &gles1() { return mGLES1State; }
const GLES1State &gles1() const { return mGLES1State; } const GLES1State &gles1() const { return mGLES1State; }
// Helpers for setting bound buffers. They should all have the same signature.
// Not meant to be called externally. Used for local helpers in State.cpp.
template <BufferBinding Target>
void setGenericBufferBindingWithBit(const Context *context, Buffer *buffer);
template <BufferBinding Target>
void setGenericBufferBinding(const Context *context, Buffer *buffer);
using BufferBindingSetter = void (State::*)(const Context *, Buffer *);
private: private:
void syncSamplers(const Context *context); void syncSamplers(const Context *context);
angle::Result syncProgramTextures(const Context *context); angle::Result syncProgramTextures(const Context *context);
...@@ -524,6 +538,9 @@ class State : angle::NonCopyable ...@@ -524,6 +538,9 @@ class State : angle::NonCopyable
size_t textureIndex, size_t textureIndex,
Texture *texture); Texture *texture);
// Dispatch table for buffer update functions.
static const angle::PackedEnumMap<BufferBinding, BufferBindingSetter> kBufferSetters;
// Cached values from Context's caps // Cached values from Context's caps
GLuint mMaxDrawBuffers; GLuint mMaxDrawBuffers;
GLuint mMaxCombinedTextureImageUnits; GLuint mMaxCombinedTextureImageUnits;
......
...@@ -107,7 +107,7 @@ void VertexArray::onDestroy(const Context *context) ...@@ -107,7 +107,7 @@ void VertexArray::onDestroy(const Context *context)
binding.setBuffer(context, nullptr, isBound); binding.setBuffer(context, nullptr, isBound);
} }
if (isBound && mState.mElementArrayBuffer.get()) if (isBound && mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1); mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
mState.mElementArrayBuffer.set(context, nullptr); mState.mElementArrayBuffer.set(context, nullptr);
mVertexArray->destroy(context); mVertexArray->destroy(context);
SafeDelete(mVertexArray); SafeDelete(mVertexArray);
...@@ -148,7 +148,7 @@ void VertexArray::detachBuffer(const Context *context, GLuint bufferName) ...@@ -148,7 +148,7 @@ void VertexArray::detachBuffer(const Context *context, GLuint bufferName)
if (mState.mElementArrayBuffer.id() == bufferName) if (mState.mElementArrayBuffer.id() == bufferName)
{ {
if (isBound && mState.mElementArrayBuffer.get()) if (isBound && mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1); mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
mState.mElementArrayBuffer.set(context, nullptr); mState.mElementArrayBuffer.set(context, nullptr);
} }
} }
...@@ -357,26 +357,6 @@ void VertexArray::setVertexAttribPointer(const Context *context, ...@@ -357,26 +357,6 @@ void VertexArray::setVertexAttribPointer(const Context *context,
boundBuffer == nullptr && pointer == nullptr); boundBuffer == nullptr && pointer == nullptr);
} }
void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer)
{
ASSERT(context->isCurrentVertexArray(this));
if (mState.mElementArrayBuffer.get())
{
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1);
}
mState.mElementArrayBuffer.set(context, buffer);
if (buffer)
{
mElementArrayBufferObserverBinding.bind(buffer->getImplementation());
buffer->onNonTFBindingChanged(context, 1);
}
else
{
mElementArrayBufferObserverBinding.bind(nullptr);
}
mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
}
angle::Result VertexArray::syncState(const Context *context) angle::Result VertexArray::syncState(const Context *context)
{ {
if (mDirtyBits.any()) if (mDirtyBits.any())
...@@ -400,7 +380,7 @@ angle::Result VertexArray::syncState(const Context *context) ...@@ -400,7 +380,7 @@ angle::Result VertexArray::syncState(const Context *context)
void VertexArray::onBindingChanged(const Context *context, int incr) void VertexArray::onBindingChanged(const Context *context, int incr)
{ {
if (mState.mElementArrayBuffer.get()) if (mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, incr); mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
for (auto &binding : mState.mVertexBindings) for (auto &binding : mState.mVertexBindings)
{ {
binding.onContainerBindingChanged(context, incr); binding.onContainerBindingChanged(context, incr);
......
...@@ -80,6 +80,9 @@ class VertexArrayState final : angle::NonCopyable ...@@ -80,6 +80,9 @@ class VertexArrayState final : angle::NonCopyable
AttributesMask mEnabledAttributesMask; AttributesMask mEnabledAttributesMask;
ComponentTypeMask mVertexAttributesTypeMask; ComponentTypeMask mVertexAttributesTypeMask;
// This is a performance optimization for buffer binding. Allows element array buffer updates.
friend class State;
// From the GLES 3.1 spec: // From the GLES 3.1 spec:
// When a generic attribute array is sourced from client memory, the vertex attribute binding // When a generic attribute array is sourced from client memory, the vertex attribute binding
// state is ignored. Thus we don't have to worry about binding state when using client memory // state is ignored. Thus we don't have to worry about binding state when using client memory
...@@ -150,8 +153,6 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -150,8 +153,6 @@ class VertexArray final : public angle::ObserverInterface,
GLintptr offset, GLintptr offset,
GLsizei stride); GLsizei stride);
void setElementArrayBuffer(const Context *context, Buffer *buffer);
const BindingPointer<Buffer> &getElementArrayBuffer() const const BindingPointer<Buffer> &getElementArrayBuffer() const
{ {
return mState.getElementArrayBuffer(); return mState.getElementArrayBuffer();
...@@ -260,6 +261,9 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -260,6 +261,9 @@ class VertexArray final : public angle::ObserverInterface,
private: private:
~VertexArray() override; ~VertexArray() override;
// This is a performance optimization for buffer binding. Allows element array buffer updates.
friend class State;
void setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit); void setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit);
void setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit); void setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit);
......
...@@ -49,9 +49,9 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool ...@@ -49,9 +49,9 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool
if (containerIsBound) if (containerIsBound)
{ {
if (mBuffer.get()) if (mBuffer.get())
mBuffer->onNonTFBindingChanged(context, -1); mBuffer->onNonTFBindingChanged(-1);
if (bufferIn) if (bufferIn)
bufferIn->onNonTFBindingChanged(context, 1); bufferIn->onNonTFBindingChanged(1);
} }
mBuffer.set(context, bufferIn); mBuffer.set(context, bufferIn);
} }
...@@ -59,7 +59,7 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool ...@@ -59,7 +59,7 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool
void VertexBinding::onContainerBindingChanged(const Context *context, int incr) const void VertexBinding::onContainerBindingChanged(const Context *context, int incr) const
{ {
if (mBuffer.get()) if (mBuffer.get())
mBuffer->onNonTFBindingChanged(context, incr); mBuffer->onNonTFBindingChanged(incr);
} }
VertexAttribute::VertexAttribute(GLuint bindingIndex) VertexAttribute::VertexAttribute(GLuint bindingIndex)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment