aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickwindow.cpp')
-rw-r--r--src/quick/items/qquickwindow.cpp295
1 files changed, 255 insertions, 40 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 4874af44ed..bb3aff0cd6 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -43,6 +43,7 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
+#include "qquickgraphicsdevice_p.h"
#if QT_CONFIG(quick_draganddrop)
#include <private/qquickdrag_p.h>
@@ -424,16 +425,66 @@ void forceUpdate(QQuickItem *item)
forceUpdate(items.at(i));
}
+void QQuickWindowRenderTarget::reset(QRhi *rhi)
+{
+ if (rhi && owns) {
+ delete renderTarget;
+ delete rpDesc;
+ delete texture;
+ delete depthStencil;
+ }
+
+ renderTarget = nullptr;
+ rpDesc = nullptr;
+ texture = nullptr;
+ depthStencil = nullptr;
+ owns = false;
+}
+
+void QQuickWindowPrivate::ensureCustomRenderTarget()
+{
+ // resolve() can be expensive when importing an existing native texture, so
+ // it is important to only do it when the QQuickRenderTarget* was really changed
+ if (!redirect.renderTargetDirty || !rhi)
+ return;
+
+ redirect.renderTargetDirty = false;
+
+ redirect.rt.reset(rhi);
+
+ // a default constructed QQuickRenderTarget means no redirection
+ if (customRenderTarget.isNull())
+ return;
+
+ QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt);
+}
+
+void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb)
+{
+ // ownership not transferred
+ redirect.commandBuffer = cb;
+}
+
void QQuickWindowPrivate::syncSceneGraph()
{
Q_Q(QQuickWindow);
+ ensureCustomRenderTarget();
+
// Calculate the dpr the same way renderSceneGraph() will.
qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (renderTargetId && !QQuickRenderControl::renderWindowFor(q))
+ const bool hasCustomRenderTarget = customRenderTargetGl.renderTargetId || redirect.rt.renderTarget;
+ if (hasCustomRenderTarget && !QQuickRenderControl::renderWindowFor(q))
devicePixelRatio = 1;
- context->prepareSync(devicePixelRatio, rhi ? swapchain->currentFrameCommandBuffer() : nullptr);
+ QRhiCommandBuffer *cb = nullptr;
+ if (rhi) {
+ if (redirect.commandBuffer)
+ cb = redirect.commandBuffer;
+ else
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->prepareSync(devicePixelRatio, cb);
animationController->beforeNodeSync();
@@ -484,11 +535,32 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
return;
if (rhi) {
- // ### no offscreen ("renderTargetId") support yet
- context->beginNextRhiFrame(renderer,
- swapchain->currentFrameRenderTarget(),
- rpDescForSwapchain,
- swapchain->currentFrameCommandBuffer(),
+ ensureCustomRenderTarget();
+ QRhiRenderTarget *rt;
+ QRhiRenderPassDescriptor *rp;
+ QRhiCommandBuffer *cb;
+ if (redirect.rt.renderTarget) {
+ rt = redirect.rt.renderTarget;
+ rp = rt->renderPassDescriptor();
+ if (!rp) {
+ qWarning("Custom render target is set but no renderpass descriptor has been provided.");
+ return;
+ }
+ cb = redirect.commandBuffer;
+ if (!cb) {
+ qWarning("Custom render target is set but no command buffer has been provided.");
+ return;
+ }
+ } else {
+ if (!swapchain) {
+ qWarning("QQuickWindow: No render target (neither swapchain nor custom target was provided)");
+ return;
+ }
+ rt = swapchain->currentFrameRenderTarget();
+ rp = rpDescForSwapchain;
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->beginNextRhiFrame(renderer, rt, rp, cb,
emitBeforeRenderPassRecording,
emitAfterRenderPassRecording,
q);
@@ -503,18 +575,28 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
if (!customRenderStage || !customRenderStage->render()) {
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
+ const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
int fboId = 0;
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (renderTargetId) {
- QRect rect(QPoint(0, 0), renderTargetSize);
- fboId = renderTargetId;
+ const bool hasCustomRenderTarget = customRenderTargetGl.renderTargetId || redirect.rt.renderTarget;
+ if (hasCustomRenderTarget) {
+ QRect rect;
+ if (redirect.rt.renderTarget) {
+ rect = QRect(QPoint(0, 0), redirect.rt.renderTarget->pixelSize());
+ } else {
+ fboId = customRenderTargetGl.renderTargetId;
+ rect = QRect(QPoint(0, 0), customRenderTargetGl.renderTargetSize);
+ }
renderer->setDeviceRect(rect);
renderer->setViewportRect(rect);
if (QQuickRenderControl::renderWindowFor(q)) {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size));
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags);
renderer->setDevicePixelRatio(devicePixelRatio);
} else {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()));
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()), matrixFlags);
renderer->setDevicePixelRatio(1);
}
} else {
@@ -530,10 +612,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
QRect rect(QPoint(0, 0), pixelSize);
renderer->setDeviceRect(rect);
renderer->setViewportRect(rect);
- const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
- QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
- if (flipY)
- matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
renderer->setDevicePixelRatio(devicePixelRatio);
}
@@ -589,8 +667,6 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, allowChildEventFiltering(true)
, allowDoubleClick(true)
, lastFocusReason(Qt::OtherFocusReason)
- , renderTarget(nullptr)
- , renderTargetId(0)
, vaoHelper(nullptr)
, incubationController(nullptr)
, hasActiveSwapchain(false)
@@ -604,6 +680,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
QQuickWindowPrivate::~QQuickWindowPrivate()
{
+ redirect.rt.reset(rhi);
delete customRenderStage;
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->removeWindow(q_func());
@@ -652,7 +729,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
q->setFormat(sg->defaultSurfaceFormat());
#if QT_CONFIG(vulkan)
- if (q->surfaceType() == QSurface::VulkanSurface)
+ if (!renderControl && q->surfaceType() == QSurface::VulkanSurface)
q->setVulkanInstance(QSGRhiSupport::vulkanInstance());
#endif
@@ -4028,13 +4105,13 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
return;
}
- d->renderTarget = fbo;
+ d->customRenderTargetGl.renderTarget = fbo;
if (fbo) {
- d->renderTargetId = fbo->handle();
- d->renderTargetSize = fbo->size();
+ d->customRenderTargetGl.renderTargetId = fbo->handle();
+ d->customRenderTargetGl.renderTargetSize = fbo->size();
} else {
- d->renderTargetId = 0;
- d->renderTargetSize = QSize();
+ d->customRenderTargetGl.renderTargetId = 0;
+ d->customRenderTargetGl.renderTargetSize = QSize();
}
}
#endif
@@ -4069,11 +4146,11 @@ void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
return;
}
- d->renderTargetId = fboId;
- d->renderTargetSize = size;
+ d->customRenderTargetGl.renderTargetId = fboId;
+ d->customRenderTargetGl.renderTargetSize = size;
// Unset any previously set instance...
- d->renderTarget = nullptr;
+ d->customRenderTargetGl.renderTarget = nullptr;
}
@@ -4083,7 +4160,7 @@ void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
uint QQuickWindow::renderTargetId() const
{
Q_D(const QQuickWindow);
- return d->renderTargetId;
+ return d->customRenderTargetGl.renderTargetId;
}
/*!
@@ -4092,11 +4169,9 @@ uint QQuickWindow::renderTargetId() const
QSize QQuickWindow::renderTargetSize() const
{
Q_D(const QQuickWindow);
- return d->renderTargetSize;
+ return d->customRenderTargetGl.renderTargetSize;
}
-
-
#if QT_CONFIG(opengl)
/*!
Returns the render target for this window.
@@ -4113,11 +4188,84 @@ QSize QQuickWindow::renderTargetSize() const
QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
{
Q_D(const QQuickWindow);
- return d->renderTarget;
+ return d->customRenderTargetGl.renderTarget;
}
#endif
/*!
+ Sets the render target for this window to be \a target.
+
+ A QQuickRenderTarget serves as an opaque handle for a renderable native
+ object, most commonly a 2D texture, and associated metadata, such as the
+ size in pixels.
+
+ A default constructed QQuickRenderTarget means no redirection. A valid
+ \a target, created via one of the static QQuickRenderTarget factory functions,
+ on the other hand, enables redirection of the rendering of the Qt Quick
+ scene: it will no longer target the color buffers for the surface
+ associated with the window, but rather the textures or other graphics
+ objects specified in \a target.
+
+ For example, assuming the scenegraph is using Vulkan to render, one can
+ redirect its output into a \c VkImage. For graphics APIs like Vulkan, the
+ image layout must be provided as well. QQuickRenderTarget instances are
+ implicitly shared and are copyable and can be passed by value. They do not
+ own the associated native objects (such as, the VkImage in the example),
+ however.
+
+ \badcode
+ QQuickRenderTarget rt = QQuickRenderTarget::fromNativeTexture({ &vulkanImage, VK_IMAGE_LAYOUT_PREINITIALIZED }, pixelSize);
+ quickWindow->setRenderTarget(rt);
+ \endcode
+
+ This function is very often used in combination with QQuickRenderControl
+ and an invisible QQuickWindow, in order to render Qt Quick content into a
+ texture, without creating an on-screen native window for this QQuickWindow.
+
+ When the desired target, or associated data, such as the size, changes,
+ call this function with a new QQuickRenderTarget. Constructing
+ QQuickRenderTarget instances and calling this function is cheap, but be
+ aware that setting a new \a target with a different native object or other
+ data may lead to potentially expensive initialization steps when the
+ scenegraph is about to render the next frame. Therefore change the target
+ only when necessary.
+
+ \note This function should not be used when using the \c software backend.
+ Instead, use grabWindow() to render the content into a QImage.
+
+ \note The window does not take ownership of any native objects referenced
+ in \a target.
+
+ \note It is the caller's responsibility to ensure the native objects
+ referred to in \a target are valid for the scenegraph renderer too. For
+ instance, with Vulkan, Metal, and Direct3D this implies that the texture or
+ image is created on the same graphics device that is used by the scenegraph
+ internally. This is often achieved by using this function in combination
+ with setGraphicsDevice().
+
+ \note With graphics APIs where relevant, the application must pay attention
+ to image layout transitions performed by the scenegraph. For example, once
+ a VkImage is associated with the scenegraph by calling this function, its
+ layout will transition to \c VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL when
+ rendering a frame.
+
+ \warning This function can only be called from the thread doing the
+ rendering.
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setGraphicsDevice(), setSceneGraphBackend()
+ */
+void QQuickWindow::setRenderTarget(const QQuickRenderTarget &target)
+{
+ Q_D(QQuickWindow);
+ if (target != d->customRenderTarget) {
+ d->customRenderTarget = target;
+ d->redirect.renderTargetDirty = true;
+ }
+}
+
+/*!
Grabs the contents of the window and returns it as an image.
It is possible to call the grabWindow() function when the window is not
@@ -4125,6 +4273,14 @@ QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
and has a valid size and that no other QQuickWindow instances are rendering
in the same process.
+ \note When using this window in combination with QQuickRenderControl, the
+ result of this function is an empty image, unless the \c software backend
+ is in use. This is because when redirecting the output to an
+ application-managed graphics resource (such as, a texture) by using
+ QQuickRenderControl and setRenderTarget(), the application is better suited
+ for managing and executing an eventual read back operation, since it is in
+ full control of the resource to begin with.
+
\warning Calling this function will cause performance problems.
\warning This function can only be called from the GUI thread.
@@ -4141,12 +4297,8 @@ QImage QQuickWindow::grabWindow()
if (!isVisible() && !d->renderControl) {
if (d->rhi) {
- // ### we may need a full offscreen round when non-exposed...
-
- if (d->renderControl)
- return d->renderControl->grab();
- else if (d->windowManager)
- return d->windowManager->grab(this);
+ if (d->windowManager)
+ return d->windowManager->grab(this); // ### we may need a full offscreen round when non-exposed?
return QImage();
}
@@ -4184,7 +4336,7 @@ QImage QQuickWindow::grabWindow()
}
if (d->renderControl)
- return d->renderControl->grab();
+ return QQuickRenderControlPrivate::get(d->renderControl)->grab();
else if (d->windowManager)
return d->windowManager->grab(this);
return QImage();
@@ -5648,7 +5800,11 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
loaded plugins.
\note The call to the function must happen before constructing the first
- QQuickWindow in the application. It cannot be changed afterwards.
+ QQuickWindow in the application. The graphics API cannot be changed
+ afterwards. When used in combination with QQuickRenderControl, this rule is
+ relaxed: it is possible to change the graphics API, but only when all
+ existing QQuickRenderControl and QQuickWindow instances have been
+ destroyed.
If the selected backend is invalid or an error occurs, the default backend
(OpenGL or software, depending on the Qt configuration) is used.
@@ -5709,6 +5865,65 @@ QString QQuickWindow::sceneGraphBackend()
}
/*!
+ Sets the graphics device objects for this window. The scenegraph will use
+ existing device, physical device, and other objects specified by \a device
+ instead of creating new ones.
+
+ This function is very often used in combination with QQuickRenderControl
+ and setRenderTarget(), in order to redirect Qt Quick rendering into a
+ texture.
+
+ A default constructed QQuickGraphicsDevice does not change the default
+ behavior in any way. Once a \a device created via one of the
+ QQuickGraphicsDevice factory functions, such as,
+ QQuickGraphicsDevice::fromDeviceObjects(), is passed in, and the scenegraph
+ uses a matching graphics API (with the example of fromDeviceObjects(), that
+ would be Vulkan), the scenegraph will use the existing device objects (such
+ as, the \c VkPhysicalDevice, \c VkDevice, and graphics queue family index,
+ in case of Vulkan) encapsulated by the QQuickGraphicsDevice. This allows
+ using the same device, and so sharing resources, such as buffers and
+ textures, between Qt Quick and native rendering engines.
+
+ \warning This function can only be called before initializing the
+ scenegraph and will have no effect if called afterwards. In practice this
+ typically means calling it right before QQuickRenderControl::initialize().
+
+ As an example, this time with Direct3D, the typical usage is expected to be
+ the following:
+
+ \badcode
+ // native graphics resources set up by a custom D3D rendering engine
+ ID3D11Device *device;
+ ID3D11DeviceContext *context;
+ ID3D11Texture2D *texture;
+ ...
+ // now to redirect Qt Quick content into 'texture' we could do the following:
+ QQuickRenderControl *renderControl = new QQuickRenderControl;
+ QQuickWindow *window = new QQuickWindow(renderControl); // this window will never be shown on-screen
+ ...
+ window->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(device, context));
+ renderControl->initialize();
+ window->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ &texture, 0 }, textureSize);
+ ...
+ \endcode
+
+ The key aspect of using this function is to ensure that resources or
+ handles to resources, such as \c texture in the above example, are visible
+ to and usable by both the external rendering engine and the scenegraph
+ renderer. This requires using the same graphics device (or with OpenGL,
+ OpenGL context).
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setRenderTarget(), setSceneGraphBackend()
+ */
+void QQuickWindow::setGraphicsDevice(const QQuickGraphicsDevice &device)
+{
+ Q_D(QQuickWindow);
+ d->customDeviceObjects = device;
+}
+
+/*!
Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
This is cross-backend alternative to constructing a QSGSimpleRectNode directly.