rhi: Make res. update batch buffer op list more bounded

Make the behavior more bounded, by disallowing keeping large sets of
buffer data for reuse.

The idea being that once I uploaded say the Sponza model (e.g. 10 MB of
vertex data merged into one blob), it is not beneficial to keep that
huge allocation since the total size of the data from per-frame buffer
changes will be in a whole different (smaller) league typically, and the
chance of another (single) buffer update that utilizes the whole 10 MB
on its own is rather small.

Have a simple (and cheap to execute) rule that no batch keeps more than
1 MB of QRhiBufferData (in total, regardless how many buffer operations
it contained/contains). If the limit is exceeded, we clear().

The catch is that even without doing the 1 MB check, there is a high
chance that the Sponza model data will be gone in a few frames
eventually, due to implicit sharing and how the QRhi backends and Qt
Quick(3D) work and shuffle their resource update batch requests and
submissions. So for many applications this is expected to change nothing
when it comes to their memory consumption profile.

Expand the comments further.

Change-Id: I93061d073b43e747a0772a3119ebdb89bed02ae0
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
(cherry picked from commit bc2c09a182c2bc80c307e7e71e0f12c1c31edc3d)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Laszlo Agocs 2024-08-09 15:51:05 +02:00 committed by Qt Cherry-pick Bot
parent e4bd5c631a
commit 7d64c858b5

View File

@ -9334,13 +9334,14 @@ void QRhiResourceUpdateBatchPrivate::free()
{
Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q);
quint32 bufferDataTotal = 0;
quint32 bufferLargeAllocTotal = 0;
for (const BufferOp &op : std::as_const(bufferOps)) {
bufferDataTotal += op.data.size();
bufferLargeAllocTotal += op.data.largeAlloc(); // alloc when > 1 KB
}
if (rhi->rubLogEnabled) {
quint32 bufferDataTotal = 0;
quint32 bufferLargeAllocTotal = 0;
for (const BufferOp &op : std::as_const(bufferOps)) {
bufferDataTotal += op.data.size();
bufferLargeAllocTotal += op.data.largeAlloc(); // alloc when > 1 KB
}
qDebug() << "[rub] release to pool upd.batch #" << poolIndex
<< "/ bufferOps active" << activeBufferOpCount
<< "of" << bufferOps.count()
@ -9362,10 +9363,19 @@ void QRhiResourceUpdateBatchPrivate::free()
// at least. Only trimOpList() goes for the more aggressive route with squeeze.
textureOps.clear();
// bufferOps is not touched, to allow reusing allocations (incl. in the
// elements' QRhiBufferData) as much as possible when this batch is used
// again in the future, which is important for performance, in particular
// with Qt Quick.
// bufferOps is not touched in many cases, to allow reusing allocations
// (incl. in the elements' QRhiBufferData) as much as possible when this
// batch is used again in the future, which is important for performance, in
// particular with Qt Quick where it is easy for scenes to produce lots of,
// typically small buffer changes on every frame.
//
// However, ensure that even in the unlikely case of having the max number
// of batches (64) created in resUpdPool, no more than 64 MB in total is
// used up by buffer data just to help future reuse. For simplicity, if
// there is more than 1 MB data -> clear. Applications with frequent, huge
// buffer updates probably have other bottlenecks anyway.
if (bufferLargeAllocTotal > 1024 * 1024)
bufferOps.clear();
}
void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other)