// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example scenegraph/rhitextureitem
    \title Scene Graph - RHI Texture Item
    \examplecategory {Graphics}
    \ingroup qtquickexamples

    \brief Shows how to implement a custom QQuickItem that displays a QRhi-rendered texture.

    \image rhitextureitem-example.jpg {RGB triangle rendered using QRhi
           displayed as a QQuickItem, with settings panel for MSAA and texture options}

    This example shows how to implement an item that performs cross-platform,
    portable 3D rendering into a texture using the QRhi APIs and then displays
    that image.

    \note This example demonstrates advanced, low-level functionality performing
    portable, cross-platform 3D rendering, while relying on APIs with limited
    compatibility guarantee from the Qt Gui module.  To be able to use the QRhi
    APIs, the application links to \c{Qt::GuiPrivate} and includes
    \c{<rhi/qrhi.h>}.

    \section2 Comparison with other approaches

    The \l{Scene Graph - RHI Under QML}{RHI Under QML} example shows how to
    implement portable, cross-platform 3D rendering with the \l QRhi APIs in a
    manner where the custom rendering is issued before the Qt Quick scene
    graph's own rendering, effectively providing an "underlay". That approach
    is efficient since now additional render targets and render passes are
    needed, the custom rendering is injected in the main render pass before the
    scene graph's own draw calls.

    In contrast, this example involves a separate render target, a QRhiTexture,
    the \l{QRhiTexture::pixelSize()}{dimensions} of which match the
    QQuickItem's size in the scene, and a whole render pass that is used to
    clear and then draw into that texture. The texture is then sampled in the
    main render pass and is used to texture a quad, effectively displaying a 2D
    image.

    Compared to the underlay/overlay approach, this allows displaying,
    blending, and transforming the flattened 2D image of the 3D rendering
    anywhere in the Qt Quick scene since here we have a true QQuickItem. This
    comes at the expense of being more expensive in terms of resources and
    performance since it involves rendering to a texture first.

    \section2 Overview

    The example is implemented using \l QQuickRhiItem and
    \l QQuickRhiItemRenderer. QQuickRhiItem is a convenience class that can be
    subclassed to easily and quickly get a fully featured, custom QQuickItem that
    displays the contents of a QRhiTexture by using QSGSimpleTextureNode under the
    hood. The contents of the texture is generated by the application-provided
    logic implemented in its QQuickRhiItemRenderer subclass.

    \c ExampleRhiItem is a QQuickRhiItem subclass that offers a few properties,
    such as \c angle and \c backgroundAlpha. These are going to be read,
    written, and animated from QML. In order to support Qt Quick's threaded
    rendering model, the QQQuickRhiItemRenderer has a virtual
    \l{QQuickRhiItemRenderer::synchronize()}{synchronize()} function that can
    be reimplemented to safely perform copying of data between the
    QQuickRhiItem (belonging to the main/GUI thread) and the
    QQuickRhiItemRenderer (belonging to the render thread, if there is one).

    \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 0

    \l{QQuickRhiItemRenderer::initialize()}{initialize()} is called at least
    once before the first call to render(), but may in practice be invoked
    multiple times: if the QQuickItem geometry changes (due to some layout
    change, resizing the window, etc.), if QQuickRhiItem setting such as the
    sample count and texture format change, or if the item is reparented so
    that is belong to a new QQuickWindow, these all trigger calling
    initialize() again because they imply that one or more of the resources
    QQuickRhiItem-managed resources change, which is going to have implications
    on the subclass as well. The example code here is prepared to handle these
    special situations (changing QRhi, changing sample count, changing texture
    format). (as it does not hold on to the texture used as the color buffer,
    the case when the texture is recreated due to a different size needs no
    special handling)

    \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 1

    The rest if initialize() is straightforward QRhi-based code.

    The 3D scene uses a perspective projection, which is calculated based on
    the output size, queried from the QRhiRenderTarget for convenience (because
    this works regardless of using multisampling or not, whereas accessing
    \l{QQuickRhiItemRenderer::colorTexture()}{colorTexture()} and
    \l{QQuickRhiItemRenderer::msaaColorBuffer()}{msaaColorBuffer()} would need
    branching logic based on which of the objects happens to be valid)

    Note the usage of \l QRhi::clipSpaceCorrMatrix() to cater for the
    coordinate system differences between 3D graphics APIs.

    \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 2

    The implementation of \l{QQuickRhiItemRenderer::render()}{render()} records
    the drawing of a single triangle. The uniform buffer with the 4x4 matrix is
    updated every time since we expect the rotation angle to change. The clear
    color has the item-provided background alpha baked in. Remember the need to
    premultiply the alpha value in the red, green, and blue components as well.

    \snippet scenegraph/rhitextureitem/rhitextureitem.cpp 3

    \sa QQuickRhiItem, {Scene Graph - RHI Under QML}, {Scene Graph - Custom QSGRenderNode}
 */
