Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
++
++ /*!
++ \title QQuickRenderControl RHI Example
++ \example rendercontrol/rendercontrol_rhi
++ \brief Shows how to render a Qt Quick scene into a QRhiTexture.
++ \examplecategory {Graphics}
++ \image rendercontrol-rhi-example.jpg {Grid of rendered frames with
++ CPU and GPU timing metrics for each frame}
++
++ This example demonstrates how to set up a Qt Quick scene that has its
++ rendering redirected into a \l QRhiTexture. The application is then free to
++ do whatever it wants with the resulting texture from each frame.
++ This example is a QWidget-based application that performs a readback of the
++ image data, and then displays the collected per-frame renders with CPU and
++ GPU-based timing information for each.
++
++ By using Qt's 3D graphics API abstraction, this example is not tied to any
++ particular graphics API. At startup, a dialog is shown with the platforms'
++ potentially supported 3D APIs.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp apiselect
++
++ \note It is not guaranteed that all selections will be functional on a given
++ platform.
++
++ Once a selection is made, a QML file is loaded. However, we will not simply
++ create a \l QQuickView instance and \l{QQuickView::show()}{show()} it.
++ Rather, the \l QQuickWindow that manages the Qt Quick scene is never shown
++ on-screen. Instead, the application takes control over when and to where
++ render, via \l QQuickRenderControl.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-1
++
++ Once the object tree is instantiated, the root item (a \l Rectangle) is
++ queried, its size is ensured to be valid and then propagated.
++
++ \note Scenes that use the \l Window element within the object tree are not
++ supported.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-instantiate
++
++ At this point there are no rendering resources initialized, i.e., nothing has
++ been done with the native 3D graphics API yet. A \l QRhi is instantiated
++ only in the next step, and that is what triggers setting up the Vulkan,
++ Metal, Direct 3D, etc. rendering system under the hood.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp load-graphicsinit
++
++ \note This application uses a model where Qt creates an instance of QRhi.
++ This is not the only possible approach: if the application maintains its own
++ QRhi (and so OpenGL context, Vulkan device, etc.), then Qt Quick can be
++ requested to adopt and use that existing QRhi. That is done via passing a \l
++ QQuickGraphicsDevice created by \l{QQuickGraphicsDevice::fromRhi()} to
++ \l QQuickWindow, similarly to how \l QQuickGraphicsConfiguration is set in
++ the snippet above. Consider for example the case of wanting to use the Qt
++ Quick rendered textures in a QRhiWidget: in that case the QRhiWidget's QRhi
++ will need to passed on to Qt Quick, instead of letting Qt Quick create its
++ own.
++
++ Once \l QQuickRenderControl::initialize() succeeds, the renderer is live and
++ ready to go. For that, we need a color buffer to render into.
++
++ \l QQuickRenderTarget is a lightweight implicitly-shared class that carries
++ (but those not own) various sets of native or QRhi objects that describe
++ textures, render targets, or similar. Calling
++ \l{QQuickWindow::setRenderTarget()}{setRenderTarget()} on the \l
++ QQuickWindow (remember that we have a QQuickWindow that is not visible
++ on-screen) is what triggers redirecting the Qt Quick scene graph's rendering
++ into the texture provided by the application. When working with \l QRhi (and
++ not with native 3D API objects such as OpenGL texture IDs or VkImage
++ objects), the application should set up a \l QRhiTextureRenderTarget and
++ then pass it to Qt Quick via \l QQuickRenderTarget::fromRhiRenderTarget().
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp texture-setup
++
++ \note Always provide a depth-stencil buffer for Qt Quick since both of these
++ buffers and the depth and stencil test may get utilized by the Qt Quick
++ scenegraph when rendering.
++
++ The main render loop is the following. This also shows how to perform
++ GPU->CPU readbacks of images. Once a QImage is available, the
++ QWidget-based user interface updates accordingly. We will omit diving
++ into the details for that here.
++
++ The example also demonstrates a simple way of measuring the cost of
++ rendering a frame on the CPU and the GPU. Offscreen-rendered frames are well
++ suited for this due to certain internal QRhi behavior, which implies that
++ operations that otherwise are asynchronous (in the sense that they complete
++ only when rendering a subsequent frame), are guaranteed to be ready once \l
++ QRhi::endOffscreenFrame() (i.e., QQuickRenderControl::endFrame()) returns.
++ We use this knowledge when reading back the texture, and it applies also to
++ GPU timestamps as well. That is why the application can display the GPU time
++ for each frame, while guaranteeing that the time actually refers to that
++ particular frame (not an earlier one). See
++ \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} for
++ details around GPU timings. The CPU side timings are taken using
++ \l QElapsedTimer.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp render-core
++
++ One important piece is the stepping of Qt Quick animations. As we do not
++ have an on-screen window that can drive the animation system either via
++ measuring elapsed time, an ordinary timer, or presentation rate-based
++ throttling, redirecting the Qt Quick rendering often implies that the
++ driving of animations needs to be taken over by the application. Otherwise,
++ animations function based on a plain system timer, but the actual elapsed
++ time will often have nothing to do with what the offscreen-rendered scene is
++ expected to perceive. Consider rendering 5 frames in a row, in a tight loop.
++ How the animations in those 5 frames move depends on the speed with which
++ the CPU executes the loop iterations. That is almost never ideal. To ensure
++ consistent animations, install a custom QAnimationDriver. While this is
++ an undocumented (but public) API meant for advanced users, the example here
++ provides a simple example of using it.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-driver
++
++ The application has a \l QSlider that can be used to change the animation
++ step value from the default 16 milliseconds to something else. Note the call
++ to the setStep() function of our QAnimationDriver subclass.
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-slider
++
++ \note Installing the custom animation driver is made optional via the
++ \c animCheckBox check box. This allows comparing the effect of having and
++ not having a custom animation driver installed. In addition, on some
++ platforms (and perhaps depending on the theme), having the custom driver
++ enabled may lead to lags in widget drawing. This is as expected, because if
++ some widget animation (e.g. highlight of a QPushButton or QCheckBox) is
++ managed via \l QPropertyAnimation and similar, then those animation are
++ driven by the same QAnimationDriver, and that does not advance until a new
++ frame is requested by clicking on the buttons.
++
++ Advancing the animations is done before each frame (i.e., before the
++ QQuickRenderControl::beginFrame() call) by simply calling advance():
++
++ \snippet rendercontrol/rendercontrol_rhi/main.cpp anim-step
++
++ \sa QRhi, QQuickRenderControl, QQuickWindow
++ */