Commit fa03eb9d by Brandon Schade Committed by Commit Bot

Vulkan: Use DynamicBuffer for BufferVk

When there is glBufferData API call, if the current VkBuffer is in use, we copy to a staging buffer to be gpu copied later. Instead of doing this second copy, write directly to a new buffer that will be used for subsequent gpu operations. BufferVk now has a DynamicBuffer to handle the logic of acquiring a new/free buffer to be used. Bug: angleproject:4380 Change-Id: I207f334013ae944090eb48c26c692a09e8815f74 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2040513Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
parent 92b3a720
......@@ -141,7 +141,7 @@ BufferVk::VertexConversionBuffer::VertexConversionBuffer(VertexConversionBuffer
BufferVk::VertexConversionBuffer::~VertexConversionBuffer() = default;
// BufferVk implementation.
BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state) {}
BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state), mBuffer(nullptr) {}
BufferVk::~BufferVk() {}
......@@ -155,9 +155,10 @@ void BufferVk::destroy(const gl::Context *context)
void BufferVk::release(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
mBuffer.release(renderer);
mStagingBuffer.release(renderer);
mShadowBuffer.release();
mBufferPool.release(renderer);
mBuffer = nullptr;
for (ConversionBuffer &buffer : mVertexConversionBuffers)
{
......@@ -212,7 +213,9 @@ angle::Result BufferVk::setData(const gl::Context *context,
{
ContextVk *contextVk = vk::GetImpl(context);
if (size > static_cast<size_t>(mState.getSize()))
// BufferData call is re-specifying the entire buffer
// Release and init a new mBuffer with this new size
if (size > 0 && size != static_cast<size_t>(mState.getSize()))
{
// Release and re-create the memory and buffer.
release(contextVk);
......@@ -230,19 +233,17 @@ angle::Result BufferVk::setData(const gl::Context *context,
usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.flags = 0;
createInfo.size = roundUpPow2(size, kBufferSizeGranularity);
createInfo.usage = usageFlags;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
// Assume host visible/coherent memory available.
VkMemoryPropertyFlags memoryPropertyFlags = GetPreferredMemoryType(target, usage);
ANGLE_TRY(mBuffer.init(contextVk, createInfo, memoryPropertyFlags));
// mBuffer will be allocated through a DynamicBuffer
constexpr size_t kBufferHelperAlignment = 1;
constexpr size_t kBufferHelperPoolInitialSize = 0;
mBufferPool.initWithFlags(contextVk->getRenderer(), usageFlags, kBufferHelperAlignment,
kBufferHelperPoolInitialSize, memoryPropertyFlags);
ANGLE_TRY(acquireBufferHelper(contextVk, size, &mBuffer));
// Initialize the staging buffer
initializeStagingBuffer(contextVk, target, size);
......@@ -265,7 +266,7 @@ angle::Result BufferVk::setSubData(const gl::Context *context,
size_t size,
size_t offset)
{
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, offset));
......@@ -279,7 +280,7 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
GLintptr destOffset,
GLsizeiptr size)
{
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
ContextVk *contextVk = vk::GetImpl(context);
auto *sourceBuffer = GetAs<BufferVk>(source);
......@@ -305,7 +306,7 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->onBufferRead(VK_ACCESS_TRANSFER_READ_BIT, &sourceBuffer->getBuffer()));
ANGLE_TRY(contextVk->onBufferWrite(VK_ACCESS_TRANSFER_WRITE_BIT, &mBuffer));
ANGLE_TRY(contextVk->onBufferWrite(VK_ACCESS_TRANSFER_WRITE_BIT, mBuffer));
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
// Enqueue a copy command on the GPU.
......@@ -313,7 +314,7 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
static_cast<VkDeviceSize>(destOffset),
static_cast<VkDeviceSize>(size)};
commandBuffer->copyBuffer(sourceBuffer->getBuffer().getBuffer(), mBuffer.getBuffer(), 1,
commandBuffer->copyBuffer(sourceBuffer->getBuffer().getBuffer(), mBuffer->getBuffer(), 1,
&copyRegion);
// The new destination buffer data may require a conversion for the next draw, so mark it dirty.
......@@ -324,7 +325,7 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
angle::Result BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr)
{
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
return mapImpl(vk::GetImpl(context), mapPtr);
}
......@@ -351,15 +352,15 @@ angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk,
{
if (!mShadowBuffer.valid())
{
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
if ((access & GL_MAP_UNSYNCHRONIZED_BIT) == 0)
{
ANGLE_TRY(mBuffer.waitForIdle(contextVk));
ANGLE_TRY(mBuffer->waitForIdle(contextVk));
}
ANGLE_TRY(mBuffer.mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr),
static_cast<size_t>(offset)));
ANGLE_TRY(mBuffer->mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr),
static_cast<size_t>(offset)));
}
else
{
......@@ -388,12 +389,12 @@ angle::Result BufferVk::unmap(const gl::Context *context, GLboolean *result)
angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
{
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
if (!mShadowBuffer.valid())
{
mBuffer.unmap(contextVk->getRenderer());
mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
mBuffer->unmap(contextVk->getRenderer());
mBuffer->onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
}
else
{
......@@ -404,7 +405,7 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
// If it was a write operation we need to update the GPU buffer.
if (writeOperation)
{
// We do not yet know if thie data will ever be used. Perform a staged
// We do not yet know if this data will ever be used. Perform a staged
// update which will get flushed if and when necessary.
const uint8_t *data = getShadowBuffer(offset);
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
......@@ -444,11 +445,11 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
if (!mShadowBuffer.valid())
{
// Needed before reading buffer or we could get stale data.
ANGLE_TRY(mBuffer.finishRunningCommands(contextVk));
ANGLE_TRY(mBuffer->finishRunningCommands(contextVk));
ASSERT(mBuffer.valid());
ASSERT(mBuffer && mBuffer->valid());
ANGLE_TRY(mBuffer.mapWithOffset(contextVk, &mapPointer, offset));
ANGLE_TRY(mBuffer->mapWithOffset(contextVk, &mapPointer, offset));
}
else
{
......@@ -457,7 +458,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
*outRange = gl::ComputeIndexRange(type, mapPointer, count, primitiveRestartEnabled);
mBuffer.unmap(renderer);
mBuffer->unmap(renderer);
return angle::Result::Continue;
}
......@@ -468,13 +469,13 @@ angle::Result BufferVk::directUpdate(ContextVk *contextVk,
{
uint8_t *mapPointer = nullptr;
ANGLE_TRY(mBuffer.mapWithOffset(contextVk, &mapPointer, offset));
ANGLE_TRY(mBuffer->mapWithOffset(contextVk, &mapPointer, offset));
ASSERT(mapPointer);
memcpy(mapPointer, data, size);
mBuffer.unmap(contextVk->getRenderer());
mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
mBuffer->unmap(contextVk->getRenderer());
mBuffer->onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
return angle::Result::Continue;
}
......@@ -502,8 +503,8 @@ angle::Result BufferVk::stagedUpdate(ContextVk *contextVk,
// Enqueue a copy command on the GPU.
VkBufferCopy copyRegion = {stagingBufferOffset, offset, size};
ANGLE_TRY(mBuffer.copyFromBuffer(contextVk, mStagingBuffer.getCurrentBuffer(),
VK_ACCESS_HOST_WRITE_BIT, copyRegion));
ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, mStagingBuffer.getCurrentBuffer(),
VK_ACCESS_HOST_WRITE_BIT, copyRegion));
mStagingBuffer.getCurrentBuffer()->retain(&contextVk->getResourceUseList());
return angle::Result::Continue;
......@@ -517,10 +518,21 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
// Update shadow buffer
updateShadowBuffer(data, size, offset);
// If the buffer is currently in use, stage the update. Otherwise update the buffer directly.
if (mBuffer.isCurrentlyInUse(contextVk->getLastCompletedQueueSerial()))
// if the buffer is currently in use
// if size matches mBuffer's size, acquire a new BufferHelper from the pool
// else stage an update
// else update the buffer directly
if (mBuffer->isCurrentlyInUse(contextVk->getLastCompletedQueueSerial()))
{
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
if (size == static_cast<size_t>(mState.getSize()))
{
ANGLE_TRY(acquireBufferHelper(contextVk, size, &mBuffer));
ANGLE_TRY(directUpdate(contextVk, data, size, offset));
}
else
{
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
}
}
else
{
......@@ -540,10 +552,10 @@ angle::Result BufferVk::copyToBufferImpl(ContextVk *contextVk,
{
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->onBufferWrite(VK_ACCESS_TRANSFER_WRITE_BIT, destBuffer));
ANGLE_TRY(contextVk->onBufferRead(VK_ACCESS_TRANSFER_READ_BIT, &mBuffer));
ANGLE_TRY(contextVk->onBufferRead(VK_ACCESS_TRANSFER_READ_BIT, mBuffer));
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
commandBuffer->copyBuffer(mBuffer.getBuffer(), destBuffer->getBuffer(), copyCount, copies);
commandBuffer->copyBuffer(mBuffer->getBuffer(), destBuffer->getBuffer(), copyCount, copies);
return angle::Result::Continue;
}
......@@ -579,4 +591,28 @@ void BufferVk::onDataChanged()
markConversionBuffersDirty();
}
angle::Result BufferVk::acquireBufferHelper(ContextVk *contextVk,
size_t sizeInBytes,
vk::BufferHelper **bufferHelperOut)
{
bool needToReleasePreviousBuffers = false;
size_t size = roundUpPow2(sizeInBytes, kBufferSizeGranularity);
ANGLE_TRY(mBufferPool.allocate(contextVk, size, nullptr, nullptr, nullptr,
&needToReleasePreviousBuffers));
if (needToReleasePreviousBuffers)
{
// Release previous buffers
mBufferPool.releaseInFlightBuffers(contextVk);
}
ASSERT(bufferHelperOut);
*bufferHelperOut = mBufferPool.getCurrentBuffer();
ASSERT(*bufferHelperOut);
return angle::Result::Continue;
}
} // namespace rx
......@@ -84,14 +84,14 @@ class BufferVk : public BufferImpl
const vk::BufferHelper &getBuffer() const
{
ASSERT(mBuffer.valid());
return mBuffer;
ASSERT(mBuffer && mBuffer->valid());
return *mBuffer;
}
vk::BufferHelper &getBuffer()
{
ASSERT(mBuffer.valid());
return mBuffer;
ASSERT(mBuffer && mBuffer->valid());
return *mBuffer;
}
angle::Result mapImpl(ContextVk *contextVk, void **mapPtr);
......@@ -146,6 +146,10 @@ class BufferVk : public BufferImpl
void release(ContextVk *context);
void markConversionBuffersDirty();
angle::Result acquireBufferHelper(ContextVk *contextVk,
size_t sizeInBytes,
vk::BufferHelper **bufferHelperOut);
struct VertexConversionBuffer : public ConversionBuffer
{
VertexConversionBuffer(RendererVk *renderer,
......@@ -163,7 +167,10 @@ class BufferVk : public BufferImpl
size_t offset;
};
vk::BufferHelper mBuffer;
vk::BufferHelper *mBuffer;
// Pool of BufferHelpers for mBuffer to acquire from
vk::DynamicBuffer mBufferPool;
// All staging buffer support is provided by a DynamicBuffer.
vk::DynamicBuffer mStagingBuffer;
......
......@@ -462,7 +462,8 @@ DynamicBuffer::DynamicBuffer()
mNextAllocationOffset(0),
mLastFlushOrInvalidateOffset(0),
mSize(0),
mAlignment(0)
mAlignment(0),
mMemoryPropertyFlags(0)
{}
DynamicBuffer::DynamicBuffer(DynamicBuffer &&other)
......@@ -474,6 +475,7 @@ DynamicBuffer::DynamicBuffer(DynamicBuffer &&other)
mLastFlushOrInvalidateOffset(other.mLastFlushOrInvalidateOffset),
mSize(other.mSize),
mAlignment(other.mAlignment),
mMemoryPropertyFlags(other.mMemoryPropertyFlags),
mInFlightBuffers(std::move(other.mInFlightBuffers))
{
other.mBuffer = nullptr;
......@@ -485,8 +487,21 @@ void DynamicBuffer::init(RendererVk *renderer,
size_t initialSize,
bool hostVisible)
{
mUsage = usage;
mHostVisible = hostVisible;
VkMemoryPropertyFlags memoryPropertyFlags =
(hostVisible) ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
initWithFlags(renderer, usage, alignment, initialSize, memoryPropertyFlags);
}
void DynamicBuffer::initWithFlags(RendererVk *renderer,
VkBufferUsageFlags usage,
size_t alignment,
size_t initialSize,
VkMemoryPropertyFlags memoryPropertyFlags)
{
mUsage = usage;
mHostVisible = ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0);
mMemoryPropertyFlags = memoryPropertyFlags;
// Check that we haven't overriden the initial size of the buffer in setMinimumSizeForTesting.
if (mInitialSize == 0)
......@@ -523,9 +538,7 @@ angle::Result DynamicBuffer::allocateNewBuffer(ContextVk *contextVk)
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
const VkMemoryPropertyFlags memoryProperty =
mHostVisible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(buffer->init(contextVk, createInfo, memoryProperty));
ANGLE_TRY(buffer->init(contextVk, createInfo, mMemoryPropertyFlags));
ASSERT(!mBuffer);
mBuffer = buffer.release();
......@@ -612,7 +625,11 @@ angle::Result DynamicBuffer::allocate(ContextVk *contextVk,
*ptrOut = mappedMemory + mNextAllocationOffset;
}
*offsetOut = static_cast<VkDeviceSize>(mNextAllocationOffset);
if (offsetOut != nullptr)
{
*offsetOut = static_cast<VkDeviceSize>(mNextAllocationOffset);
}
mNextAllocationOffset += static_cast<uint32_t>(sizeToAllocate);
return angle::Result::Continue;
}
......
......@@ -70,6 +70,13 @@ class DynamicBuffer : angle::NonCopyable
size_t initialSize,
bool hostVisible);
// Init that gives the ability to pass in specified memory property flags for the buffer.
void initWithFlags(RendererVk *renderer,
VkBufferUsageFlags usage,
size_t alignment,
size_t initialSize,
VkMemoryPropertyFlags memoryProperty);
// This call will allocate a new region at the end of the buffer. It internally may trigger
// a new buffer to be created (which is returned in the optional parameter
// `newBufferAllocatedOut`). The new region will be in the returned buffer at given offset. If
......@@ -117,6 +124,7 @@ class DynamicBuffer : angle::NonCopyable
uint32_t mLastFlushOrInvalidateOffset;
size_t mSize;
size_t mAlignment;
VkMemoryPropertyFlags mMemoryPropertyFlags;
std::vector<BufferHelper *> mInFlightBuffers;
std::vector<BufferHelper *> mBufferFreeList;
......
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