Revision control
++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
++
++ /*!
++ \example rhi/simplerhiwidget
++ \title Simple RHI Widget Example
++ \examplecategory {Graphics}
++ \ingroup examples-widgets
++ \brief Shows how to render a triangle using QRhi, Qt's 3D API and shading language abstraction layer.
++
++ \image simplerhiwidget-example.jpg
++ {Colored triangle on a green background}
++ \caption Screenshot of the Simple RHI Widget example
++
++ This example is, in many ways, the counterpart of the \l{RHI Window
++ Example} in the \l QWidget world. The \l QRhiWidget subclass in this
++ applications renders a single triangle, using a simple graphics pipeline
++ with basic vertex and fragment shaders. Unlike the plain QWindow-based
++ application, this example does not need to worry about lower level details,
++ such as setting up the window and the QRhi, or dealing with swapchain and
++ window events, as that is taken care of by the QWidget framework here. The
++ instance of the \l QRhiWidget subclass is added to a QVBoxLayout. To keep
++ the example minimal and compact, there are no further widgets or 3D content
++ introduced.
++
++ Once an instance of \c ExampleRhiWidget, a \l QRhiWidget subclass, is added
++ to a top-level widget's child hierarchy, the corresponding window
++ automatically becomes a Direct 3D, Vulkan, Metal, or OpenGL-rendered
++ window. The QPainter-rendered widget content, i.e. everything that is not a
++ QRhiWidget, QOpenGLWidget, or QQuickWidget, is then uploaded to a texture,
++ whereas the mentioned special widgets each render to a texture. The
++ resulting set of \l{QRhiTexture}{textures} is composited together by the
++ top-level widget's backingstore.
++
++ \section1 Structure and main()
++
++ The \c{main()} function is quite simple. The top-level widget defaults to a
++ size of 720p (this size is in logical units, the actual pixel size may be
++ different, depending on the \l{QWidget::devicePixelRatio()}{scale factor}.
++ The window is resizable. QRhiWidget makes it simple to implement subclasses
++ that correctly deal with the resizing of the widget due to window size or
++ layout changes.
++
++ \snippet rhi/simplerhiwidget/main.cpp 0
++
++ The QRhiWidget subclass reimplements the two virtuals:
++ \l{QRhiWidget::initialize()}{initialize()} and
++ \l{QRhiWidget::render()}{render()}.
++ initialize() is called at least once before render(),
++ but is also invoked upon a number of important changes, such as when the
++ widget's backing texture is recreated due to a changing widget size, when
++ render target parameters change, or when the widget changes to a new QRhi
++ due to moving to a new top-level window.
++
++ \note Unlike QOpenGLWidget's legacy \c initializeGL - \c resizeGL - \c
++ paintGL model, there are only two virtuals in QRhiWidget. This is because
++ there are more special events that possible need taking care of than just
++ resizing, e.g. when reparenting to a different top-level window. (robust
++ QOpenGLWidget implementations had to deal with this by performing
++ additional bookkeeping, e.g. by tracking the associated QOpenGLContext
++ lifetime, meaning the three virtuals were not actually sufficient) A
++ simpler pair of \c initialize - \c render, where \c initialize is
++ re-invoked upon important changes is better suited for this.
++
++ The \l QRhi instance is not owned by the widget. It is going to be queried
++ in \c initialize() \l{QRhiWidget::rhi()}{from the base class}. Storing it
++ as a member allows recognizing changes when \c initialize() is invoked
++ again. Graphics resources, such as the vertex and uniform buffers, or the
++ graphics pipeline are however under the control of \c ExampleRhiWidget.
++
++ \snippet rhi/simplerhiwidget/examplewidget.h 0
++
++ For the \c{#include <rhi/qrhi.h>} statement to work, the application must
++ link to \c GuiPrivate (or \c{gui-private} with qmake). See \l QRhi for more
++ details about the compatibility promise of the QRhi family of APIs.
++
++ \c CMakeLists.txt
++
++ \badcode
++ target_link_libraries(simplerhiwidget PRIVATE
++ Qt6::Core
++ Qt6::Gui
++ Qt6::GuiPrivate
++ Qt6::Widgets
++ )
++ \endcode
++
++ \section1 Rendering Setup
++
++ In \c examplewidget.cpp the widget implementation uses a helper function to
++ load up a \l QShader object from a \c{.qsb} file. This application ships
++ pre-conditioned \c{.qsb} files embedded in to the executable via the Qt
++ Resource System. Due to module dependencies (and due to still supporting
++ qmake), this example does not use the convenient CMake function
++ \c{qt_add_shaders()}, but rather comes with the \c{.qsb} files as part of
++ the source tree. Real world applications are encouraged to avoid this and
++ rather use the Qt Shader Tools module's CMake integration features (\c
++ qt_add_shaders). Regardless of the approach, in the C++ code the loading
++ of the bundled/generated \c{.qsb} files is the same.
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp get-shader
++
++ Let's look at the initialize() implementation. First, the \l QRhi object is
++ queried and stored for later use, and also to allow comparison in future
++ invocations of the function. When there is a mismatch (e.g. when the widget
++ is moved between windows), recreation of graphics resources need to be
++ recreated is triggered by destroying and nulling out a suitable object, in
++ this case the \c m_pipeline. The example does not actively demonstrate
++ reparenting between windows, but it is prepared to handle it. It is also
++ prepared to handle a changing widget size that can happen when resizing the
++ window. That needs no special handling since \c{initialize()} is invoked
++ every time that happens, and so querying
++ \c{renderTarget()->pixelSize()} or \c{colorTexture()->pixelSize()}
++ always gives the latest, up-to-date size in pixels. What this example is
++ not prepared for is changing
++ \l{QRhiWidget::colorBufferFormat}{color buffer formats} and
++ \l{QRhiWidget::sampleCount}{multisample settings}
++ since it only ever uses the defaults (RGBA8 and no multisample antialiasing).
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp init-1
++
++ When the graphics resources need to be (re)created, \c{initialize()} does
++ this using quite typical QRhi-based code. A single vertex buffer with the
++ interleaved position - color vertex data is sufficient, whereas the
++ modelview-projection matrix is exposed via a uniform buffer of 64 bytes (16
++ floats). The uniform buffer is the only shader visible resource, and it is
++ only used in the vertex shader. The graphics pipeline relies on a lot of
++ defaults (for example, depth test off, blending disabled, color write
++ enabled, face culling disabled, the default topology of triangles, etc.)
++ The vertex data layout is \c x, \c y, \c r, \c g, \c b, hence the stride is
++ 5 floats, whereas the second vertex input attribute (the color) has an
++ offset of 2 floats (skipping \c x and \c y). Each graphics pipeline has to
++ be associated with a \l QRhiRenderPassDescriptor. This can be retrieved
++ from the \l QRhiRenderTarget managed by the base class.
++
++ \note This example relies on the QRhiWidget's default of
++ \l{QRhiWidget::autoRenderTarget}{autoRenderTarget} set to \c true.
++ That is why it does not need to manage the render target, but can just
++ query the existing one by calling
++ \l{QRhiWidget::renderTarget()}{renderTarget()}.
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp init-pipeline
++
++ Finally, the projection matrix is calculated. This depends on the widget
++ size and is thus done unconditionally in every invocation of the functions.
++
++ \note Any size and viewport calculations should only ever rely on the pixel
++ size queried from the resource serving as the color buffer since that is
++ the actual render target. Avoid manually calculating sizes, viewports,
++ scissors, etc. based on the QWidget-reported size or device pixel ratio.
++
++ \note The projection matrix includes the
++ \l{QRhi::clipSpaceCorrMatrix()}{correction matrix} from QRhi in order to
++ cater for 3D API differences in normalized device coordinates.
++ (for example, Y down vs. Y up)
++
++ A translation of \c{-4} is applied just to make sure the triangle with \c z
++ values of 0 will be visible.
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp init-matrix
++
++ \section1 Rendering
++
++ The widget records a single render pass, which contains a single draw call.
++
++ The view-projection matrix calculated in the initialize step gets combined
++ with the model matrix, which in this case happens to be a simple rotation.
++ The resulting matrix is then written to the uniform buffer. Note how
++ \c resourceUpdates is passed to
++ \l{QRhiCommandBuffer::beginPass()}{beginPass()}, which is a shortcut to not
++ having to invoke \l{QRhiCommandBuffer::resourceUpdate()}{resourceUpdate()}
++ manually.
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp render-1
++
++ In the render pass, a single draw call with 3 vertices is recorded. The
++ graphics pipeline created in the initialize step is bound on the command
++ buffer, and the viewport is set to cover the entire widget. To make the
++ uniform buffer visible to the (vertex) shader,
++ \l{QRhiCommandBuffer::setShaderResources()}{setShaderResources()} is called
++ with no argument, which means using the \c m_srb since that was associated
++ with the pipeline at pipeline creation time. In more complex renderers it
++ is not unusual to pass in a different \l QRhiShaderResourceBindings object,
++ as long as that is
++ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible}
++ with the one given at pipeline creation time.
++ There is no index buffer, and there is a single vertex buffer binding (the
++ single element in \c vbufBinding refers to the single entry in the binding
++ list of the \l QRhiVertexInputLayout that was specified when creating
++ pipeline).
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp render-pass
++
++ Once the render pass is recorded, \l{QWidget::update()}{update()} is
++ called. This requests a new frame, and is used to ensure the widget
++ continuously updates, and the triangle appears rotating. The rendering
++ thread (the main thread in this case) is throttled by the presentation rate
++ by default. There is no proper animation system in this example, and so the
++ rotation will increase in every frame, meaning the triangle will rotate at
++ different speeds on displays with different refresh rates.
++
++ \snippet rhi/simplerhiwidget/examplewidget.cpp render-2
++
++ \sa QRhi, {Cube RHI Widget Example}, {RHI Window Example}
++ */