Commit 44063c80 by Jamie Madill Committed by Commit Bot

Vulkan: Store array buffer conversions in BufferVk.

The intent of this CL is to call convertVertexBuffer*PU only when we have new data to convert. If the app unbinds and rebinds a vertex buffer without changing the data we can now retrieve the cached vertex buffer info from the BufferVk class. Previously we would always reconvert the data on a rebind. This was slowing down applications and benchmarks. To achieve this we add a conversion cache to BufferVk. Each cache entry stores a key based on the vertex info. Also we store a ring buffer for each cache entry and a flag to indicate if the entry is dirty. The cache is dirtied on a bufffer data update or a map call. Improves performance in the T-Rex benchmark. Bug: angleproject:3495 Change-Id: Ia999c9187510748ba95bc98362eb332e1990d270 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1638903 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com>
parent e431aaa1
......@@ -26,8 +26,44 @@ namespace
// On some hardware, reading 4 bytes from address 4k returns 0, making it impossible to read the
// last n bytes. By rounding up the buffer sizes to a multiple of 4, the problem is alleviated.
constexpr size_t kBufferSizeGranularity = 4;
// Start with a fairly small buffer size. We can increase this dynamically as we convert more data.
constexpr size_t kConvertedArrayBufferInitialSize = 1024 * 8;
} // namespace
// ConversionBuffer implementation.
ConversionBuffer::ConversionBuffer(RendererVk *renderer,
VkBufferUsageFlags usageFlags,
size_t initialSize,
size_t alignment)
: dirty(true), lastAllocationOffset(0), data(usageFlags, initialSize, true)
{
data.init(alignment, renderer);
}
ConversionBuffer::~ConversionBuffer() = default;
ConversionBuffer::ConversionBuffer(ConversionBuffer &&other) = default;
// BufferVk::VertexConversionBuffer implementation.
BufferVk::VertexConversionBuffer::VertexConversionBuffer(RendererVk *renderer,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn)
: ConversionBuffer(renderer,
vk::kVertexBufferUsageFlags,
kConvertedArrayBufferInitialSize,
vk::kVertexBufferAlignment),
formatID(formatIDIn),
stride(strideIn),
offset(offsetIn)
{}
BufferVk::VertexConversionBuffer::VertexConversionBuffer(VertexConversionBuffer &&other) = default;
BufferVk::VertexConversionBuffer::~VertexConversionBuffer() = default;
// BufferVk implementation.
BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state), mDataWriteAccessFlags(0) {}
BufferVk::~BufferVk() {}
......@@ -42,6 +78,11 @@ void BufferVk::destroy(const gl::Context *context)
void BufferVk::release(ContextVk *contextVk)
{
mBuffer.release(contextVk);
for (ConversionBuffer &buffer : mVertexConversionBuffers)
{
buffer.data.release(contextVk);
}
}
angle::Result BufferVk::setData(const gl::Context *context,
......@@ -156,6 +197,8 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
mBuffer.getDeviceMemory().unmap(contextVk->getDevice());
mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
markConversionBuffersDirty();
return angle::Result::Continue;
}
......@@ -222,7 +265,7 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
size_t size,
size_t offset)
{
VkDevice device = contextVk->getDevice();
VkDevice device = contextVk->getDevice();
// Use map when available.
if (mBuffer.isResourceInUse(contextVk))
......@@ -260,6 +303,9 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
}
// Update conversions
markConversionBuffersDirty();
return angle::Result::Continue;
}
......@@ -278,4 +324,29 @@ angle::Result BufferVk::copyToBuffer(ContextVk *contextVk,
return angle::Result::Continue;
}
ConversionBuffer *BufferVk::getVertexConversionBuffer(RendererVk *renderer,
angle::FormatID formatID,
GLuint stride,
size_t offset)
{
for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
{
if (buffer.formatID == formatID && buffer.stride == stride && buffer.offset == offset)
{
return &buffer;
}
}
mVertexConversionBuffers.emplace_back(renderer, formatID, stride, offset);
return &mVertexConversionBuffers.back();
}
void BufferVk::markConversionBuffersDirty()
{
for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
{
buffer.dirty = true;
}
}
} // namespace rx
......@@ -19,6 +19,27 @@ namespace rx
{
class RendererVk;
// Conversion buffers hold translated index and vertex data.
struct ConversionBuffer
{
ConversionBuffer(RendererVk *renderer,
VkBufferUsageFlags usageFlags,
size_t initialSize,
size_t alignment);
~ConversionBuffer();
ConversionBuffer(ConversionBuffer &&other);
// One state value determines if we need to re-stream vertex data.
bool dirty;
// One additional state value keeps the last allocation offset.
VkDeviceSize lastAllocationOffset;
// The conversion is stored in a dynamic buffer.
vk::DynamicBuffer data;
};
class BufferVk : public BufferImpl
{
public:
......@@ -83,15 +104,40 @@ class BufferVk : public BufferImpl
uint32_t copyCount,
const VkBufferCopy *copies);
ConversionBuffer *getVertexConversionBuffer(RendererVk *renderer,
angle::FormatID formatID,
GLuint stride,
size_t offset);
private:
angle::Result setDataImpl(ContextVk *contextVk,
const uint8_t *data,
size_t size,
size_t offset);
void release(ContextVk *context);
void markConversionBuffersDirty();
struct VertexConversionBuffer : public ConversionBuffer
{
VertexConversionBuffer(RendererVk *renderer,
angle::FormatID formatIDIn,
GLuint strideIn,
size_t offsetIn);
~VertexConversionBuffer();
VertexConversionBuffer(VertexConversionBuffer &&other);
// The conversion is identified by the triple of {format, stride, offset}.
angle::FormatID formatID;
GLuint stride;
size_t offset;
};
vk::BufferHelper mBuffer;
VkAccessFlags mDataWriteAccessFlags;
// A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers;
};
} // namespace rx
......
......@@ -17,6 +17,7 @@
namespace rx
{
class BufferVk;
struct ConversionBuffer;
class VertexArrayVk : public VertexArrayImpl
{
......@@ -97,13 +98,14 @@ class VertexArrayVk : public VertexArrayImpl
BufferVk *srcBuffer,
const gl::VertexBinding &binding,
size_t attribIndex,
const vk::Format &vertexFormat);
const vk::Format &vertexFormat,
ConversionBuffer *conversion);
angle::Result convertVertexBufferCPU(ContextVk *contextVk,
BufferVk *srcBuffer,
const gl::VertexBinding &binding,
size_t attribIndex,
const vk::Format &vertexFormat);
void ensureConversionReleased(ContextVk *contextVk, size_t attribIndex);
const vk::Format &vertexFormat,
ConversionBuffer *conversion);
angle::Result syncDirtyAttrib(ContextVk *contextVk,
const gl::VertexAttribute &attrib,
......@@ -113,8 +115,6 @@ class VertexArrayVk : public VertexArrayImpl
gl::AttribArray<VkBuffer> mCurrentArrayBufferHandles;
gl::AttribArray<VkDeviceSize> mCurrentArrayBufferOffsets;
gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers;
gl::AttribArray<vk::DynamicBuffer> mCurrentArrayBufferConversion;
gl::AttribArray<bool> mCurrentArrayBufferConversionCanRelease;
VkDeviceSize mCurrentElementArrayBufferOffset;
vk::BufferHelper *mCurrentElementArrayBuffer;
......
......@@ -21,6 +21,13 @@ namespace rx
{
namespace vk
{
constexpr VkBufferUsageFlags kVertexBufferUsageFlags =
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
constexpr VkBufferUsageFlags kIndexBufferUsageFlags =
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
constexpr size_t kVertexBufferAlignment = 4;
constexpr size_t kIndexBufferAlignment = 4;
// A dynamic buffer is conceptually an infinitely long buffer. Each time you write to the buffer,
// you will always write to a previously unused portion. After a series of writes, you must flush
// the buffer data to the device. Buffer lifetime currently assumes that each new allocation will
......
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