diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-11-23 22:08:03 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-11-27 00:19:27 +0100 |
commit | 5a496a614bb949ad7743960df7a8769509b90b8b (patch) | |
tree | ca148141dba44e15721bfe9f113a7b63604c84c8 /tests | |
parent | 722df970e82b35e8da9e14a2efaacf733315065c (diff) |
rhi: Add a more sophisticated resource update autotest case
This time exercising series of buffer updates and texture uploads
within proper, on-screen frames. (particularly interesting for dynamic
buffers in case the double (or more) buffering and having multiple
frames in flight involves special bookkeeping for these - using
'offscreen' frames like in other test cases does not necessarily
exercise all of this)
Change-Id: Id470919d27037359a1f0346a50a2a0e3966f5cd2
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/gui/rhi/qrhi/tst_qrhi.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 8ac18af9fb..5142d5566f 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -135,6 +135,8 @@ private slots: void renderToWindowSimple(); void finishWithinSwapchainFrame_data(); void finishWithinSwapchainFrame(); + void resourceUpdateBatchBufferTextureWithSwapchainFrames_data(); + void resourceUpdateBatchBufferTextureWithSwapchainFrames(); void pipelineCache_data(); void pipelineCache(); @@ -910,6 +912,8 @@ void tst_QRhi::resourceUpdateBatchBuffer() if (rhi->isFeatureSupported(QRhi::ReadBackNonUniformBuffer)) batch->readBackBuffer(dynamicBuffer.data(), 5, 10, &readResult); + else + qDebug("Skipping verification of buffer data as ReadBackNonUniformBuffer is not supported"); QVERIFY(submitResourceUpdates(rhi.data(), batch)); @@ -3553,6 +3557,216 @@ void tst_QRhi::finishWithinSwapchainFrame() rhi->endFrame(swapChain.data()); } +void tst_QRhi::resourceUpdateBatchBufferTextureWithSwapchainFrames_data() +{ + rhiTestData(); +} + +void tst_QRhi::resourceUpdateBatchBufferTextureWithSwapchainFrames() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive)) + QSKIP("Offscreen: Skipping onscreen test"); + + QFETCH(QRhi::Implementation, impl); + QFETCH(QRhiInitParams *, initParams); + + QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing buffer resource updates"); + + QScopedPointer<QWindow> window(new QWindow); + setWindowType(window.data(), impl); + + window->setGeometry(0, 0, 640, 480); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QScopedPointer<QRhiSwapChain> swapChain(rhi->newSwapChain()); + swapChain->setWindow(window.data()); + swapChain->setFlags(QRhiSwapChain::UsedAsTransferSource); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(swapChain->newCompatibleRenderPassDescriptor()); + swapChain->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(swapChain->createOrResize()); + + const int bufferSize = 18; + const char *a = "123456789"; + const char *b = "abcdefghi"; + + bool readCompleted = false; + QRhiBufferReadbackResult readResult; + readResult.completed = [&readCompleted] { readCompleted = true; }; + QRhiReadbackResult texReadResult; + texReadResult.completed = [&readCompleted] { readCompleted = true; }; + + { + QScopedPointer<QRhiBuffer> dynamicBuffer(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, bufferSize)); + QVERIFY(dynamicBuffer->create()); + + for (int i = 0; i < bufferSize; ++i) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + + // One byte every 16.66 ms should be enough for everyone: fill up + // the buffer with "123456789abcdefghi", one byte in each frame. + if (i >= bufferSize / 2) + batch->updateDynamicBuffer(dynamicBuffer.data(), i, 1, b + (i - bufferSize / 2)); + else + batch->updateDynamicBuffer(dynamicBuffer.data(), i, 1, a + i); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + // just clear to black, but submit the resource update + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + } + + { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + readCompleted = false; + batch->readBackBuffer(dynamicBuffer.data(), 0, bufferSize, &readResult); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + + // This is a proper, typically at least double buffered renderer (as + // a real swapchain is involved). readCompleted may only become true + // in a future frame. + while (!readCompleted) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + rhi->endFrame(swapChain.data()); + } + + QVERIFY(readResult.data.size() == bufferSize); + QCOMPARE(readResult.data.left(bufferSize / 2), QByteArray(a)); + QCOMPARE(readResult.data.mid(bufferSize / 2), QByteArray(b)); + } + } + + // Repeat for types Immutable and Static, declare Vertex usage. + // This may not be readable on GLES 2.0 so skip the verification then. + for (QRhiBuffer::Type type : { QRhiBuffer::Immutable, QRhiBuffer::Static }) { + QScopedPointer<QRhiBuffer> buffer(rhi->newBuffer(type, QRhiBuffer::VertexBuffer, bufferSize)); + QVERIFY(buffer->create()); + + for (int i = 0; i < bufferSize; ++i) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + if (i >= bufferSize / 2) + batch->uploadStaticBuffer(buffer.data(), i, 1, b + (i - bufferSize / 2)); + else + batch->uploadStaticBuffer(buffer.data(), i, 1, a + i); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + } + + if (rhi->isFeatureSupported(QRhi::ReadBackNonUniformBuffer)) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + readCompleted = false; + batch->readBackBuffer(buffer.data(), 0, bufferSize, &readResult); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + + while (!readCompleted) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + rhi->endFrame(swapChain.data()); + } + + QVERIFY(readResult.data.size() == bufferSize); + QCOMPARE(readResult.data.left(bufferSize / 2), QByteArray(a)); + QCOMPARE(readResult.data.mid(bufferSize / 2), QByteArray(b)); + } else { + qDebug("Skipping verification of buffer data as ReadBackNonUniformBuffer is not supported"); + } + } + + // Now exercise a texture. Internally this is expected (with low level APIs + // at least) to be similar to what happens with a staic buffer: copy to host + // visible staging buffer, enqueue buffer-to-buffer (or here + // buffer-to-image) copy. + { + const int w = 234; + const int h = 8; // use a small height because vsync throttling is active + const QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::gray, Qt::yellow, Qt::black, Qt::white, Qt::magenta }; + QImage image(w, h, QImage::Format_RGBA8888); + for (int i = 0; i < h; ++i) { + QRgb c = colors[i].rgb(); + uchar *p = image.scanLine(i); + int x = w; + while (x--) { + *p++ = qRed(c); + *p++ = qGreen(c); + *p++ = qBlue(c); + *p++ = qAlpha(c); + } + } + + QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(w, h), 1, QRhiTexture::UsedAsTransferSource)); + QVERIFY(texture->create()); + + // fill a texture from the image, two lines at a time + for (int i = 0; i < h / 2; ++i) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + + QRhiTextureSubresourceUploadDescription subresDesc(image); + subresDesc.setSourceSize(QSize(w, 2)); + subresDesc.setSourceTopLeft(QPoint(0, i * 2)); + subresDesc.setDestinationTopLeft(QPoint(0, i * 2)); + QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, subresDesc)); + batch->uploadTexture(texture.data(), uploadDesc); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + } + + { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + + QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch(); + readCompleted = false; + batch->readBackTexture(texture.data(), &texReadResult); + + QRhiCommandBuffer *cb = swapChain->currentFrameCommandBuffer(); + cb->beginPass(swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, batch); + cb->endPass(); + + rhi->endFrame(swapChain.data()); + + while (!readCompleted) { + QVERIFY(rhi->beginFrame(swapChain.data()) == QRhi::FrameOpSuccess); + rhi->endFrame(swapChain.data()); + } + + QCOMPARE(texReadResult.pixelSize, image.size()); + QImage wrapperImage(reinterpret_cast<const uchar *>(texReadResult.data.constData()), + texReadResult.pixelSize.width(), texReadResult.pixelSize.height(), + image.format()); + QVERIFY(imageRGBAEquals(image, wrapperImage)); + } + } +} + void tst_QRhi::srbLayoutCompatibility_data() { rhiTestData(); |