Commit c759b8b4 by Jamie Madill Committed by Commit Bot

Vulkan: More Vertex Array optimizations.

Inlines a number of Vulkan vertex array methods. Also changes the way vertex buffers are bound. Note that Vulkan doesn't support NULL buffer bindings. Thus we create an emulated NULL buffer to work around the problem of having gaps in the bound vertex buffers. This allows us to use a single bind call for ranges of vertex buffers even when there are gaps. Also changes how vertex array dirty bits are reset. Instead of calling memset to clear the affected buffers we pass a mutable pointer to the Vertex Array sync state. This allows us to only reset the dirty bits that we sync. This saves on the memory clearing time. Improves perf by about 10% in the Vulkan VBO state change test. Bug: angleproject:3014 Change-Id: Ib7b742dff7897fc891606a652ea0b64255a24c86 Reviewed-on: https://chromium-review.googlesource.com/c/1390360 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 633d5e69
......@@ -252,22 +252,6 @@ GLint64 Buffer::getMemorySize() const
return implSize > 0 ? implSize : mState.mSize;
}
bool Buffer::isBound() const
{
return mState.mBindingCount;
}
bool Buffer::isBoundForTransformFeedbackAndOtherUse() const
{
// The transform feedback generic binding point is not an indexed binding point but it also does
// not count as a non-transform-feedback use of the buffer, so we subtract it from the binding
// count when checking if the buffer is bound to a non-transform-feedback location. See
// https://crbug.com/853978
return mState.mTransformFeedbackIndexedBindingCount > 0 &&
mState.mTransformFeedbackIndexedBindingCount !=
mState.mBindingCount - mState.mTransformFeedbackGenericBindingCount;
}
bool Buffer::isDoubleBoundForTransformFeedback() const
{
return mState.mTransformFeedbackIndexedBindingCount > 1;
......
......@@ -121,8 +121,19 @@ class Buffer final : public RefCountObject,
rx::BufferImpl *getImplementation() const { return mImpl; }
bool isBound() const;
bool isBoundForTransformFeedbackAndOtherUse() const;
ANGLE_INLINE bool isBound() const { return mState.mBindingCount > 0; }
ANGLE_INLINE bool isBoundForTransformFeedbackAndOtherUse() const
{
// The transform feedback generic binding point is not an indexed binding point but it also
// does not count as a non-transform-feedback use of the buffer, so we subtract it from the
// binding count when checking if the buffer is bound to a non-transform-feedback location.
// See https://crbug.com/853978
return mState.mTransformFeedbackIndexedBindingCount > 0 &&
mState.mTransformFeedbackIndexedBindingCount !=
mState.mBindingCount - mState.mTransformFeedbackGenericBindingCount;
}
bool isDoubleBoundForTransformFeedback() const;
void onTFBindingChanged(const Context *context, bool bound, bool indexed);
void onNonTFBindingChanged(int incr) { mState.mBindingCount += incr; }
......
......@@ -8143,7 +8143,6 @@ void StateCache::updateVertexElementLimits(Context *context)
for (size_t attributeIndex : mCachedActiveBufferedAttribsMask)
{
const VertexAttribute &attrib = vertexAttribs[attributeIndex];
ASSERT(attrib.enabled);
const VertexBinding &binding = vertexBindings[attrib.bindingIndex];
ASSERT(context->isGLES1() ||
......
......@@ -122,11 +122,6 @@ VertexArray::~VertexArray()
ASSERT(!mVertexArray);
}
GLuint VertexArray::id() const
{
return mId;
}
void VertexArray::setLabel(const Context *context, const std::string &label)
{
mState.mLabel = label;
......@@ -176,18 +171,51 @@ size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
return (dirtyBit - DIRTY_BIT_ATTRIB_0) % gl::MAX_VERTEX_ATTRIBS;
}
void VertexArray::setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit)
ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
DirtyAttribBitType dirtyAttribBit)
{
mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
}
void VertexArray::setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit)
ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
DirtyBindingBitType dirtyBindingBit)
{
mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
}
ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
{
for (size_t boundAttribute : binding->getBoundAttributesMask())
{
mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
}
}
ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers(VertexBinding *binding)
{
Buffer *buffer = binding->getBuffer().get();
if (buffer && buffer->isMapped())
{
mState.mCachedMappedArrayBuffers |= binding->getBoundAttributesMask();
}
else
{
mState.mCachedMappedArrayBuffers &= ~binding->getBoundAttributesMask();
}
mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
}
ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
const Buffer *buffer)
{
const bool hasConflict = buffer && buffer->isBoundForTransformFeedbackAndOtherUse();
mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
}
void VertexArray::bindVertexBufferImpl(const Context *context,
size_t bindingIndex,
Buffer *boundBuffer,
......@@ -266,12 +294,12 @@ void VertexArray::setVertexBindingDivisor(size_t bindingIndex, GLuint divisor)
}
}
void VertexArray::setVertexAttribFormatImpl(size_t attribIndex,
GLint size,
GLenum type,
bool normalized,
bool pureInteger,
GLuint relativeOffset)
ANGLE_INLINE void VertexArray::setVertexAttribFormatImpl(size_t attribIndex,
GLint size,
GLenum type,
bool normalized,
bool pureInteger,
GLuint relativeOffset)
{
ASSERT(attribIndex < getMaxAttribs());
......@@ -366,16 +394,13 @@ angle::Result VertexArray::syncState(const Context *context)
{
mDirtyBitsGuard = mDirtyBits;
ANGLE_TRY(
mVertexArray->syncState(context, mDirtyBits, mDirtyAttribBits, mDirtyBindingBits));
mVertexArray->syncState(context, mDirtyBits, &mDirtyAttribBits, &mDirtyBindingBits));
mDirtyBits.reset();
mDirtyBitsGuard.reset();
// This is a bit of an implementation hack - but since we know the implementation
// details of the dirty bit class it should always have the same effect as iterating
// individual attribs. We could also look into schemes where iterating the dirty
// bit set also resets it as you pass through it.
memset(&mDirtyAttribBits, 0, sizeof(mDirtyAttribBits));
memset(&mDirtyBindingBits, 0, sizeof(mDirtyBindingBits));
// The dirty bits should be reset in the back-end. To simplify ASSERTs only check attrib 0.
ASSERT(mDirtyAttribBits[0].none());
ASSERT(mDirtyBindingBits[0].none());
}
return angle::Result::Continue;
}
......@@ -474,21 +499,6 @@ void VertexArray::updateObserverBinding(size_t bindingIndex)
mArrayBufferObserverBindings[bindingIndex].bind(boundBuffer);
}
void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
{
for (size_t boundAttribute : binding->getBoundAttributesMask())
{
mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
}
}
void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
const Buffer *buffer)
{
const bool hasConflict = buffer && buffer->isBoundForTransformFeedbackAndOtherUse();
mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
}
bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context) const
{
// Fast check first.
......@@ -512,22 +522,6 @@ bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context
return false;
}
void VertexArray::updateCachedMappedArrayBuffers(VertexBinding *binding)
{
Buffer *buffer = binding->getBuffer().get();
if (buffer && buffer->isMapped())
{
mState.mCachedMappedArrayBuffers |= binding->getBoundAttributesMask();
}
else
{
mState.mCachedMappedArrayBuffers &= ~binding->getBoundAttributesMask();
}
mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
}
angle::Result VertexArray::getIndexRangeImpl(const Context *context,
DrawElementsType type,
GLsizei indexCount,
......
......@@ -104,7 +104,7 @@ class VertexArray final : public angle::ObserverInterface,
void onDestroy(const Context *context);
GLuint id() const;
GLuint id() const { return mId; }
void setLabel(const Context *context, const std::string &label) override;
const std::string &getLabel() const override;
......
......@@ -40,18 +40,6 @@ VertexBinding &VertexBinding::operator=(VertexBinding &&binding)
return *this;
}
void VertexBinding::setBuffer(const gl::Context *context, Buffer *bufferIn, bool containerIsBound)
{
if (containerIsBound)
{
if (mBuffer.get())
mBuffer->onNonTFBindingChanged(-1);
if (bufferIn)
bufferIn->onNonTFBindingChanged(1);
}
mBuffer.set(context, bufferIn);
}
void VertexBinding::onContainerBindingChanged(const Context *context, int incr) const
{
if (mBuffer.get())
......
......@@ -39,7 +39,18 @@ class VertexBinding final : angle::NonCopyable
void setOffset(GLintptr offsetIn) { mOffset = offsetIn; }
const BindingPointer<Buffer> &getBuffer() const { return mBuffer; }
void setBuffer(const gl::Context *context, Buffer *bufferIn, bool containerIsBound);
ANGLE_INLINE void setBuffer(const gl::Context *context, Buffer *bufferIn, bool containerIsBound)
{
if (containerIsBound)
{
if (mBuffer.get())
mBuffer->onNonTFBindingChanged(-1);
if (bufferIn)
bufferIn->onNonTFBindingChanged(1);
}
mBuffer.set(context, bufferIn);
}
void onContainerBindingChanged(const Context *context, int incr) const;
......
......@@ -32,10 +32,13 @@ class VertexArrayImpl : angle::NonCopyable
{
public:
VertexArrayImpl(const gl::VertexArrayState &state) : mState(state) {}
// It's up to the implementation to reset the attrib and binding dirty bits.
// This is faster than the front-end having to clear all the bits after they have been scanned.
virtual angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
return angle::Result::Continue;
}
......
......@@ -34,23 +34,25 @@ void VertexArray11::destroy(const gl::Context *context) {}
// As VertexAttribPointer can modify both attribute and binding, we should also set other attributes
// that are also using this binding dirty.
#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
if (attribBits[INDEX][gl::VertexArray::DirtyAttribBitType::DIRTY_ATTRIB_POINTER]) \
{ \
attributesToUpdate |= mState.getBindingToAttributesMask(INDEX); \
} \
else \
{ \
attributesToUpdate.set(INDEX); \
} \
invalidateVertexBuffer = true; \
#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
if ((*attribBits)[INDEX][gl::VertexArray::DirtyAttribBitType::DIRTY_ATTRIB_POINTER]) \
{ \
attributesToUpdate |= mState.getBindingToAttributesMask(INDEX); \
} \
else \
{ \
attributesToUpdate.set(INDEX); \
} \
invalidateVertexBuffer = true; \
(*attribBits)[INDEX].reset(); \
break;
#define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
attributesToUpdate |= mState.getBindingToAttributesMask(INDEX); \
invalidateVertexBuffer = true; \
(*bindingBits)[INDEX].reset(); \
break;
#define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \
......@@ -64,8 +66,8 @@ void VertexArray11::destroy(const gl::Context *context) {}
angle::Result VertexArray11::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
ASSERT(dirtyBits.any());
......
......@@ -29,8 +29,8 @@ class VertexArray11 : public VertexArrayImpl
// the draw call parameters.
angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
// Applied buffer pointers are updated here.
angle::Result syncStateForDraw(const gl::Context *context,
......
......@@ -25,8 +25,8 @@ class VertexArray9 : public VertexArrayImpl
angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
~VertexArray9() override {}
......@@ -36,15 +36,20 @@ class VertexArray9 : public VertexArrayImpl
Serial mCurrentStateSerial;
};
inline angle::Result VertexArray9::syncState(
const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
inline angle::Result VertexArray9::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
ASSERT(dirtyBits.any());
Renderer9 *renderer = GetImplAs<Context9>(context)->getRenderer();
mCurrentStateSerial = renderer->generateSerial();
// Clear the dirty bits in the back-end here.
memset(attribBits, 0, sizeof(gl::VertexArray::DirtyAttribBitsArray));
memset(bindingBits, 0, sizeof(gl::VertexArray::DirtyBindingBitsArray));
return angle::Result::Continue;
}
} // namespace rx
......
......@@ -654,14 +654,16 @@ void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
}
}
#define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \
case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
syncDirtyAttrib(context, INDEX, attribBits[INDEX]); \
#define ANGLE_DIRTY_ATTRIB_FUNC(INDEX) \
case VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
syncDirtyAttrib(context, INDEX, (*attribBits)[INDEX]); \
(*attribBits)[INDEX].reset(); \
break;
#define ANGLE_DIRTY_BINDING_FUNC(INDEX) \
case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
syncDirtyBinding(context, INDEX, bindingBits[INDEX]); \
#define ANGLE_DIRTY_BINDING_FUNC(INDEX) \
case VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
syncDirtyBinding(context, INDEX, (*bindingBits)[INDEX]); \
(*bindingBits)[INDEX].reset(); \
break;
#define ANGLE_DIRTY_BUFFER_DATA_FUNC(INDEX) \
......@@ -670,8 +672,8 @@ void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
angle::Result VertexArrayGL::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
......
......@@ -50,8 +50,8 @@ class VertexArrayGL : public VertexArrayImpl
angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
void applyNumViewsToDivisor(int numViews);
void applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask);
......
......@@ -99,11 +99,6 @@ bool CommandGraphResource::hasPendingWork(RendererVk *renderer) const
return mStoredQueueSerial == renderer->getCurrentQueueSerial();
}
Serial CommandGraphResource::getStoredQueueSerial() const
{
return mStoredQueueSerial;
}
// RecordableGraphResource implementation.
RecordableGraphResource::RecordableGraphResource(CommandGraphResourceType resourceType)
: CommandGraphResource(resourceType)
......@@ -111,18 +106,6 @@ RecordableGraphResource::RecordableGraphResource(CommandGraphResourceType resour
RecordableGraphResource::~RecordableGraphResource() = default;
void RecordableGraphResource::updateQueueSerial(Serial queueSerial)
{
ASSERT(queueSerial >= mStoredQueueSerial);
if (queueSerial > mStoredQueueSerial)
{
mCurrentWritingNode = nullptr;
mCurrentReadingNodes.clear();
mStoredQueueSerial = queueSerial;
}
}
angle::Result RecordableGraphResource::recordCommands(Context *context,
CommandBuffer **commandBufferOut)
{
......@@ -149,21 +132,6 @@ angle::Result RecordableGraphResource::recordCommands(Context *context,
return angle::Result::Continue;
}
bool RecordableGraphResource::appendToStartedRenderPass(RendererVk *renderer,
CommandBuffer **commandBufferOut)
{
updateQueueSerial(renderer->getCurrentQueueSerial());
if (hasStartedRenderPass())
{
*commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
return true;
}
else
{
return false;
}
}
const gl::Rectangle &RecordableGraphResource::getRenderPassRenderArea() const
{
ASSERT(hasStartedRenderPass());
......@@ -377,15 +345,6 @@ void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
}
// static
void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
CommandGraphNode *afterNode)
{
ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
afterNode->mParents.emplace_back(beforeNode);
beforeNode->setHasChildren();
}
// static
void CommandGraphNode::SetHappensBeforeDependencies(CommandGraphNode **beforeNodes,
size_t beforeNodesCount,
CommandGraphNode *afterNode)
......@@ -426,17 +385,6 @@ void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIn
mQueryIndex = queryIndex;
}
void CommandGraphNode::addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess)
{
mGlobalMemoryBarrierSrcAccess |= srcAccess;
mGlobalMemoryBarrierDstAccess |= dstAccess;
}
void CommandGraphNode::setHasChildren()
{
mHasChildren = true;
}
// Do not call this in anything but testing code, since it's slow.
bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
{
......
......@@ -76,7 +76,13 @@ class CommandGraphNode final : angle::NonCopyable
// Once a node has commands that must happen after it, recording is stopped and the node is
// frozen forever.
static void SetHappensBeforeDependency(CommandGraphNode *beforeNode,
CommandGraphNode *afterNode);
CommandGraphNode *afterNode)
{
ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
afterNode->mParents.emplace_back(beforeNode);
beforeNode->setHasChildren();
}
static void SetHappensBeforeDependencies(CommandGraphNode **beforeNodes,
size_t beforeNodesCount,
CommandGraphNode *afterNode);
......@@ -107,10 +113,14 @@ class CommandGraphNode final : angle::NonCopyable
void setQueryPool(const QueryPool *queryPool, uint32_t queryIndex);
void addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess);
ANGLE_INLINE void addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess)
{
mGlobalMemoryBarrierSrcAccess |= srcAccess;
mGlobalMemoryBarrierDstAccess |= dstAccess;
}
private:
void setHasChildren();
void setHasChildren() { mHasChildren = true; }
// Used for testing only.
bool isChildOf(CommandGraphNode *parent);
......@@ -169,7 +179,7 @@ class CommandGraphResource : angle::NonCopyable
// Get the current queue serial for this resource. Used to release resources, and for
// queries, to know if the queue they are submitted on has finished execution.
Serial getStoredQueueSerial() const;
Serial getStoredQueueSerial() const { return mStoredQueueSerial; }
protected:
explicit CommandGraphResource(CommandGraphResourceType resourceType);
......@@ -198,7 +208,17 @@ class RecordableGraphResource : public CommandGraphResource
// Updates the in-use serial tracked for this resource. Will clear dependencies if the resource
// was not used in this set of command nodes.
void updateQueueSerial(Serial queueSerial);
ANGLE_INLINE void updateQueueSerial(Serial queueSerial)
{
ASSERT(queueSerial >= mStoredQueueSerial);
if (queueSerial > mStoredQueueSerial)
{
mCurrentWritingNode = nullptr;
mCurrentReadingNodes.clear();
mStoredQueueSerial = queueSerial;
}
}
// Allocates a write node via getNewWriteNode and returns a started command buffer.
// The started command buffer will render outside of a RenderPass.
......@@ -216,7 +236,20 @@ class RecordableGraphResource : public CommandGraphResource
// Checks if we're in a RenderPass, returning true if so. Updates serial internally.
// Returns the started command buffer in commandBufferOut.
bool appendToStartedRenderPass(RendererVk *renderer, CommandBuffer **commandBufferOut);
ANGLE_INLINE bool appendToStartedRenderPass(Serial currentQueueSerial,
CommandBuffer **commandBufferOut)
{
updateQueueSerial(currentQueueSerial);
if (hasStartedRenderPass())
{
*commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
return true;
}
else
{
return false;
}
}
// Accessor for RenderPass RenderArea.
const gl::Rectangle &getRenderPassRenderArea() const;
......@@ -236,7 +269,7 @@ class RecordableGraphResource : public CommandGraphResource
private:
// Returns true if this node has a current writing node with no children.
bool hasChildlessWritingNode() const
ANGLE_INLINE bool hasChildlessWritingNode() const
{
// Note: currently, we don't have a resource that can issue both generic and special
// commands. We don't create read/write dependencies between mixed generic/special
......
......@@ -52,45 +52,6 @@ GLenum DefaultGLErrorCode(VkResult result)
}
}
void BindNonNullVertexBufferRanges(vk::CommandBuffer *commandBuffer,
const gl::AttributesMask &nonNullAttribMask,
uint32_t maxAttrib,
const gl::AttribArray<VkBuffer> &arrayBufferHandles,
const gl::AttribArray<VkDeviceSize> &arrayBufferOffsets)
{
// Vulkan does not allow binding a null vertex buffer but the default state of null buffers is
// valid.
// We can detect if there are no gaps in active attributes by using the mask of the program
// attribs and the max enabled attrib.
ASSERT(maxAttrib > 0);
if (nonNullAttribMask.to_ulong() == (maxAttrib - 1))
{
commandBuffer->bindVertexBuffers(0, maxAttrib, arrayBufferHandles.data(),
arrayBufferOffsets.data());
return;
}
// Find ranges of non-null buffers and bind them all together.
for (uint32_t attribIdx = 0; attribIdx < maxAttrib; attribIdx++)
{
if (arrayBufferHandles[attribIdx] != VK_NULL_HANDLE)
{
// Find the end of this range of non-null handles
uint32_t rangeCount = 1;
while (attribIdx + rangeCount < maxAttrib &&
arrayBufferHandles[attribIdx + rangeCount] != VK_NULL_HANDLE)
{
rangeCount++;
}
commandBuffer->bindVertexBuffers(attribIdx, rangeCount, &arrayBufferHandles[attribIdx],
&arrayBufferOffsets[attribIdx]);
attribIdx += rangeCount;
}
}
}
constexpr VkColorComponentFlags kAllColorChannelsMask =
(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT);
......@@ -251,7 +212,8 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
mGraphicsPipelineDesc->updateTopology(&mGraphicsPipelineTransition, mCurrentDrawMode);
}
if (!mDrawFramebuffer->appendToStartedRenderPass(mRenderer, commandBufferOut))
if (!mDrawFramebuffer->appendToStartedRenderPass(mRenderer->getCurrentQueueSerial(),
commandBufferOut))
{
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, commandBufferOut));
mDirtyBits |= mNewCommandBufferDirtyBits;
......@@ -278,10 +240,11 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
// Flush any relevant dirty bits.
for (size_t dirtyBit : dirtyBits)
{
mDirtyBits.reset(dirtyBit);
ANGLE_TRY((this->*mDirtyBitHandlers[dirtyBit])(context, *commandBufferOut));
}
mDirtyBits &= ~dirtyBitMask;
return angle::Result::Continue;
}
......@@ -410,13 +373,15 @@ angle::Result ContextVk::handleDirtyTextures(const gl::Context *context,
angle::Result ContextVk::handleDirtyVertexBuffers(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
BindNonNullVertexBufferRanges(
commandBuffer, mProgram->getState().getActiveAttribLocationsMask(),
mProgram->getState().getMaxActiveAttribLocation(),
mVertexArray->getCurrentArrayBufferHandles(), mVertexArray->getCurrentArrayBufferOffsets());
uint32_t maxAttrib = mProgram->getState().getMaxActiveAttribLocation();
const gl::AttribArray<VkBuffer> &bufferHandles = mVertexArray->getCurrentArrayBufferHandles();
const gl::AttribArray<VkDeviceSize> &bufferOffsets =
mVertexArray->getCurrentArrayBufferOffsets();
const auto &arrayBufferResources = mVertexArray->getCurrentArrayBuffers();
commandBuffer->bindVertexBuffers(0, maxAttrib, bufferHandles.data(), bufferOffsets.data());
const gl::AttribArray<vk::BufferHelper *> &arrayBufferResources =
mVertexArray->getCurrentArrayBuffers();
vk::FramebufferHelper *framebuffer = mDrawFramebuffer->getFramebuffer();
for (size_t attribIndex : context->getStateCache().getActiveBufferedAttribsMask())
......@@ -1023,7 +988,7 @@ BufferImpl *ContextVk::createBuffer(const gl::BufferState &state)
VertexArrayImpl *ContextVk::createVertexArray(const gl::VertexArrayState &state)
{
return new VertexArrayVk(state, mRenderer);
return new VertexArrayVk(this, state);
}
QueryImpl *ContextVk::createQuery(gl::QueryType type)
......
......@@ -1030,7 +1030,7 @@ angle::Result FramebufferVk::getCommandBufferForDraw(ContextVk *contextVk,
RendererVk *renderer = contextVk->getRenderer();
// This will clear the current write operation if it is complete.
if (appendToStartedRenderPass(renderer, commandBufferOut))
if (appendToStartedRenderPass(renderer->getCurrentQueueSerial(), commandBufferOut))
{
*modeOut = vk::RecordingMode::Append;
return angle::Result::Continue;
......
......@@ -106,9 +106,9 @@ class FramebufferVk : public FramebufferImpl
RenderTargetVk *getColorReadRenderTarget() const;
// This will clear the current write operation if it is complete.
bool appendToStartedRenderPass(RendererVk *renderer, vk::CommandBuffer **commandBufferOut)
bool appendToStartedRenderPass(Serial currentQueueSerial, vk::CommandBuffer **commandBufferOut)
{
return mFramebuffer.appendToStartedRenderPass(renderer, commandBufferOut);
return mFramebuffer.appendToStartedRenderPass(currentQueueSerial, commandBufferOut);
}
vk::FramebufferHelper *getFramebuffer() { return &mFramebuffer; }
......
......@@ -614,7 +614,7 @@ angle::Result UtilsVk::clearImage(ContextVk *contextVk,
ANGLE_TRY(ensureImageClearResourcesInitialized(contextVk));
vk::CommandBuffer *commandBuffer;
if (!framebuffer->appendToStartedRenderPass(contextVk->getRenderer(), &commandBuffer))
if (!framebuffer->appendToStartedRenderPass(renderer->getCurrentQueueSerial(), &commandBuffer))
{
ANGLE_TRY(framebuffer->startNewRenderPass(contextVk, &commandBuffer))
}
......
......@@ -95,7 +95,7 @@ size_t GetVertexCount(BufferVk *srcBuffer, const gl::VertexBinding &binding, uin
kVertexBufferUsageFlags, 1024 * 8, true \
}
VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state, RendererVk *renderer)
VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
: VertexArrayImpl(state),
mCurrentArrayBufferHandles{},
mCurrentArrayBufferOffsets{},
......@@ -126,10 +126,18 @@ VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state, RendererVk *rend
mDynamicVertexData(kVertexBufferUsageFlags, kDynamicVertexDataSize, true),
mDynamicIndexData(kIndexBufferUsageFlags, kDynamicIndexDataSize, true),
mTranslatedByteIndexData(kIndexBufferUsageFlags, kDynamicIndexDataSize, true),
mLineLoopHelper(renderer),
mLineLoopHelper(contextVk->getRenderer()),
mDirtyLineLoopTranslation(true)
{
mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
RendererVk *renderer = contextVk->getRenderer();
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = 16;
createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
(void)mTheNullBuffer.init(contextVk, createInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
mCurrentArrayBufferHandles.fill(mTheNullBuffer.getBuffer().getHandle());
mCurrentArrayBufferOffsets.fill(0);
mCurrentArrayBuffers.fill(nullptr);
......@@ -148,6 +156,8 @@ void VertexArrayVk::destroy(const gl::Context *context)
{
RendererVk *renderer = vk::GetImpl(context)->getRenderer();
mTheNullBuffer.release(renderer);
for (vk::DynamicBuffer &buffer : mCurrentArrayBufferConversion)
{
buffer.release(renderer);
......@@ -286,7 +296,7 @@ angle::Result VertexArrayVk::convertVertexBufferCpu(ContextVk *contextVk,
return angle::Result::Continue;
}
void VertexArrayVk::ensureConversionReleased(RendererVk *renderer, size_t attribIndex)
ANGLE_INLINE void VertexArrayVk::ensureConversionReleased(RendererVk *renderer, size_t attribIndex)
{
if (mCurrentArrayBufferConversionCanRelease[attribIndex])
{
......@@ -297,8 +307,8 @@ void VertexArrayVk::ensureConversionReleased(RendererVk *renderer, size_t attrib
angle::Result VertexArrayVk::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
ASSERT(dirtyBits.any());
......@@ -347,6 +357,7 @@ angle::Result VertexArrayVk::syncState(const gl::Context *context,
ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
bindings[attribs[INDEX].bindingIndex], INDEX)); \
invalidateContext = true; \
(*attribBits)[INDEX].reset(); \
break;
ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC);
......@@ -356,6 +367,7 @@ angle::Result VertexArrayVk::syncState(const gl::Context *context,
ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
bindings[attribs[INDEX].bindingIndex], INDEX)); \
invalidateContext = true; \
(*bindingBits)[INDEX].reset(); \
break;
ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC);
......@@ -404,7 +416,6 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
size_t attribIndex)
{
RendererVk *renderer = contextVk->getRenderer();
bool releaseConversion = true;
bool anyVertexBufferConvertedOnGpu = false;
if (attrib.enabled)
......@@ -436,8 +447,6 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
// http://anglebug.com/3009
ANGLE_TRY(convertVertexBufferCpu(contextVk, bufferVk, binding, attribIndex));
}
releaseConversion = false;
}
else
{
......@@ -446,15 +455,17 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
bufferVk->getBuffer().getBuffer().getHandle();
mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset();
mCurrentArrayBufferStrides[attribIndex] = binding.getStride();
ensureConversionReleased(renderer, attribIndex);
}
}
else
{
mCurrentArrayBuffers[attribIndex] = nullptr;
mCurrentArrayBufferHandles[attribIndex] = VK_NULL_HANDLE;
mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
mCurrentArrayBufferOffsets[attribIndex] = 0;
mCurrentArrayBufferStrides[attribIndex] =
mCurrentArrayBufferFormats[attribIndex]->bufferFormat().pixelBytes;
ensureConversionReleased(renderer, attribIndex);
}
setPackedInputInfo(contextVk, attribIndex, attrib, binding);
......@@ -465,13 +476,14 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
// These will be filled out by the ContextVk.
mCurrentArrayBuffers[attribIndex] = nullptr;
mCurrentArrayBufferHandles[attribIndex] = VK_NULL_HANDLE;
mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
mCurrentArrayBufferOffsets[attribIndex] = 0;
mCurrentArrayBufferStrides[attribIndex] = 0;
mCurrentArrayBufferFormats[attribIndex] =
&renderer->getFormat(angle::FormatID::R32G32B32A32_FLOAT);
setDefaultPackedInput(contextVk, attribIndex);
ensureConversionReleased(renderer, attribIndex);
}
if (anyVertexBufferConvertedOnGpu && renderer->getFeatures().flushAfterVertexConversion)
......@@ -479,9 +491,6 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
ANGLE_TRY(renderer->flush(contextVk));
}
if (releaseConversion)
ensureConversionReleased(renderer, attribIndex);
return angle::Result::Continue;
}
......
......@@ -26,15 +26,15 @@ class RecordableGraphResource;
class VertexArrayVk : public VertexArrayImpl
{
public:
VertexArrayVk(const gl::VertexArrayState &state, RendererVk *renderer);
VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state);
~VertexArrayVk() override;
void destroy(const gl::Context *context) override;
angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
void updateDefaultAttrib(ContextVk *contextVk,
size_t attribIndex,
......@@ -130,6 +130,9 @@ class VertexArrayVk : public VertexArrayImpl
Optional<GLint> mLineLoopBufferFirstIndex;
Optional<size_t> mLineLoopBufferLastIndex;
bool mDirtyLineLoopTranslation;
// Vulkan does not allow binding a null vertex buffer. We use a dummy as a placeholder.
vk::BufferHelper mTheNullBuffer;
};
} // namespace rx
......
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