aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-05-13 11:42:35 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2020-05-18 18:49:06 +0200
commit1e30ec77aaedcc29ba7ac1c3ec3937ee0b8287e9 (patch)
treef73fe032c760f70c239c4cc53b58e18fb1b7f153 /src
parent6ef31dfb484321ec6f5a3ffe27fef98e7c1cd3b0 (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.cpp19
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp2
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp79
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h4
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp2
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(&params);
+
+ // 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) {