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
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());
return mPrivateData[static_cast<UnderlyingType>(n)];
......
......@@ -119,7 +119,7 @@ class Buffer final : public RefCountObject, public LabeledObject
bool isBound() const;
bool isBoundForTransformFeedbackAndOtherUse() const;
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:
BufferState mState;
......
......@@ -110,6 +110,8 @@ class BindingPointer
}
}
void assign(ObjectType *object) { mObject = object; }
ObjectType *get() const { return mObject; }
ObjectType *operator->() const { return mObject; }
......@@ -201,6 +203,13 @@ class OffsetBindingPointer : public gl::BindingPointer<ObjectType>
return !(*this == other);
}
void assign(ObjectType *object, GLintptr offset, GLsizeiptr size)
{
BindingPointer<ObjectType>::assign(object);
mOffset = offset;
mSize = size;
}
private:
GLintptr mOffset;
GLsizeiptr mSize;
......
......@@ -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 gl
namespace
{
bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType)
{
switch (type)
......@@ -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
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 @@ 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,
bool bindGeneratesResource,
bool clientArraysEnabled,
......@@ -1493,48 +1587,6 @@ Query *State::getActiveQuery(QueryType type) const
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,
......
......@@ -268,7 +268,11 @@ class State : angle::NonCopyable
void detachProgramPipeline(const Context *context, GLuint pipeline);
//// 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;
void setIndexedBufferBinding(const Context *context,
BufferBinding target,
......@@ -516,6 +520,16 @@ class State : angle::NonCopyable
GLES1State &gles1() { 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:
void syncSamplers(const Context *context);
angle::Result syncProgramTextures(const Context *context);
......@@ -524,6 +538,9 @@ class State : angle::NonCopyable
size_t textureIndex,
Texture *texture);
// Dispatch table for buffer update functions.
static const angle::PackedEnumMap<BufferBinding, BufferBindingSetter> kBufferSetters;
// Cached values from Context's caps
GLuint mMaxDrawBuffers;
GLuint mMaxCombinedTextureImageUnits;
......
......@@ -107,7 +107,7 @@ void VertexArray::onDestroy(const Context *context)
binding.setBuffer(context, nullptr, isBound);
}
if (isBound && mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1);
mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
mState.mElementArrayBuffer.set(context, nullptr);
mVertexArray->destroy(context);
SafeDelete(mVertexArray);
......@@ -148,7 +148,7 @@ void VertexArray::detachBuffer(const Context *context, GLuint bufferName)
if (mState.mElementArrayBuffer.id() == bufferName)
{
if (isBound && mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1);
mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
mState.mElementArrayBuffer.set(context, nullptr);
}
}
......@@ -357,26 +357,6 @@ void VertexArray::setVertexAttribPointer(const Context *context,
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)
{
if (mDirtyBits.any())
......@@ -400,7 +380,7 @@ angle::Result VertexArray::syncState(const Context *context)
void VertexArray::onBindingChanged(const Context *context, int incr)
{
if (mState.mElementArrayBuffer.get())
mState.mElementArrayBuffer->onNonTFBindingChanged(context, incr);
mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
for (auto &binding : mState.mVertexBindings)
{
binding.onContainerBindingChanged(context, incr);
......
......@@ -80,6 +80,9 @@ class VertexArrayState final : angle::NonCopyable
AttributesMask mEnabledAttributesMask;
ComponentTypeMask mVertexAttributesTypeMask;
// This is a performance optimization for buffer binding. Allows element array buffer updates.
friend class State;
// From the GLES 3.1 spec:
// 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
......@@ -150,8 +153,6 @@ class VertexArray final : public angle::ObserverInterface,
GLintptr offset,
GLsizei stride);
void setElementArrayBuffer(const Context *context, Buffer *buffer);
const BindingPointer<Buffer> &getElementArrayBuffer() const
{
return mState.getElementArrayBuffer();
......@@ -260,6 +261,9 @@ class VertexArray final : public angle::ObserverInterface,
private:
~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 setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit);
......
......@@ -49,9 +49,9 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool
if (containerIsBound)
{
if (mBuffer.get())
mBuffer->onNonTFBindingChanged(context, -1);
mBuffer->onNonTFBindingChanged(-1);
if (bufferIn)
bufferIn->onNonTFBindingChanged(context, 1);
bufferIn->onNonTFBindingChanged(1);
}
mBuffer.set(context, bufferIn);
}
......@@ -59,7 +59,7 @@ void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool
void VertexBinding::onContainerBindingChanged(const Context *context, int incr) const
{
if (mBuffer.get())
mBuffer->onNonTFBindingChanged(context, incr);
mBuffer->onNonTFBindingChanged(incr);
}
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