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 ...@@ -26,8 +26,44 @@ namespace
// On some hardware, reading 4 bytes from address 4k returns 0, making it impossible to read the // 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. // last n bytes. By rounding up the buffer sizes to a multiple of 4, the problem is alleviated.
constexpr size_t kBufferSizeGranularity = 4; 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 } // 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(const gl::BufferState &state) : BufferImpl(state), mDataWriteAccessFlags(0) {}
BufferVk::~BufferVk() {} BufferVk::~BufferVk() {}
...@@ -42,6 +78,11 @@ void BufferVk::destroy(const gl::Context *context) ...@@ -42,6 +78,11 @@ void BufferVk::destroy(const gl::Context *context)
void BufferVk::release(ContextVk *contextVk) void BufferVk::release(ContextVk *contextVk)
{ {
mBuffer.release(contextVk); mBuffer.release(contextVk);
for (ConversionBuffer &buffer : mVertexConversionBuffers)
{
buffer.data.release(contextVk);
}
} }
angle::Result BufferVk::setData(const gl::Context *context, angle::Result BufferVk::setData(const gl::Context *context,
...@@ -156,6 +197,8 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk) ...@@ -156,6 +197,8 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
mBuffer.getDeviceMemory().unmap(contextVk->getDevice()); mBuffer.getDeviceMemory().unmap(contextVk->getDevice());
mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT; mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
markConversionBuffersDirty();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -260,6 +303,9 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk, ...@@ -260,6 +303,9 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT; mDataWriteAccessFlags = VK_ACCESS_HOST_WRITE_BIT;
} }
// Update conversions
markConversionBuffersDirty();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -278,4 +324,29 @@ angle::Result BufferVk::copyToBuffer(ContextVk *contextVk, ...@@ -278,4 +324,29 @@ angle::Result BufferVk::copyToBuffer(ContextVk *contextVk,
return angle::Result::Continue; 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 } // namespace rx
...@@ -19,6 +19,27 @@ namespace rx ...@@ -19,6 +19,27 @@ namespace rx
{ {
class RendererVk; 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 class BufferVk : public BufferImpl
{ {
public: public:
...@@ -83,15 +104,40 @@ class BufferVk : public BufferImpl ...@@ -83,15 +104,40 @@ class BufferVk : public BufferImpl
uint32_t copyCount, uint32_t copyCount,
const VkBufferCopy *copies); const VkBufferCopy *copies);
ConversionBuffer *getVertexConversionBuffer(RendererVk *renderer,
angle::FormatID formatID,
GLuint stride,
size_t offset);
private: private:
angle::Result setDataImpl(ContextVk *contextVk, angle::Result setDataImpl(ContextVk *contextVk,
const uint8_t *data, const uint8_t *data,
size_t size, size_t size,
size_t offset); size_t offset);
void release(ContextVk *context); 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; vk::BufferHelper mBuffer;
VkAccessFlags mDataWriteAccessFlags; VkAccessFlags mDataWriteAccessFlags;
// A cache of converted vertex data.
std::vector<VertexConversionBuffer> mVertexConversionBuffers;
}; };
} // namespace rx } // namespace rx
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
namespace rx namespace rx
{ {
class BufferVk; class BufferVk;
struct ConversionBuffer;
class VertexArrayVk : public VertexArrayImpl class VertexArrayVk : public VertexArrayImpl
{ {
...@@ -97,13 +98,14 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -97,13 +98,14 @@ class VertexArrayVk : public VertexArrayImpl
BufferVk *srcBuffer, BufferVk *srcBuffer,
const gl::VertexBinding &binding, const gl::VertexBinding &binding,
size_t attribIndex, size_t attribIndex,
const vk::Format &vertexFormat); const vk::Format &vertexFormat,
ConversionBuffer *conversion);
angle::Result convertVertexBufferCPU(ContextVk *contextVk, angle::Result convertVertexBufferCPU(ContextVk *contextVk,
BufferVk *srcBuffer, BufferVk *srcBuffer,
const gl::VertexBinding &binding, const gl::VertexBinding &binding,
size_t attribIndex, size_t attribIndex,
const vk::Format &vertexFormat); const vk::Format &vertexFormat,
void ensureConversionReleased(ContextVk *contextVk, size_t attribIndex); ConversionBuffer *conversion);
angle::Result syncDirtyAttrib(ContextVk *contextVk, angle::Result syncDirtyAttrib(ContextVk *contextVk,
const gl::VertexAttribute &attrib, const gl::VertexAttribute &attrib,
...@@ -113,8 +115,6 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -113,8 +115,6 @@ class VertexArrayVk : public VertexArrayImpl
gl::AttribArray<VkBuffer> mCurrentArrayBufferHandles; gl::AttribArray<VkBuffer> mCurrentArrayBufferHandles;
gl::AttribArray<VkDeviceSize> mCurrentArrayBufferOffsets; gl::AttribArray<VkDeviceSize> mCurrentArrayBufferOffsets;
gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers; gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers;
gl::AttribArray<vk::DynamicBuffer> mCurrentArrayBufferConversion;
gl::AttribArray<bool> mCurrentArrayBufferConversionCanRelease;
VkDeviceSize mCurrentElementArrayBufferOffset; VkDeviceSize mCurrentElementArrayBufferOffset;
vk::BufferHelper *mCurrentElementArrayBuffer; vk::BufferHelper *mCurrentElementArrayBuffer;
......
...@@ -21,6 +21,13 @@ namespace rx ...@@ -21,6 +21,13 @@ namespace rx
{ {
namespace vk 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, // 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 // 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 // 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