diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2020-09-29 17:38:26 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2020-09-30 10:39:59 +0200 |
commit | 2f879062a5ce9e78c1e257924bcdc5ac95cba465 (patch) | |
tree | 34222dd886c8608d4ea7190763a67827f598e321 /src | |
parent | 0836db2330c0542d91fe61935e97d3369db93156 (diff) |
rhi: Manage buffer data without QBA
Use a simple and straightforward container that only does what
we need here.
Change-Id: I1a81b53a58bc91d533e3d7df5471a1362046825d
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/rhi/qrhi_p_p.h | 115 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 8 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p_p.h | 6 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2.cpp | 14 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2_p_p.h | 6 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal.mm | 10 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull.cpp | 2 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 16 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p_p.h | 5 |
9 files changed, 119 insertions, 63 deletions
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 3b68b5d078..1316321694 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -277,6 +277,80 @@ bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, return true; } +struct QRhiBufferDataPrivate +{ + Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate) + QRhiBufferDataPrivate() { } + ~QRhiBufferDataPrivate() { delete[] largeData; } + int ref = 1; + int size = 0; + int largeAlloc = 0; + char *largeData = nullptr; + static constexpr int SMALL_DATA_SIZE = 1024; + char data[SMALL_DATA_SIZE]; +}; + +// no detach-with-contents, no atomic refcount, no shrink +class QRhiBufferData +{ +public: + QRhiBufferData() = default; + ~QRhiBufferData() + { + if (d && !--d->ref) + delete d; + } + QRhiBufferData(const QRhiBufferData &other) + : d(other.d) + { + if (d) + d->ref += 1; + } + QRhiBufferData &operator=(const QRhiBufferData &other) + { + if (d == other.d) + return *this; + if (other.d) + other.d->ref += 1; + if (d && !--d->ref) + delete d; + d = other.d; + return *this; + } + const char *constData() const + { + return d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData; + } + int size() const + { + return d->size; + } + void assign(const char *s, int size) + { + if (!d) { + d = new QRhiBufferDataPrivate; + } else if (d->ref != 1) { + d->ref -= 1; + d = new QRhiBufferDataPrivate; + } + d->size = size; + if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) { + memcpy(d->data, s, size); + } else { + if (d->largeAlloc < size) { + delete[] d->largeData; + d->largeAlloc = size; + d->largeData = new char[size]; + } + memcpy(d->largeData, s, size); + } + } +private: + QRhiBufferDataPrivate *d = nullptr; +}; + +Q_DECLARE_TYPEINFO(QRhiBufferData, Q_MOVABLE_TYPE); + class QRhiResourceUpdateBatchPrivate { public: @@ -289,8 +363,7 @@ public: Type type; QRhiBuffer *buf; int offset; - QByteArray data; - int dataSize; // the real number of currently used bytes in data, not the same as data.size() + QRhiBufferData data; int readSize; QRhiBufferReadbackResult *result; @@ -301,8 +374,7 @@ public: op.buf = buf; op.offset = offset; const int effectiveSize = size ? size : buf->size(); - op.data = QByteArray(reinterpret_cast<const char *>(data), effectiveSize); - op.dataSize = effectiveSize; + op.data.assign(reinterpret_cast<const char *>(data), effectiveSize); return op; } @@ -312,28 +384,7 @@ public: op->buf = buf; op->offset = offset; const int effectiveSize = size ? size : buf->size(); - - // Why the isDetached check? Simply because the cost of detaching - // with a larger allocation may be a lot higher than creating a new - // deep copy bytearray with our (potentially lot smaller) data. - // This reduces the benefits with certain backends (e.g. Vulkan) - // that hold on to the data (implicit sharing!) of host visible - // buffers for the current and next frame (assuming 2 frames in - // flight), but it is still an improvement (enabled by - // nextResourceUpdateBatch's shuffling when choosing a free batch - // from the pool). For other backends (e.g. D3D11) this can reduce - // mallocs (caused by creating new deep copy bytearrays) almost - // completely after a few frames (assuming of course that no - // dynamic elements with larger buffer data appear). - - if (op->data.isDetached()) { - if (op->data.size() < effectiveSize) - op->data.resize(effectiveSize); - memcpy(op->data.data(), data, effectiveSize); - } else { - op->data = QByteArray(reinterpret_cast<const char *>(data), effectiveSize); - } - op->dataSize = effectiveSize; + op->data.assign(reinterpret_cast<const char *>(data), effectiveSize); } static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data) @@ -343,8 +394,7 @@ public: op.buf = buf; op.offset = offset; const int effectiveSize = size ? size : buf->size(); - op.data = QByteArray(reinterpret_cast<const char *>(data), effectiveSize); - op.dataSize = effectiveSize; + op.data.assign(reinterpret_cast<const char *>(data), effectiveSize); return op; } @@ -354,14 +404,7 @@ public: op->buf = buf; op->offset = offset; const int effectiveSize = size ? size : buf->size(); - if (op->data.isDetached()) { - if (op->data.size() < effectiveSize) - op->data.resize(effectiveSize); - memcpy(op->data.data(), data, effectiveSize); - } else { - op->data = QByteArray(reinterpret_cast<const char *>(data), effectiveSize); - } - op->dataSize = effectiveSize; + op->data.assign(reinterpret_cast<const char *>(data), effectiveSize); } static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result) diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 1c7f3cd17a..2fe138dd55 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -1418,17 +1418,17 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) { QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); - memcpy(bufD->dynBuf + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(bufD->dynBuf + u.offset, u.data.constData(), size_t(u.data.size())); bufD->hasPendingDynamicUpdates = true; } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.dataSize <= bufD->m_size); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; cmd.args.updateSubRes.dst = bufD->buffer; cmd.args.updateSubRes.dstSubRes = 0; - cmd.args.updateSubRes.src = cbD->retainData(u.data); + cmd.args.updateSubRes.src = cbD->retainBufferData(u.data); cmd.args.updateSubRes.srcRowPitch = 0; // Specify the region (even when offset is 0 and all data is provided) // since the ID3D11Buffer's size is rounded up to be a multiple of 256 @@ -1437,7 +1437,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate box.left = UINT(u.offset); box.top = box.front = 0; box.back = box.bottom = 1; - box.right = UINT(u.offset + u.dataSize); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc + box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc cmd.args.updateSubRes.hasDstBox = true; cmd.args.updateSubRes.dstBox = box; cbD->commands.append(cmd); diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 7c13544276..b89b6f5124 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -473,6 +473,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer quint32 currentVertexOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; QVarLengthArray<QByteArray, 4> dataRetainPool; + QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool; QVarLengthArray<QImage, 4> imageRetainPool; // relies heavily on implicit sharing (no copies of the actual data will be made) @@ -480,6 +481,10 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer dataRetainPool.append(data); return reinterpret_cast<const uchar *>(dataRetainPool.last().constData()); } + const uchar *retainBufferData(const QRhiBufferData &data) { + bufferDataRetainPool.append(data); + return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData()); + } const uchar *retainImage(const QImage &image) { imageRetainPool.append(image); return imageRetainPool.last().constBits(); @@ -487,6 +492,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer void resetCommands() { commands.clear(); dataRetainPool.clear(); + bufferDataRetainPool.clear(); imageRetainPool.clear(); } void resetState() { diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 372bf39c3e..c1ea444c50 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1697,7 +1697,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { - memcpy(bufD->ubuf + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(bufD->ubuf + u.offset, u.data.constData(), size_t(u.data.size())); } else { trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); QGles2CommandBuffer::Command cmd; @@ -1705,16 +1705,16 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.bufferSubData.target = bufD->targetForDataOps; cmd.args.bufferSubData.buffer = bufD->buffer; cmd.args.bufferSubData.offset = u.offset; - cmd.args.bufferSubData.size = u.dataSize; - cmd.args.bufferSubData.data = cbD->retainData(u.data); + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainBufferData(u.data); cbD->commands.append(cmd); } } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.dataSize <= bufD->m_size); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { - memcpy(bufD->ubuf + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(bufD->ubuf + u.offset, u.data.constData(), size_t(u.data.size())); } else { trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); QGles2CommandBuffer::Command cmd; @@ -1722,8 +1722,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.bufferSubData.target = bufD->targetForDataOps; cmd.args.bufferSubData.buffer = bufD->buffer; cmd.args.bufferSubData.offset = u.offset; - cmd.args.bufferSubData.size = u.dataSize; - cmd.args.bufferSubData.data = cbD->retainData(u.data); + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainBufferData(u.data); cbD->commands.append(cmd); } } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 5940633151..65fb820a36 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -578,6 +578,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer } computePassState; QVarLengthArray<QByteArray, 4> dataRetainPool; + QVarLengthArray<QRhiBufferData, 4> bufferDataRetainPool; QVarLengthArray<QImage, 4> imageRetainPool; // relies heavily on implicit sharing (no copies of the actual data will be made) @@ -585,6 +586,10 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer dataRetainPool.append(data); return dataRetainPool.last().constData(); } + const uchar *retainBufferData(const QRhiBufferData &data) { + bufferDataRetainPool.append(data); + return reinterpret_cast<const uchar *>(bufferDataRetainPool.last().constData()); + } const void *retainImage(const QImage &image) { imageRetainPool.append(image); return imageRetainPool.last().constBits(); @@ -592,6 +597,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer void resetCommands() { commands.clear(); dataRetainPool.clear(); + bufferDataRetainPool.clear(); imageRetainPool.clear(); passResTrackers.clear(); diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 9058a7556a..a31d37c01a 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -1703,10 +1703,10 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate // basically the same. So go through the same pendingUpdates machinery. QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.dataSize <= bufD->m_size); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) bufD->d->pendingUpdates[i].append( - QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(u.buf, u.offset, u.dataSize, u.data.constData())); + QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(u.buf, u.offset, u.data.size(), u.data.constData())); } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); executeBufferHostWritesForCurrentFrame(bufD); @@ -1868,11 +1868,11 @@ void QRhiMetal::executeBufferHostWritesForSlot(QMetalBuffer *bufD, int slot) int changeEnd = -1; for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : qAsConst(bufD->d->pendingUpdates[slot])) { Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf)); - memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) changeBegin = u.offset; - if (changeEnd == -1 || u.offset + u.dataSize > changeEnd) - changeEnd = u.offset + u.dataSize; + if (changeEnd == -1 || u.offset + u.data.size() > changeEnd) + changeEnd = u.offset + u.data.size(); } #ifdef Q_OS_MACOS if (changeBegin >= 0 && bufD->d->managed) diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index e0eb110c0d..4c650e68a0 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -465,7 +465,7 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re || u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf); - memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size())); } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { QRhiBufferReadbackResult *result = u.result; result->data.resize(u.readSize); diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 5146a2ccc3..c0162820b6 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -2929,11 +2929,11 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) - bufD->pendingDynamicUpdates[i].append({ u.offset, u.dataSize, u.data }); + bufD->pendingDynamicUpdates[i].append({ u.offset, u.data }); } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.dataSize <= bufD->m_size); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); if (!bufD->stagingBuffers[currentFrameSlot]) { VkBufferCreateInfo bufferInfo; @@ -2967,9 +2967,9 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat qWarning("Failed to map buffer: %d", err); continue; } - memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.dataSize)); + memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); vmaUnmapMemory(toVmaAllocator(allocator), a); - vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.dataSize)); + vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size())); trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); @@ -2978,7 +2978,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat memset(©Info, 0, sizeof(copyInfo)); copyInfo.srcOffset = VkDeviceSize(u.offset); copyInfo.dstOffset = VkDeviceSize(u.offset); - copyInfo.size = VkDeviceSize(u.dataSize); + copyInfo.size = VkDeviceSize(u.data.size()); QVkCommandBuffer::Command cmd; cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; @@ -3429,11 +3429,11 @@ void QRhiVulkan::executeBufferHostWritesForSlot(QVkBuffer *bufD, int slot) int changeBegin = -1; int changeEnd = -1; for (const QVkBuffer::DynamicUpdate &u : qAsConst(bufD->pendingDynamicUpdates[slot])) { - memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.size)); + memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) changeBegin = u.offset; - if (changeEnd == -1 || u.offset + u.size > changeEnd) - changeEnd = u.offset + u.size; + if (changeEnd == -1 || u.offset + u.data.size() > changeEnd) + changeEnd = u.offset + u.data.size(); } vmaUnmapMemory(toVmaAllocator(allocator), a); if (changeBegin >= 0) diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index f3157c9112..c590039bc1 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -82,8 +82,7 @@ struct QVkBuffer : public QRhiBuffer QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; struct DynamicUpdate { int offset; - int size; - QByteArray data; + QRhiBufferData data; }; QVarLengthArray<DynamicUpdate, 16> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; @@ -98,6 +97,8 @@ struct QVkBuffer : public QRhiBuffer friend class QRhiVulkan; }; +Q_DECLARE_TYPEINFO(QVkBuffer::DynamicUpdate, Q_MOVABLE_TYPE); + struct QVkTexture; struct QVkRenderBuffer : public QRhiRenderBuffer |