rhi: add QByteArray overloads avoiding copies to QRhiResourceUpdateBatch
It seems wasteful to make a copy of the buffer we want to upload inside the update batch, especially when we construct a large geometry inside a QByteArray in user code. Avoid such copies, by creating update/upload overloads accepting a QByteArray. The received QByteArray is moved inside largeData directly, instead of allocating a new char[size] and then memcpying into it. The QByteArray is still copied into smallData if its size is less than SMALL_DATA_SIZE. This patch only applies to large buffers. Modify 2 auto tests to use the new uploadStaticBuffer() overload. [ChangeLog][RHI] Added QByteArray overloads to QRhiResourceUpdateBatch::updateDynamicBuffer and QRhiResourceUpdateBatch::uploadStaticBuffer that allow passing large buffers without copy to the RHI. Change-Id: I58f41b2f7889d88b565237819c0dcecf90988cb2 Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
parent
38fc8d845d
commit
5d857ed3bc
@ -9479,7 +9479,9 @@ bool QRhiResourceUpdateBatch::hasOptimalCapacity() const
|
||||
|
||||
The region is specified \a offset and \a size. The actual bytes to write
|
||||
are specified by \a data which must have at least \a size bytes available.
|
||||
\a data can safely be destroyed or changed once this function returns.
|
||||
|
||||
\a data is copied and can safely be destroyed or changed once this function
|
||||
returns.
|
||||
|
||||
\note If host writes are involved, which is the case with
|
||||
updateDynamicBuffer() typically as such buffers are backed by host visible
|
||||
@ -9504,13 +9506,36 @@ void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, quint32 offse
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\since 6.10
|
||||
|
||||
Enqueues updating a region of a QRhiBuffer \a buf created with the type
|
||||
QRhiBuffer::Dynamic.
|
||||
|
||||
\a data is moved into the batch instead of copied with this overload.
|
||||
*/
|
||||
void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
if (!data.isEmpty()) {
|
||||
const int idx = d->activeBufferOpCount++;
|
||||
const int opListSize = d->bufferOps.size();
|
||||
if (idx < opListSize)
|
||||
QRhiResourceUpdateBatchPrivate::BufferOp::changeToDynamicUpdate(&d->bufferOps[idx], buf, offset, std::move(data));
|
||||
else
|
||||
d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(buf, offset, std::move(data)));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Enqueues updating a region of a QRhiBuffer \a buf created with the type
|
||||
QRhiBuffer::Immutable or QRhiBuffer::Static.
|
||||
|
||||
The region is specified \a offset and \a size. The actual bytes to write
|
||||
are specified by \a data which must have at least \a size bytes available.
|
||||
\a data can safely be destroyed or changed once this function returns.
|
||||
|
||||
\a data is copied and can safely be destroyed or changed once this function
|
||||
returns.
|
||||
*/
|
||||
void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
|
||||
{
|
||||
@ -9523,6 +9548,26 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, quint32 offset
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\since 6.10
|
||||
|
||||
Enqueues updating a region of a QRhiBuffer \a buf created with the type
|
||||
QRhiBuffer::Immutable or QRhiBuffer::Static.
|
||||
|
||||
\a data is moved into the batch instead of copied with this overload.
|
||||
*/
|
||||
void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
if (!data.isEmpty()) {
|
||||
const int idx = d->activeBufferOpCount++;
|
||||
if (idx < d->bufferOps.size())
|
||||
QRhiResourceUpdateBatchPrivate::BufferOp::changeToStaticUpload(&d->bufferOps[idx], buf, offset, std::move(data));
|
||||
else
|
||||
d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, offset, std::move(data)));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
@ -9540,6 +9585,28 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\since 6.10
|
||||
|
||||
Enqueues updating the entire QRhiBuffer \a buf created with the type
|
||||
QRhiBuffer::Immutable or QRhiBuffer::Static.
|
||||
|
||||
\a data is moved into the batch instead of copied with this overload.
|
||||
|
||||
\a data size must equal the size of \a buf.
|
||||
*/
|
||||
void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, QByteArray data)
|
||||
{
|
||||
if (buf->size() > 0 && quint32(data.size()) == buf->size()) {
|
||||
const int idx = d->activeBufferOpCount++;
|
||||
if (idx < d->bufferOps.size())
|
||||
QRhiResourceUpdateBatchPrivate::BufferOp::changeToStaticUpload(&d->bufferOps[idx], buf, 0, 0, std::move(data));
|
||||
else
|
||||
d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, 0, 0, std::move(data)));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Enqueues reading back a region of the QRhiBuffer \a buf. The size of the
|
||||
region is specified by \a size in bytes, \a offset is the offset in bytes
|
||||
|
@ -1774,8 +1774,11 @@ public:
|
||||
bool hasOptimalCapacity() const;
|
||||
|
||||
void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data);
|
||||
void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, QByteArray data);
|
||||
void uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data);
|
||||
void uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, QByteArray data);
|
||||
void uploadStaticBuffer(QRhiBuffer *buf, const void *data);
|
||||
void uploadStaticBuffer(QRhiBuffer *buf, QByteArray data);
|
||||
void readBackBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result);
|
||||
void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc);
|
||||
void uploadTexture(QRhiTexture *tex, const QImage &image);
|
||||
|
@ -324,14 +324,12 @@ bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T,
|
||||
struct QRhiBufferDataPrivate
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
|
||||
QRhiBufferDataPrivate() { }
|
||||
~QRhiBufferDataPrivate() { delete[] largeData; }
|
||||
QRhiBufferDataPrivate() { } // don't value-initialize smallData
|
||||
int ref = 1;
|
||||
quint32 size = 0;
|
||||
quint32 largeAlloc = 0;
|
||||
char *largeData = nullptr;
|
||||
QByteArray largeData;
|
||||
static constexpr quint32 SMALL_DATA_SIZE = 1024;
|
||||
char data[SMALL_DATA_SIZE];
|
||||
char smallData[SMALL_DATA_SIZE];
|
||||
};
|
||||
|
||||
// no detach-with-contents, no atomic refcount, no shrink
|
||||
@ -363,7 +361,7 @@ public:
|
||||
}
|
||||
const char *constData() const
|
||||
{
|
||||
return d ? (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData) : nullptr;
|
||||
return d ? (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->smallData : d->largeData.constData()) : nullptr;
|
||||
}
|
||||
quint32 size() const
|
||||
{
|
||||
@ -371,7 +369,7 @@ public:
|
||||
}
|
||||
quint32 largeAlloc() const
|
||||
{
|
||||
return d ? d->largeAlloc : 0;
|
||||
return d ? d->largeData.size() : 0;
|
||||
}
|
||||
void assign(const char *s, quint32 size)
|
||||
{
|
||||
@ -385,16 +383,28 @@ public:
|
||||
}
|
||||
d->size = size;
|
||||
if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
|
||||
memcpy(d->data, s, size);
|
||||
memcpy(d->smallData, s, size);
|
||||
} else {
|
||||
if (d->largeAlloc < size) {
|
||||
if (QRhiImplementation::rubLogEnabled)
|
||||
qDebug("[rub] QRhiBufferData %p/%p new large data allocation %u -> %u", this, d, d->largeAlloc, size);
|
||||
delete[] d->largeData;
|
||||
d->largeAlloc = size;
|
||||
d->largeData = new char[size];
|
||||
}
|
||||
memcpy(d->largeData, s, size);
|
||||
if (QRhiImplementation::rubLogEnabled && largeAlloc() < size)
|
||||
qDebug("[rub] QRhiBufferData %p/%p new large data allocation %u -> %u", this, d, largeAlloc(), size);
|
||||
d->largeData.assign(QByteArrayView(s, size)); // keeps capacity
|
||||
}
|
||||
}
|
||||
void assign(QByteArray data)
|
||||
{
|
||||
if (!d) {
|
||||
d = new QRhiBufferDataPrivate;
|
||||
} else if (d->ref != 1) {
|
||||
if (QRhiImplementation::rubLogEnabled)
|
||||
qDebug("[rub] QRhiBufferData %p/%p new backing due to no-copy detach, ref was %d", this, d, d->ref);
|
||||
d->ref -= 1;
|
||||
d = new QRhiBufferDataPrivate;
|
||||
}
|
||||
d->size = data.size();
|
||||
if (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
|
||||
memcpy(d->smallData, data.constData(), data.size());
|
||||
} else {
|
||||
d->largeData = std::move(data);
|
||||
}
|
||||
}
|
||||
private:
|
||||
@ -435,6 +445,21 @@ public:
|
||||
op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
|
||||
}
|
||||
|
||||
static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
BufferOp op = {};
|
||||
changeToDynamicUpdate(&op, buf, offset, std::move(data));
|
||||
return op;
|
||||
}
|
||||
|
||||
static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
op->type = DynamicUpdate;
|
||||
op->buf = buf;
|
||||
op->offset = offset;
|
||||
op->data.assign(std::move(data));
|
||||
}
|
||||
|
||||
static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
|
||||
{
|
||||
BufferOp op = {};
|
||||
@ -451,6 +476,21 @@ public:
|
||||
op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
|
||||
}
|
||||
|
||||
static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
BufferOp op = {};
|
||||
changeToStaticUpload(&op, buf, offset, std::move(data));
|
||||
return op;
|
||||
}
|
||||
|
||||
static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, QByteArray data)
|
||||
{
|
||||
op->type = StaticUpload;
|
||||
op->buf = buf;
|
||||
op->offset = offset;
|
||||
op->data.assign(std::move(data));
|
||||
}
|
||||
|
||||
static BufferOp read(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
|
||||
{
|
||||
BufferOp op = {};
|
||||
|
@ -6509,10 +6509,11 @@ void tst_QRhi::tessellationInterfaceBlocks()
|
||||
u->updateDynamicBuffer(ubuf.data(), 0, 64, mvp.constData());
|
||||
|
||||
QScopedPointer<QRhiBuffer> buffer(
|
||||
rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::UsageFlag::StorageBuffer, 1024));
|
||||
rhi->newBuffer(QRhiBuffer::Static, QRhiBuffer::UsageFlag::StorageBuffer, 2048));
|
||||
QVERIFY(buffer->create());
|
||||
|
||||
u->uploadStaticBuffer(buffer.data(), 0, 1024, QByteArray(1024, 0).constData());
|
||||
// 2048 is a large buffer for RUB
|
||||
u->uploadStaticBuffer(buffer.data(), QByteArray(2048, 0));
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||
srb->setBindings(
|
||||
@ -6825,7 +6826,7 @@ void tst_QRhi::storageBuffer()
|
||||
reinterpret_cast<float *>(&toGpuData.data()[toGpuMembers["_vec4"].offset])[2] = 9.0f;
|
||||
reinterpret_cast<float *>(&toGpuData.data()[toGpuMembers["_vec4"].offset])[3] = 10.0f;
|
||||
|
||||
u->uploadStaticBuffer(toGpuBuffer.data(), 0, toGpuData.size(), toGpuData.constData());
|
||||
u->uploadStaticBuffer(toGpuBuffer.data(), std::move(toGpuData));
|
||||
u->uploadStaticBuffer(fromGpuBuffer.data(), 0, blocks["fromGpu"].knownSize, QByteArray(blocks["fromGpu"].knownSize, 0).constData());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||
|
Loading…
x
Reference in New Issue
Block a user