| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #include "qquickrendercontrol.h" | 
| 5 | #include "qquickrendercontrol_p.h" | 
| 6 |  | 
| 7 | #include <QtCore/QCoreApplication> | 
| 8 | #include <QtCore/QTime> | 
| 9 | #include <QtQuick/private/qquickanimatorcontroller_p.h> | 
| 10 | #include <QtQuick/private/qsgdefaultrendercontext_p.h> | 
| 11 | #include <QtQuick/private/qsgrhisupport_p.h> | 
| 12 |  | 
| 13 | #include <private/qsgrhishadereffectnode_p.h> | 
| 14 |  | 
| 15 | #include <QtGui/private/qguiapplication_p.h> | 
| 16 | #include <qpa/qplatformintegration.h> | 
| 17 | #include <QtGui/qoffscreensurface.h> | 
| 18 |  | 
| 19 | #include <QtQml/private/qqmlglobal_p.h> | 
| 20 |  | 
| 21 | #include <QtQuick/QQuickWindow> | 
| 22 | #include <QtQuick/QQuickRenderTarget> | 
| 23 | #include <QtQuick/private/qquickwindow_p.h> | 
| 24 | #include <QtQuick/private/qquickitem_p.h> | 
| 25 | #include <QtQuick/private/qsgsoftwarerenderer_p.h> | 
| 26 | #include <QtCore/private/qobject_p.h> | 
| 27 |  | 
| 28 | #include <QtQuick/private/qquickwindow_p.h> | 
| 29 | #include <rhi/qrhi.h> | 
| 30 |  | 
| 31 | QT_BEGIN_NAMESPACE | 
| 32 |  | 
| 33 | /*! | 
| 34 |   \class QQuickRenderControl | 
| 35 |  | 
| 36 |   \brief The QQuickRenderControl class provides a mechanism for rendering the Qt | 
| 37 |   Quick scenegraph onto an offscreen render target in a fully | 
| 38 |   application-controlled manner. | 
| 39 |  | 
| 40 |   \since 5.4 | 
| 41 |  | 
| 42 |   QQuickWindow and QQuickView and their associated internal render loops render | 
| 43 |   the Qt Quick scene onto a native window. In some cases, for example when | 
| 44 |   integrating with 3rd party OpenGL, Vulkan, Metal, or Direct 3D renderers, it | 
| 45 |   can be useful to get the scene into a texture that can then be used in | 
| 46 |   arbitrary ways by the external rendering engine. Such a mechanism is also | 
| 47 |   essential when integrating with a VR framework. QQuickRenderControl makes this | 
| 48 |   possible in a hardware accelerated manner, unlike the performance-wise limited | 
| 49 |   alternative of using QQuickWindow::grabWindow() | 
| 50 |  | 
| 51 |   When using a QQuickRenderControl, the QQuickWindow must not be | 
| 52 |   \l{QWindow::show()}{shown} (it will not be visible on-screen) and there will | 
| 53 |   not be an underlying native window for it. Instead, the QQuickWindow instance | 
| 54 |   is associated with the render control object, using the overload of the | 
| 55 |   QQuickWindow constructor, and a texture or image object specified via | 
| 56 |   QQuickWindow::setRenderTarget(). The QQuickWindow object is still essential, | 
| 57 |   because it represents the Qt Quick scene and provides the bulk of the scene | 
| 58 |   management and event delivery mechanisms. It does not however act as a real | 
| 59 |   on-screen window from the windowing system's perspective. | 
| 60 |  | 
| 61 |   Management of the graphics devices, contexts, image and texture objects is up | 
| 62 |   to the application. The device or context that will be used by Qt Quick must | 
| 63 |   be created before calling initialize(). The creation of the texture object | 
| 64 |   can be deferred, see below. Qt 5.4 introduces the ability for QOpenGLContext | 
| 65 |   to adopt existing native contexts. Together with QQuickRenderControl this | 
| 66 |   makes it possible to create a QOpenGLContext that shares with an external | 
| 67 |   rendering engine's existing context. This new QOpenGLContext can then be used | 
| 68 |   to render the Qt Quick scene into a texture that is accessible by the other | 
| 69 |   engine's context too. For Vulkan, Metal, and Direct 3D there are no | 
| 70 |   Qt-provided wrappers for device objects, so existing ones can be passed as-is | 
| 71 |   via QQuickWindow::setGraphicsDevice(). | 
| 72 |  | 
| 73 |   Loading and instantiation of the QML components happen by using a | 
| 74 |   QQmlEngine. Once the root object is created, it will need to be parented to | 
| 75 |   the QQuickWindow's contentItem(). | 
| 76 |  | 
| 77 |   Applications will usually have to connect to 4 important signals: | 
| 78 |  | 
| 79 |   \list | 
| 80 |  | 
| 81 |   \li QQuickWindow::sceneGraphInitialized() Emitted at some point after calling | 
| 82 |   QQuickRenderControl::initialize(). Upon this signal, the application is | 
| 83 |   expected to create its framebuffer object and associate it with the | 
| 84 |   QQuickWindow. | 
| 85 |  | 
| 86 |   \li QQuickWindow::sceneGraphInvalidated() When the scenegraph resources are | 
| 87 |   released, the framebuffer object can be destroyed too. | 
| 88 |  | 
| 89 |   \li QQuickRenderControl::renderRequested() Indicates that the scene has to be | 
| 90 |   rendered by calling render(). After making the context current, applications | 
| 91 |   are expected to call render(). | 
| 92 |  | 
| 93 |   \li QQuickRenderControl::sceneChanged() Indicates that the scene has changed | 
| 94 |   meaning that, before rendering, polishing and synchronizing is also necessary. | 
| 95 |  | 
| 96 |   \endlist | 
| 97 |  | 
| 98 |   To send events, for example mouse or keyboard events, to the scene, use | 
| 99 |   QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver. | 
| 100 |  | 
| 101 |   For key events it may be also necessary to set the focus manually on the | 
| 102 |   desired item. In practice this involves calling | 
| 103 |   \l{QQuickItem::forceActiveFocus()}{forceActiveFocus()} on the desired item, | 
| 104 |   for example the scene's root item, once it is associated with the scene (the | 
| 105 |   QQuickWindow). | 
| 106 |  | 
| 107 |   \note In general QQuickRenderControl is supported in combination with all Qt | 
| 108 |   Quick backends. However, some functionality, in particular grab(), may not be | 
| 109 |   available in all cases. | 
| 110 |  | 
| 111 |   \inmodule QtQuick | 
| 112 | */ | 
| 113 |  | 
| 114 | QSGContext *QQuickRenderControlPrivate::sg = nullptr; | 
| 115 |  | 
| 116 | QQuickRenderControlPrivate::QQuickRenderControlPrivate(QQuickRenderControl *renderControl) | 
| 117 |     : q(renderControl), | 
| 118 |       initialized(false), | 
| 119 |       window(nullptr), | 
| 120 |       rhi(nullptr), | 
| 121 |       ownRhi(true), | 
| 122 |       cb(nullptr), | 
| 123 |       offscreenSurface(nullptr), | 
| 124 |       sampleCount(1), | 
| 125 |       frameStatus(NotRecordingFrame) | 
| 126 | { | 
| 127 |     if (!sg) { | 
| 128 |         qAddPostRoutine(cleanup); | 
| 129 |         sg = QSGContext::createDefaultContext(); | 
| 130 |     } | 
| 131 |     rc = sg->createRenderContext(); | 
| 132 | } | 
| 133 |  | 
| 134 | void QQuickRenderControlPrivate::cleanup() | 
| 135 | { | 
| 136 |     delete sg; | 
| 137 |     sg = nullptr; | 
| 138 | } | 
| 139 |  | 
| 140 | /*! | 
| 141 |    Constructs a QQuickRenderControl object, with parent | 
| 142 |    object \a parent. | 
| 143 | */ | 
| 144 | QQuickRenderControl::QQuickRenderControl(QObject *parent) | 
| 145 |     : QObject(*(new QQuickRenderControlPrivate(this)), parent) | 
| 146 | { | 
| 147 | } | 
| 148 |  | 
| 149 | /*! | 
| 150 |  \internal | 
| 151 | */ | 
| 152 | QQuickRenderControl::QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent) | 
| 153 |     : QObject(dd, parent) | 
| 154 | { | 
| 155 | } | 
| 156 |  | 
| 157 | /*! | 
| 158 |   Destroys the instance. Releases all scenegraph resources. | 
| 159 |  | 
| 160 |   \sa invalidate() | 
| 161 |  */ | 
| 162 | QQuickRenderControl::~QQuickRenderControl() | 
| 163 | { | 
| 164 |     Q_D(QQuickRenderControl); | 
| 165 |  | 
| 166 |     invalidate(); | 
| 167 |  | 
| 168 |     QQuickGraphicsConfiguration config; | 
| 169 |     if (d->window) { | 
| 170 |         QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window); | 
| 171 |         wd->renderControl = nullptr; | 
| 172 |         config = wd->graphicsConfig; | 
| 173 |     } | 
| 174 |  | 
| 175 |     // It is likely that the cleanup in windowDestroyed() is not called since | 
| 176 |     // the standard pattern is to destroy the rendercontrol before the QQuickWindow. | 
| 177 |     // Do it here. | 
| 178 |     d->windowDestroyed(); | 
| 179 |  | 
| 180 |     delete d->rc; | 
| 181 |  | 
| 182 |     // Only call rhi related cleanup when we actually got to initialize() and | 
| 183 |     // managed to get a QRhi. The software backend for instance would mean | 
| 184 |     // using the rendercontrol without ever calling initialize() - it is then | 
| 185 |     // important to completely skip calling any QSGRhiSupport functions. | 
| 186 |     if (d->rhi) | 
| 187 |         d->resetRhi(config); | 
| 188 | } | 
| 189 |  | 
| 190 | void QQuickRenderControlPrivate::windowDestroyed() | 
| 191 | { | 
| 192 |     if (window) { | 
| 193 |         QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window); | 
| 194 |         cd->cleanupNodesOnShutdown(); | 
| 195 |  | 
| 196 |         rc->invalidate(); | 
| 197 |  | 
| 198 |         QQuickWindowPrivate::get(c: window)->animationController.reset(); | 
| 199 |  | 
| 200 | #if QT_CONFIG(quick_shadereffect) | 
| 201 |         QSGRhiShaderEffectNode::resetMaterialTypeCache(materialTypeCacheKey: window); | 
| 202 | #endif | 
| 203 |  | 
| 204 |         window = nullptr; | 
| 205 |     } | 
| 206 | } | 
| 207 |  | 
| 208 | /*! | 
| 209 |   Prepares rendering the Qt Quick scene outside the GUI thread. | 
| 210 |  | 
| 211 |   \a targetThread specifies the thread on which synchronization and | 
| 212 |   rendering will happen. There is no need to call this function in a | 
| 213 |   single threaded scenario. | 
| 214 |  */ | 
| 215 | void QQuickRenderControl::prepareThread(QThread *targetThread) | 
| 216 | { | 
| 217 |     Q_D(QQuickRenderControl); | 
| 218 |     d->rc->moveToThread(thread: targetThread); | 
| 219 |     QQuickWindowPrivate::get(c: d->window)->animationController->moveToThread(thread: targetThread); | 
| 220 | } | 
| 221 |  | 
| 222 | /*! | 
| 223 |     Sets the number of samples to use for multisampling. When \a sampleCount is | 
| 224 |     0 or 1, multisampling is disabled. | 
| 225 |  | 
| 226 |     \note This function is always used in combination with a multisample render | 
| 227 |     target, which means \a sampleCount must match the sample count passed to | 
| 228 |     QQuickRenderTarget::fromNativeTexture(), which in turn must match the | 
| 229 |     sample count of the native texture. | 
| 230 |  | 
| 231 |     \since 6.0 | 
| 232 |  | 
| 233 |     \sa initialize(), QQuickRenderTarget | 
| 234 |  */ | 
| 235 | void QQuickRenderControl::setSamples(int sampleCount) | 
| 236 | { | 
| 237 |     Q_D(QQuickRenderControl); | 
| 238 |     d->sampleCount = qMax(a: 1, b: sampleCount); | 
| 239 | } | 
| 240 |  | 
| 241 | /*! | 
| 242 |     \return the current sample count. 1 or 0 means no multisampling. | 
| 243 |  | 
| 244 |     \since 6.0 | 
| 245 |  */ | 
| 246 | int QQuickRenderControl::samples() const | 
| 247 | { | 
| 248 |     Q_D(const QQuickRenderControl); | 
| 249 |     return d->sampleCount; | 
| 250 | } | 
| 251 |  | 
| 252 | /*! | 
| 253 |     Initializes the scene graph resources. When using a graphics API, such as | 
| 254 |     Vulkan, Metal, OpenGL, or Direct3D, for Qt Quick rendering, | 
| 255 |     QQuickRenderControl will set up an appropriate rendering engine when this | 
| 256 |     function is called. This rendering infrastructure exists as long as the | 
| 257 |     QQuickRenderControl exists. | 
| 258 |  | 
| 259 |     To control what graphics API Qt Quick uses, call | 
| 260 |     QQuickWindow::setGraphicsApi() with one of the | 
| 261 |     QSGRendererInterface:GraphicsApi constants. That must be done before | 
| 262 |     calling this function. | 
| 263 |  | 
| 264 |     To prevent the scenegraph from creating its own device and context objects, | 
| 265 |     specify an appropriate QQuickGraphicsDevice, wrapping existing graphics | 
| 266 |     objects, by calling QQuickWindow::setGraphicsDevice(). | 
| 267 |  | 
| 268 |     To configure which device extensions to enable (for example, for Vulkan), | 
| 269 |     call QQuickWindow::setGraphicsConfiguration() before this function. | 
| 270 |  | 
| 271 |     \note When using Vulkan, QQuickRenderControl does not create a QVulkanInstance | 
| 272 |     automatically. Rather, it is the application's responsibility to create a | 
| 273 |     suitable QVulkanInstance and \l{QWindow::setVulkanInstance()}{associate it} with | 
| 274 |     the QQuickWindow. Before initializing the QVulkanInstance, it is strongly | 
| 275 |     encouraged to query the list of Qt Quick's desired instance extensions by calling | 
| 276 |     the static function QQuickGraphicsConfiguration::preferredInstanceExtensions() | 
| 277 |     and to pass the returned list to QVulkanInstance::setExtensions(). | 
| 278 |  | 
| 279 |     Returns \c true on success, \c false otherwise. | 
| 280 |  | 
| 281 |     \note This function does not need to be, and must not be, called when using | 
| 282 |     the \c software adaptation of Qt Quick. | 
| 283 |  | 
| 284 |     With the default Qt Quick adaptation this function creates a new \l QRhi | 
| 285 |     object, similarly to what would happen with an on-screen QQuickWindow when | 
| 286 |     QQuickRenderControl was not used. To make this new QRhi object adopt some | 
| 287 |     existing device or context resource (e.g. use an existing QOpenGLContext | 
| 288 |     instead of creating a new one), use QQuickWindow::setGraphicsDevice() as | 
| 289 |     mentioned above. When the application wants to make the Qt Quick rendering | 
| 290 |     use an already existing \l QRhi object, that is possible as well via | 
| 291 |     \l QQuickGraphicsDevice::fromRhi(). When such a QQuickGraphicsDevice, | 
| 292 |     referencing an already existing QRhi, is set, there will be no new, | 
| 293 |     dedicated \l QRhi object created in initialize(). | 
| 294 |  | 
| 295 |     \since 6.0 | 
| 296 |  | 
| 297 |     \sa QQuickRenderTarget, QQuickGraphicsDevice, QQuickGraphicsConfiguration::preferredInstanceExtensions() | 
| 298 |  */ | 
| 299 | bool QQuickRenderControl::initialize() | 
| 300 | { | 
| 301 |     Q_D(QQuickRenderControl); | 
| 302 |     if (!d->window) { | 
| 303 |         qWarning(msg: "QQuickRenderControl::initialize called with no associated window" ); | 
| 304 |         return false; | 
| 305 |     } | 
| 306 |  | 
| 307 |     if (!d->initRhi()) | 
| 308 |         return false; | 
| 309 |  | 
| 310 |     QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window); | 
| 311 |     wd->rhi = d->rhi; | 
| 312 |  | 
| 313 |     QSGDefaultRenderContext *renderContext = qobject_cast<QSGDefaultRenderContext *>(object: d->rc); | 
| 314 |     if (renderContext) { | 
| 315 |         QSGDefaultRenderContext::InitParams params; | 
| 316 |         params.rhi = d->rhi; | 
| 317 |         params.sampleCount = d->sampleCount; | 
| 318 |         params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio(); | 
| 319 |         params.maybeSurface = d->window; | 
| 320 |         renderContext->initialize(params: ¶ms); | 
| 321 |         d->initialized = true; | 
| 322 |     } else { | 
| 323 |         qWarning(msg: "QRhi is only compatible with default adaptation" ); | 
| 324 |         return false; | 
| 325 |     } | 
| 326 |     return true; | 
| 327 | } | 
| 328 |  | 
| 329 | /*! | 
| 330 |   This function should be called as late as possible before | 
| 331 |   sync(). In a threaded scenario, rendering can happen in parallel | 
| 332 |   with this function. | 
| 333 |  */ | 
| 334 | void QQuickRenderControl::polishItems() | 
| 335 | { | 
| 336 |     Q_D(QQuickRenderControl); | 
| 337 |     if (!d->window) | 
| 338 |         return; | 
| 339 |  | 
| 340 |     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window); | 
| 341 |     cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(win: d->window); | 
| 342 |     if (!d->window) | 
| 343 |         return; | 
| 344 |     cd->polishItems(); | 
| 345 |     emit d->window->afterAnimating(); | 
| 346 | } | 
| 347 |  | 
| 348 | /*! | 
| 349 |   This function is used to synchronize the QML scene with the rendering scene | 
| 350 |   graph. | 
| 351 |  | 
| 352 |   If a dedicated render thread is used, the GUI thread should be blocked for the | 
| 353 |   duration of this call. | 
| 354 |  | 
| 355 |   \return \e true if the synchronization changed the scene graph. | 
| 356 |  */ | 
| 357 | bool QQuickRenderControl::sync() | 
| 358 | { | 
| 359 |     Q_D(QQuickRenderControl); | 
| 360 |     if (!d->window) | 
| 361 |         return false; | 
| 362 |  | 
| 363 |     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window); | 
| 364 |     // we may not have a d->rhi (software backend) hence the check is important | 
| 365 |     if (d->rhi) { | 
| 366 |         if (!d->rhi->isRecordingFrame()) { | 
| 367 |             qWarning(msg: "QQuickRenderControl can only sync when beginFrame() has been called" ); | 
| 368 |             return false; | 
| 369 |         } | 
| 370 |         if (!d->cb) { | 
| 371 |             qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided "  | 
| 372 |                      "(perhaps beginFrame() was not called or it was unsuccessful?)" ); | 
| 373 |             return false; | 
| 374 |         } | 
| 375 |         cd->setCustomCommandBuffer(d->cb); | 
| 376 |     } | 
| 377 |  | 
| 378 |     cd->syncSceneGraph(); | 
| 379 |     d->rc->endSync(); | 
| 380 |  | 
| 381 |     return true; | 
| 382 | } | 
| 383 |  | 
| 384 | /*! | 
| 385 |   Stop rendering and release resources. | 
| 386 |  | 
| 387 |   This is the equivalent of the cleanup operations that happen with a | 
| 388 |   real QQuickWindow when the window becomes hidden. | 
| 389 |  | 
| 390 |   This function is called from the destructor. Therefore there will | 
| 391 |   typically be no need to call it directly. | 
| 392 |  | 
| 393 |   Once invalidate() has been called, it is possible to reuse the | 
| 394 |   QQuickRenderControl instance by calling initialize() again. | 
| 395 |  | 
| 396 |   \note This function does not take | 
| 397 |   QQuickWindow::persistentSceneGraph() or | 
| 398 |   QQuickWindow::persistentGraphics() into account. This means | 
| 399 |   that context-specific resources are always released. | 
| 400 |  */ | 
| 401 | void QQuickRenderControl::invalidate() | 
| 402 | { | 
| 403 |     Q_D(QQuickRenderControl); | 
| 404 |     if (!d->window) | 
| 405 |         return; | 
| 406 |  | 
| 407 |     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window); | 
| 408 |     cd->fireAboutToStop(); | 
| 409 |     cd->cleanupNodesOnShutdown(); | 
| 410 |  | 
| 411 |     if (!d->initialized) | 
| 412 |         return; | 
| 413 |  | 
| 414 |     // We must invalidate since the context can potentially be destroyed by the | 
| 415 |     // application right after returning from this function. Invalidating is | 
| 416 |     // also essential to allow a subsequent initialize() to succeed. | 
| 417 |     d->rc->invalidate(); | 
| 418 |  | 
| 419 |     d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame; | 
| 420 |     d->initialized = false; | 
| 421 | } | 
| 422 |  | 
| 423 | /*! | 
| 424 |   Renders the scenegraph using the current context. | 
| 425 |  */ | 
| 426 | void QQuickRenderControl::render() | 
| 427 | { | 
| 428 |     Q_D(QQuickRenderControl); | 
| 429 |     if (!d->window) | 
| 430 |         return; | 
| 431 |  | 
| 432 |     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window); | 
| 433 |     // we may not have a d->rhi (software backend) hence the check is important | 
| 434 |     if (d->rhi) { | 
| 435 |         if (!d->rhi->isRecordingFrame()) { | 
| 436 |             qWarning(msg: "QQuickRenderControl can only render when beginFrame() has been called" ); | 
| 437 |             return; | 
| 438 |         } | 
| 439 |         if (!d->cb) { | 
| 440 |             qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided" ); | 
| 441 |             return; | 
| 442 |         } | 
| 443 |         cd->setCustomCommandBuffer(d->cb); | 
| 444 |     } | 
| 445 |  | 
| 446 |     cd->renderSceneGraph(); | 
| 447 | } | 
| 448 |  | 
| 449 | /*! | 
| 450 |     \fn void QQuickRenderControl::renderRequested() | 
| 451 |  | 
| 452 |     This signal is emitted when the scene graph needs to be rendered. It is not necessary to call sync(). | 
| 453 |  | 
| 454 |     \note Avoid triggering rendering directly when this signal is | 
| 455 |     emitted. Instead, prefer deferring it by using a timer for example. This | 
| 456 |     will lead to better performance. | 
| 457 | */ | 
| 458 |  | 
| 459 | /*! | 
| 460 |     \fn void QQuickRenderControl::sceneChanged() | 
| 461 |  | 
| 462 |     This signal is emitted when the scene graph is updated, meaning that | 
| 463 |     polishItems() and sync() needs to be called. If sync() returns | 
| 464 |     true, then render() needs to be called. | 
| 465 |  | 
| 466 |     \note Avoid triggering polishing, synchronization and rendering directly | 
| 467 |     when this signal is emitted. Instead, prefer deferring it by using a timer | 
| 468 |     for example. This will lead to better performance. | 
| 469 | */ | 
| 470 |  | 
| 471 | QImage QQuickRenderControlPrivate::grab() | 
| 472 | { | 
| 473 |     if (!window) | 
| 474 |         return QImage(); | 
| 475 |  | 
| 476 |     QImage grabContent; | 
| 477 |  | 
| 478 |     if (rhi) { | 
| 479 |  | 
| 480 |         // As documented by QQuickWindow::grabWindow(): Nothing to do here, we | 
| 481 |         // do not support "grabbing" with an application-provided render target | 
| 482 |         // in Qt 6. (with the exception of the software backend because that | 
| 483 |         // does not support custom render targets, so the grab implementation | 
| 484 |         // here is still valuable) | 
| 485 |  | 
| 486 | #if QT_CONFIG(thread) | 
| 487 |     } else if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { | 
| 488 |         QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window); | 
| 489 |         cd->polishItems(); | 
| 490 |         cd->syncSceneGraph(); | 
| 491 |         QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer); | 
| 492 |         if (softwareRenderer) { | 
| 493 |             const qreal dpr = window->effectiveDevicePixelRatio(); | 
| 494 |             const QSize imageSize = window->size() * dpr; | 
| 495 |             grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); | 
| 496 |             grabContent.setDevicePixelRatio(dpr); | 
| 497 |             QPaintDevice *prevDev = softwareRenderer->currentPaintDevice(); | 
| 498 |             softwareRenderer->setCurrentPaintDevice(&grabContent); | 
| 499 |             softwareRenderer->markDirty(); | 
| 500 |             rc->endSync(); | 
| 501 |             q->render(); | 
| 502 |             softwareRenderer->setCurrentPaintDevice(prevDev); | 
| 503 |         } | 
| 504 | #endif | 
| 505 |     } else { | 
| 506 |         qWarning(msg: "QQuickRenderControl: grabs are not supported with the current Qt Quick backend" ); | 
| 507 |     } | 
| 508 |  | 
| 509 |     return grabContent; | 
| 510 | } | 
| 511 |  | 
| 512 | void QQuickRenderControlPrivate::update() | 
| 513 | { | 
| 514 |     Q_Q(QQuickRenderControl); | 
| 515 |     emit q->renderRequested(); | 
| 516 | } | 
| 517 |  | 
| 518 | void QQuickRenderControlPrivate::maybeUpdate() | 
| 519 | { | 
| 520 |     Q_Q(QQuickRenderControl); | 
| 521 |     emit q->sceneChanged(); | 
| 522 | } | 
| 523 |  | 
| 524 | /*! | 
| 525 |   \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset) | 
| 526 |  | 
| 527 |   Reimplemented in subclasses to return the real window this render control | 
| 528 |   is rendering into. | 
| 529 |  | 
| 530 |   If \a offset is non-null, it is set to the offset of the control | 
| 531 |   inside the window. | 
| 532 |  | 
| 533 |   \note While not mandatory, reimplementing this function becomes essential for | 
| 534 |   supporting multiple screens with different device pixel ratios and properly positioning | 
| 535 |   popup windows opened from QML. Therefore providing it in subclasses is highly | 
| 536 |   recommended. | 
| 537 | */ | 
| 538 |  | 
| 539 | /*! | 
| 540 |   Returns the real window that \a win is being rendered to, if any. | 
| 541 |  | 
| 542 |   If \a offset is non-null, it is set to the offset of the rendering | 
| 543 |   inside its window. | 
| 544 |  | 
| 545 |  */ | 
| 546 | QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset) | 
| 547 | { | 
| 548 |     if (!win) | 
| 549 |         return nullptr; | 
| 550 |     QQuickRenderControl *rc = QQuickWindowPrivate::get(c: win)->renderControl; | 
| 551 |     if (rc) | 
| 552 |         return rc->renderWindow(offset); | 
| 553 |     return nullptr; | 
| 554 | } | 
| 555 |  | 
| 556 | bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin) | 
| 557 | { | 
| 558 |     QQuickRenderControl *rc = QQuickWindowPrivate::get(c: quickWin)->renderControl; | 
| 559 |     if (rc) | 
| 560 |         return QQuickRenderControlPrivate::get(renderControl: rc)->isRenderWindow(w: renderWin); | 
| 561 |     return false; | 
| 562 | } | 
| 563 |  | 
| 564 | bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w) | 
| 565 | { | 
| 566 |     Q_Q(QQuickRenderControl); | 
| 567 |  | 
| 568 |     if (window && w) | 
| 569 |         return q->renderWindowFor(win: window, offset: nullptr) == w; | 
| 570 |  | 
| 571 |     return false; | 
| 572 | } | 
| 573 |  | 
| 574 | /*! | 
| 575 |     \return the QQuickWindow this QQuickRenderControl is associated with. | 
| 576 |  | 
| 577 |     \note A QQuickRenderControl gets associated with a QQuickWindow when | 
| 578 |     constructing the QQuickWindow. The return value from this function is null | 
| 579 |     before that point. | 
| 580 |  | 
| 581 |     \since 6.0 | 
| 582 |  */ | 
| 583 | QQuickWindow *QQuickRenderControl::window() const | 
| 584 | { | 
| 585 |     Q_D(const QQuickRenderControl); | 
| 586 |     return d->window; | 
| 587 | } | 
| 588 |  | 
| 589 | /*! | 
| 590 |     \return the QRhi this QQuickRenderControl is associated with. | 
| 591 |  | 
| 592 |     \note The QRhi exists only when initialize() has successfully completed. | 
| 593 |     Before that the return value is null. | 
| 594 |  | 
| 595 |     \note This function is not applicable and returns null when using the | 
| 596 |     \c software adaptation of Qt Quick. | 
| 597 |  | 
| 598 |     \since 6.6 | 
| 599 |  | 
| 600 |     \sa commandBuffer(), beginFrame(), endFrame() | 
| 601 |  */ | 
| 602 | QRhi *QQuickRenderControl::rhi() const | 
| 603 | { | 
| 604 |     Q_D(const QQuickRenderControl); | 
| 605 |     return d->rhi; | 
| 606 | } | 
| 607 |  | 
| 608 | /*! | 
| 609 |     \return the current command buffer. | 
| 610 |  | 
| 611 |     Once beginFrame() is called, a QRhiCommandBuffer is set up automatically. | 
| 612 |     That is the command buffer Qt Quick scenegraph uses, but in some cases | 
| 613 |     applications may also want to query it, for example to issue resource | 
| 614 |     updates (for example, a texture readback). | 
| 615 |  | 
| 616 |     The returned command buffer reference should only be used between | 
| 617 |     beginFrame() and endFrame(). There are specific exceptions, for example | 
| 618 |     calling | 
| 619 |     \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on | 
| 620 |     the command buffer right after endFrame(), but before the next | 
| 621 |     beginFrame(), is valid. | 
| 622 |  | 
| 623 |     \note This function is not applicable and returns null when using the | 
| 624 |     \c software adaptation of Qt Quick. | 
| 625 |  | 
| 626 |     \since 6.6 | 
| 627 |  | 
| 628 |     \sa rhi(), beginFrame(), endFrame() | 
| 629 |  */ | 
| 630 | QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const | 
| 631 | { | 
| 632 |     Q_D(const QQuickRenderControl); | 
| 633 |     return d->cb; | 
| 634 | } | 
| 635 |  | 
| 636 | /*! | 
| 637 |     Specifies the start of a graphics frame. Calls to sync() or render() must | 
| 638 |     be enclosed by calls to beginFrame() and endFrame(). | 
| 639 |  | 
| 640 |     Unlike the earlier OpenGL-only world of Qt 5, rendering with other graphics | 
| 641 |     APIs requires more well-defined points of starting and ending a frame. When | 
| 642 |     manually driving the rendering loop via QQuickRenderControl, it now falls | 
| 643 |     to the user of QQuickRenderControl to specify these points. | 
| 644 |  | 
| 645 |     A typical update step, including initialization of rendering into an | 
| 646 |     existing texture, could look like the following. The example snippet | 
| 647 |     assumes Direct3D 11 but the same concepts apply other graphics APIs as | 
| 648 |     well. | 
| 649 |  | 
| 650 |     \code | 
| 651 |         if (!m_quickInitialized) { | 
| 652 |             m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context())); | 
| 653 |  | 
| 654 |             if (!m_renderControl->initialize()) | 
| 655 |                 qWarning("Failed to initialize redirected Qt Quick rendering"); | 
| 656 |  | 
| 657 |             m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ quint64(m_res.texture), 0 }, | 
| 658 |                                                                                  QSize(QML_WIDTH, QML_HEIGHT), | 
| 659 |                                                                                  SAMPLE_COUNT)); | 
| 660 |  | 
| 661 |             m_quickInitialized = true; | 
| 662 |         } | 
| 663 |  | 
| 664 |         m_renderControl->polishItems(); | 
| 665 |  | 
| 666 |         m_renderControl->beginFrame(); | 
| 667 |         m_renderControl->sync(); | 
| 668 |         m_renderControl->render(); | 
| 669 |         m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here | 
| 670 |     \endcode | 
| 671 |  | 
| 672 |     \note This function does not need to be, and must not be, called when using | 
| 673 |     the \c software adaptation of Qt Quick. | 
| 674 |  | 
| 675 |     \note Internally beginFrame() and endFrame() invoke | 
| 676 |     \l{QRhi::}{beginOffscreenFrame()} and \l{QRhi::}{endOffscreenFrame()}, | 
| 677 |     respectively. This implies that there must not be a frame (neither | 
| 678 |     offscreen, nor swapchain-based) being recorded on the QRhi when | 
| 679 |     this function is called. | 
| 680 |  | 
| 681 |     \since 6.0 | 
| 682 |  | 
| 683 |     \sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget | 
| 684 |  */ | 
| 685 | void QQuickRenderControl::beginFrame() | 
| 686 | { | 
| 687 |     Q_D(QQuickRenderControl); | 
| 688 |     if (!d->rhi) { | 
| 689 |         qWarning(msg: "QQuickRenderControl: No QRhi in beginFrame()" ); | 
| 690 |         return; | 
| 691 |     } | 
| 692 |     if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) { | 
| 693 |         qWarning(msg: "QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again" ); | 
| 694 |         return; | 
| 695 |     } | 
| 696 |     if (d->rhi->isRecordingFrame()) { | 
| 697 |         qWarning(msg: "QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame" ); | 
| 698 |         return; | 
| 699 |     } | 
| 700 |  | 
| 701 |     emit d->window->beforeFrameBegin(); | 
| 702 |  | 
| 703 |     QRhi::FrameOpResult result = d->rhi->beginOffscreenFrame(cb: &d->cb); | 
| 704 |  | 
| 705 |     switch (result) { | 
| 706 |     case QRhi::FrameOpSuccess: | 
| 707 |     case QRhi::FrameOpSwapChainOutOfDate: | 
| 708 |         d->frameStatus = QQuickRenderControlPrivate::RecordingFrame; | 
| 709 |         break; | 
| 710 |     case QRhi::FrameOpError: | 
| 711 |         d->frameStatus = QQuickRenderControlPrivate::ErrorInBeginFrame; | 
| 712 |         break; | 
| 713 |     case QRhi::FrameOpDeviceLost: | 
| 714 |         d->frameStatus = QQuickRenderControlPrivate::DeviceLostInBeginFrame; | 
| 715 |         break; | 
| 716 |     default: | 
| 717 |         d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame; | 
| 718 |         break; | 
| 719 |     } | 
| 720 | } | 
| 721 |  | 
| 722 | /*! | 
| 723 |     Specifies the end of a graphics frame. Calls to sync() or render() must be | 
| 724 |     enclosed by calls to beginFrame() and endFrame(). | 
| 725 |  | 
| 726 |     When this function is called, any graphics commands enqueued by the | 
| 727 |     scenegraph are submitted to the context or command queue, whichever is | 
| 728 |     applicable. | 
| 729 |  | 
| 730 |     \note This function does not need to be, and must not be, called when using | 
| 731 |     the \c software adaptation of Qt Quick. | 
| 732 |  | 
| 733 |     \since 6.0 | 
| 734 |  | 
| 735 |     \sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget | 
| 736 |  */ | 
| 737 | void QQuickRenderControl::endFrame() | 
| 738 | { | 
| 739 |     Q_D(QQuickRenderControl); | 
| 740 |     if (!d->rhi) { | 
| 741 |         qWarning(msg: "QQuickRenderControl: No QRhi in endFrame()" ); | 
| 742 |         return; | 
| 743 |     } | 
| 744 |     if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) { | 
| 745 |         qWarning(msg: "QQuickRenderControl: endFrame() must only be called after a successful beginFrame()" ); | 
| 746 |         return; | 
| 747 |     } | 
| 748 |     if (!d->rhi->isRecordingFrame()) { | 
| 749 |         qWarning(msg: "QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame" ); | 
| 750 |         return; | 
| 751 |     } | 
| 752 |  | 
| 753 |     d->rhi->endOffscreenFrame(); | 
| 754 |     // do not null out d->cb; this allows calling lastCompletedGpuTime() for example | 
| 755 |  | 
| 756 |     d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame; | 
| 757 |  | 
| 758 |     emit d->window->afterFrameEnd(); | 
| 759 | } | 
| 760 |  | 
| 761 | bool QQuickRenderControlPrivate::initRhi() | 
| 762 | { | 
| 763 |     // initialize() - invalidate() - initialize() uses the QRhi the first | 
| 764 |     // initialize() created, so if already exists, we are done. Does not apply | 
| 765 |     // when wrapping an externally created QRhi, because we may be associated | 
| 766 |     // with a new one now. | 
| 767 |     if (rhi && ownRhi) | 
| 768 |         return true; | 
| 769 |  | 
| 770 |     QSGRhiSupport *rhiSupport = QSGRhiSupport::instance(); | 
| 771 |  | 
| 772 |     // sanity check for Vulkan | 
| 773 | #if QT_CONFIG(vulkan) | 
| 774 |     if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) { | 
| 775 |         qWarning(msg: "QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize" ); | 
| 776 |         return false; | 
| 777 |     } | 
| 778 | #endif | 
| 779 |  | 
| 780 |     // for OpenGL | 
| 781 |     if (!offscreenSurface) | 
| 782 |         offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window); | 
| 783 |  | 
| 784 |     QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(window, offscreenSurface); | 
| 785 |     if (!result.rhi) { | 
| 786 |         qWarning(msg: "QQuickRenderControl: Failed to initialize QRhi" ); | 
| 787 |         return false; | 
| 788 |     } | 
| 789 |  | 
| 790 |     rhi = result.rhi; | 
| 791 |     ownRhi = result.own; | 
| 792 |  | 
| 793 |     return true; | 
| 794 | } | 
| 795 |  | 
| 796 | void QQuickRenderControlPrivate::resetRhi(const QQuickGraphicsConfiguration &config) | 
| 797 | { | 
| 798 |     if (ownRhi) | 
| 799 |         QSGRhiSupport::instance()->destroyRhi(rhi, config); | 
| 800 |  | 
| 801 |     rhi = nullptr; | 
| 802 |  | 
| 803 |     delete offscreenSurface; | 
| 804 |     offscreenSurface = nullptr; | 
| 805 | } | 
| 806 |  | 
| 807 | QT_END_NAMESPACE | 
| 808 |  | 
| 809 | #include "moc_qquickrendercontrol.cpp" | 
| 810 |  |