diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2020-05-13 11:42:35 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2020-05-18 18:49:06 +0200 |
commit | 1e30ec77aaedcc29ba7ac1c3ec3937ee0b8287e9 (patch) | |
tree | f73fe032c760f70c239c4cc53b58e18fb1b7f153 /src | |
parent | 6ef31dfb484321ec6f5a3ffe27fef98e7c1cd3b0 (diff) |
rhi: Add support for grabbing non-exposed windows
tst_qquickwindow::grab is now expected to pass in all combinations
(both direct GL and RHI with any backend).
Task-number: QTBUG-78608
Change-Id: I8c1f2ff3d50144c7dd4498160818d0860b67db15
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 19 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrenderloop.cpp | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrhisupport.cpp | 79 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrhisupport_p.h | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgthreadedrenderloop.cpp | 2 |
5 files changed, 94 insertions, 12 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c53fd00e45..05819c430f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4393,15 +4393,18 @@ QImage QQuickWindow::grabWindow() // backends like software can grab regardless of the window state if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose)) return d->windowManager->grab(this); - } - if (!isVisible() && !d->renderControl) { - if (d->rhi) { - if (d->windowManager) - return d->windowManager->grab(this); // ### we may need a full offscreen round when non-exposed? - return QImage(); + if (!isSceneGraphInitialized() && QSGRhiSupport::instance()->isRhiEnabled()) { + // We do not have rendering up and running. Forget the render loop, + // do a frame completely offscreen and synchronously into a + // texture. This can be *very* slow due to all the device/context + // and resource initialization but the documentation warns for it, + // and is still important for some use cases. + Q_ASSERT(!d->rhi); + return QSGRhiSupport::instance()->grabOffscreen(this); } + // ### Qt 6 remove #if QT_CONFIG(opengl) auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context); if (!openglRenderContext->openglContext()) { @@ -4435,6 +4438,10 @@ QImage QQuickWindow::grabWindow() #endif } + // The common case: we have an exposed window with an initialized + // scenegraph, meaning we can request grabbing via the render loop, or we + // are not targeting the window, in which case the request is to be + // forwarded to the rendercontrol. if (d->renderControl) return QQuickRenderControlPrivate::get(d->renderControl)->grab(); else if (d->windowManager) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index aa956fda9e..61837fb7bb 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -774,7 +774,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (data.grabOnly) { const bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255; if (cd->swapchain) - grabContent = rhiSupport->grabAndBlockInCurrentFrame(rhi, cd->swapchain); + grabContent = rhiSupport->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); else grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha); grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio()); diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp index b86667a972..df1a0e072c 100644 --- a/src/quick/scenegraph/qsgrhisupport.cpp +++ b/src/quick/scenegraph/qsgrhisupport.cpp @@ -52,6 +52,7 @@ #endif #include <QOperatingSystemVersion> +#include <QOffscreenSurface> QT_BEGIN_NAMESPACE @@ -630,16 +631,16 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree return rhi; } -QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain) +QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiCommandBuffer *cb, QRhiTexture *src) { Q_ASSERT(rhi->isRecordingFrame()); QRhiReadbackResult result; - QRhiReadbackDescription readbackDesc; // read from swapchain backbuffer + QRhiReadbackDescription readbackDesc(src); // null src == read from swapchain backbuffer QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch(); resourceUpdates->readBackTexture(readbackDesc, &result); - swapchain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates); + cb->resourceUpdate(resourceUpdates); rhi->finish(); // make sure the readback has finished, stall the pipeline if needed // May be RGBA or BGRA. Plus premultiplied alpha. @@ -664,6 +665,78 @@ QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapc return img.copy(); } +QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window) +{ + // Set up and then tear down the entire rendering infrastructure. This + // function is called on the gui/main thread - but that's alright because + // there is no onscreen rendering initialized at this point (so no render + // thread for instance). + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + // It is expected that window is not using QQuickRenderControl, i.e. it is + // a normal QQuickWindow that just happens to be not exposed. + Q_ASSERT(!wd->renderControl); + + QScopedPointer<QOffscreenSurface> offscreenSurface(maybeCreateOffscreenSurface(window)); + QScopedPointer<QRhi> rhi(createRhi(window, offscreenSurface.data())); + if (!rhi) { + qWarning("Failed to initialize QRhi for offscreen readback"); + return QImage(); + } + + const QSize pixelSize = window->size() * window->devicePixelRatio(); + QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); + if (!texture->build()) { + qWarning("Failed to build texture for offscreen readback"); + return QImage(); + } + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + if (!rt->build()) { + qWarning("Failed to build render target for offscreen readback"); + return QImage(); + } + + wd->rhi = rhi.data(); + + QSGDefaultRenderContext::InitParams params; + params.rhi = rhi.data(); + params.sampleCount = 1; + params.initialSurfacePixelSize = pixelSize; + params.maybeSurface = window; + wd->context->initialize(¶ms); + + // There was no rendercontrol which means a custom render target + // should not be set either. Set our own, temporarily. + window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(rt.data())); + + QRhiCommandBuffer *cb = nullptr; + if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) { + qWarning("Failed to start recording the frame for offscreen readback"); + return QImage(); + } + + wd->setCustomCommandBuffer(cb); + wd->polishItems(); + wd->syncSceneGraph(); + wd->renderSceneGraph(window->size()); + wd->setCustomCommandBuffer(nullptr); + + QImage image = grabAndBlockInCurrentFrame(rhi.data(), cb, texture.data()); + rhi->endOffscreenFrame(); + + image.setDevicePixelRatio(window->devicePixelRatio()); + wd->cleanupNodesOnShutdown(); + wd->context->invalidate(); + + window->setRenderTarget(QQuickRenderTarget()); + wd->rhi = nullptr; + + return image; +} + QSGRhiProfileConnection *QSGRhiProfileConnection::instance() { static QSGRhiProfileConnection inst; diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h index ec7ebbbfd1..9e9b6f88bc 100644 --- a/src/quick/scenegraph/qsgrhisupport_p.h +++ b/src/quick/scenegraph/qsgrhisupport_p.h @@ -126,7 +126,9 @@ public: QOffscreenSurface *maybeCreateOffscreenSurface(QWindow *window); QRhi *createRhi(QQuickWindow *window, QOffscreenSurface *offscreenSurface); - QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain); + QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiCommandBuffer *cb, QRhiTexture *src = nullptr); + + QImage grabOffscreen(QQuickWindow *window); static void checkEnvQSgInfo(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 0a62f85388..e59b4aaabc 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -856,7 +856,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) // through syncAndRender(). if (grabRequested) { Q_ASSERT(rhi && !gl && cd->swapchain); - *grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain); + *grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer()); } if (cd->swapchain) { |