diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgmaterial.cpp')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgmaterial.cpp | 357 |
1 files changed, 251 insertions, 106 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index c8ae26311b..c507f2a87f 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qsgmaterial.h" #include "qsgrenderer_p.h" @@ -57,10 +21,6 @@ void qsg_set_material_failure() } #endif -#ifndef QT_NO_DEBUG -static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); -#endif - /*! \group qtquick-scenegraph-materials \title Qt Quick Scene Graph Material Classes @@ -75,7 +35,7 @@ static int qt_material_count = 0; static void qt_print_material_count() { - qDebug("Number of leaked materials: %i", qt_material_count); + qCDebug(lcQsgLeak, "Number of leaked materials: %i", qt_material_count); qt_material_count = -1; } #endif @@ -98,54 +58,43 @@ static void qt_print_material_count() \inmodule QtQuick \ingroup qtquick-scenegraph-materials - The QSGMaterial, QSGMaterialShader and QSGMaterialRhiShader subclasses - form a tight relationship. For one scene graph (including nested graphs), - there is one unique QSGMaterialShader or QSGMaterialRhiShader instance - which encapsulates the shaders the scene graph uses to render that - material, such as a shader to flat coloring of geometry. Each - QSGGeometryNode can have a unique QSGMaterial containing the how the shader - should be configured when drawing that node, such as the actual color to - used to render the geometry. + QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For + one scene graph (including nested graphs), there is one unique + QSGMaterialShader instance which encapsulates the shaders the scene graph + uses to render that material, such as a shader to flat coloring of + geometry. Each QSGGeometryNode can have a unique QSGMaterial containing the + how the shader should be configured when drawing that node, such as the + actual color to used to render the geometry. QSGMaterial has two virtual functions that both need to be implemented. The function type() should return a unique instance for all instances of a specific subclass. The createShader() function should return a new instance - of QSGMaterialShader or QSGMaterialRhiShader, specific to that subclass of - QSGMaterial. + of QSGMaterialShader, specific to that subclass of QSGMaterial. A minimal QSGMaterial implementation could look like this: \code class Material : public QSGMaterial { public: - QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - QSGMaterialShader *createShader() const { return new Shader; } + QSGMaterialType *type() const override { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override { return new Shader; } }; \endcode - This is suitable only for the OpenGL-based, traditional renderer of the - scene graph. When using the new, graphics API abstracted renderer, - materials must create QSGMaterialRhiShader instances instead, or in - addition: - \code - class Material : public QSGMaterial - { - public: - Material() { setFlag(SupportsRhiShader, true); } - QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - QSGMaterialShader *createShader() { - if (flags().testFlag(RhiShaderWanted)) { - return new RhiShader; - } else { - // this is optional, relevant for materials that intend to be usable with the legacy OpenGL renderer as well - return new Shader; - } - } - }; - \endcode + See the \l{Scene Graph - Custom Material}{Custom Material example} for an introduction + on implementing a QQuickItem subclass backed by a QSGGeometryNode and a custom + material. + + \note createShader() is called only once per QSGMaterialType, to reduce + redundant work with shader preparation. If a QSGMaterial is backed by + multiple sets of vertex and fragment shader combinations, the implementation + of type() must return a different, unique QSGMaterialType pointer for each + combination of shaders. \note All classes with QSG prefix should be used solely on the scene graph's rendering thread. See \l {Scene Graph and Rendering} for more information. + + \sa QSGMaterialShader, {Scene Graph - Custom Material}, {Scene Graph - Two Texture Providers}, {Scene Graph - Graph} */ /*! @@ -153,11 +102,10 @@ static void qt_print_material_count() */ QSGMaterial::QSGMaterial() - : m_flags(nullptr) { Q_UNUSED(m_reserved); #ifndef QT_NO_DEBUG - if (qsg_leak_check) { + if (lcQsgLeak().isDebugEnabled()) { ++qt_material_count; static bool atexit_registered = false; if (!atexit_registered) { @@ -176,10 +124,10 @@ QSGMaterial::QSGMaterial() QSGMaterial::~QSGMaterial() { #ifndef QT_NO_DEBUG - if (qsg_leak_check) { + if (lcQsgLeak().isDebugEnabled()) { --qt_material_count; if (qt_material_count < 0) - qDebug("Material destroyed after qt_print_material_count() was called."); + qCDebug(lcQsgLeak, "Material destroyed after qt_print_material_count() was called."); } #endif } @@ -189,7 +137,7 @@ QSGMaterial::~QSGMaterial() /*! \enum QSGMaterial::Flag - \value Blending Set this flag to true if the material requires GL_BLEND to be + \value Blending Set this flag to true if the material requires blending to be enabled during rendering. \value RequiresDeterminant Set this flag to true if the material relies on @@ -201,20 +149,22 @@ QSGMaterial::~QSGMaterial() \value RequiresFullMatrix Set this flag to true if the material relies on the full matrix of the geometry nodes for rendering. - \value CustomCompileStep Starting with Qt 5.2, the scene graph will not always call - QSGMaterialShader::compile() when its shader program is compiled and linked. - Set this flag to enforce that the function is called. - - \value SupportsRhiShader Starting with Qt 5.14, the scene graph supports - QSGMaterialRhiShader as an alternative to the OpenGL-specific - QSGMaterialShader. Set this flag to indicate createShader() is capable of - returning QSGMaterialRhiShader instances when the RhiShaderWanted flag is - set. - - \value RhiShaderWanted This flag is set by the scene graph, not by the - QSGMaterial. When set, and that can only happen when SupportsRhiShader was - set by the material, it indicates that createShader() must return a - QSGMaterialRhiShader instance instead of QSGMaterialShader. + \value NoBatching Set this flag to true if the material uses shaders that are + incompatible with the \l{Qt Quick Scene Graph Default Renderer}{scene graph's batching + mechanism}. This is relevant in certain advanced usages, such as, directly + manipulating \c{gl_Position.z} in the vertex shader. Such solutions are often tied to + a specific scene structure, and are likely not safe to use with arbitrary contents in + a scene. Thus this flag should only be set after appropriate investigation, and will + never be needed for the vast majority of materials. Setting this flag can lead to + reduced performance due to having to issue more draw calls. This flag was introduced + in Qt 6.3. + + \value CustomCompileStep In Qt 6 this flag is identical to NoBatching. Prefer using + NoBatching instead. + + \omitvalue MultiView2 + \omitvalue MultiView3 + \omitvalue MultiView4 */ /*! @@ -256,34 +206,229 @@ void QSGMaterial::setFlag(Flags flags, bool on) int QSGMaterial::compare(const QSGMaterial *other) const { Q_ASSERT(other && type() == other->type()); - return qint64(this) - qint64(other); + const qintptr diff = qintptr(this) - qintptr(other); + return diff < 0 ? -1 : (diff > 0 ? 1 : 0); } /*! - \fn QSGMaterialType QSGMaterial::type() const + \fn QSGMaterialType *QSGMaterial::type() const + + This function is called by the scene graph to query an identifier that is + unique to the QSGMaterialShader instantiated by createShader(). - This function is called by the scene graph to return a unique instance - per subclass. + For many materials, the typical approach will be to return a pointer to a + static, and so globally available, QSGMaterialType instance. The + QSGMaterialType is an opaque object. Its purpose is only to serve as a + type-safe, simple way to generate unique material identifiers. + \code + QSGMaterialType *type() const override + { + static QSGMaterialType type; + return &type; + } + \endcode */ /*! - \fn QSGMaterialShader *QSGMaterial::createShader() const + \fn QSGMaterialShader *QSGMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const This function returns a new instance of a the QSGMaterialShader - implementatation used to render geometry for a specific implementation + implementation used to render geometry for a specific implementation of QSGMaterial. - The function will be called only once for each material type that - exists in the scene graph and will be cached internally. + The function will be called only once for each combination of material type and \a renderMode + and will be cached internally. - When the QSGMaterial reports SupportsRhiShader in flags(), the scene graph - may request a QSGMaterialRhiShader instead of QSGMaterialShader. This is - indicated by having the RhiShaderWanted flag set. In this case the return - value must be a QSGRhiMaterialShader subclass. + For most materials, the \a renderMode can be ignored. A few materials may need + custom handling for specific render modes. For instance if the material implements + antialiasing in a way that needs to account for perspective transformations when + RenderMode3D is in use. */ +/*! + \return The number of views in case of the material is used in multiview + rendering. + + \note The return value is valid only when called from createShader(), and + afterwards. The value is not necessarily up-to-date before createShader() + is invokved by the scene graph. + + Normally the return value is \c 1. A view count greater than 2 implies a + \e{multiview render pass}. Materials that support multiview are expected to + query viewCount() in createShader(), or in their QSGMaterialShader + constructor, and ensure the appropriate shaders are picked. The vertex + shader is then expected to use + \c{gl_ViewIndex} to index the modelview-projection matrix array as there + are multiple matrices in multiview mode. (one for each view) + + As an example, take the following simple vertex shader: + + \badcode + #version 440 + + layout(location = 0) in vec4 vertexCoord; + layout(location = 1) in vec4 vertexColor; + + layout(location = 0) out vec4 color; + + layout(std140, binding = 0) uniform buf { + mat4 matrix[2]; + float opacity; + }; + + void main() + { + gl_Position = matrix[gl_ViewIndex] * vertexCoord; + color = vertexColor * opacity; + } + \endcode + + This shader is prepared to handle 2 views, and 2 views only. It is not + compatible with other view counts. When conditioning the shader, the \c qsb + tool has to be invoked with \c{--view-count 2} or, if using the CMake + integration, + \c{VIEW_COUNT 2} must be specified in the \c{qt_add_shaders()} command. + + \note A line with \c{#extension GL_EXT_multiview : require} is injected + automatically by \c qsb whenever a view count of 2 or greater is set. + + Developers are encouraged to use the automatically injected preprocessor + variable \c{QSHADER_VIEW_COUNT} to simplify the handling of the different + number of views. For example, if there is a need to support both + non-multiview and multiview with a view count of 2 in the same source file, + the following could be done: + + \badcode + #version 440 + + layout(location = 0) in vec4 vertexCoord; + layout(location = 1) in vec4 vertexColor; + + layout(location = 0) out vec4 color; + + layout(std140, binding = 0) uniform buf { + #if QSHADER_VIEW_COUNT >= 2 + mat4 matrix[QSHADER_VIEW_COUNT]; + #else + mat4 matrix; + #endif + float opacity; + }; + + void main() + { + #if QSHADER_VIEW_COUNT >= 2 + gl_Position = matrix[gl_ViewIndex] * vertexCoord; + #else + gl_Position = matrix * vertexCoord; + #endif + color = vertexColor * opacity; + } + \endcode + + The same source file can now be run through \c qsb or \c{qt_add_shaders()} + twice, once without specify the view count, and once with the view count + set to 2. The material can then pick the appropriate .qsb file based on + viewCount() at run time. + + With CMake, this could looks similar to the following. With this example + the corresponding QSGMaterialShader is expected to choose between + \c{:/shaders/example.vert.qsb} and \c{:/shaders/multiview/example.vert.qsb} + based on the value of viewCount(). (same goes for the fragment shader) + + \badcode + qt_add_shaders(application "application_shaders" + PREFIX + / + FILES + shaders/example.vert + shaders/example.frag + ) + + qt_add_shaders(application "application_multiview_shaders" + GLSL + 330,300es + HLSL + 61 + MSL + 12 + VIEW_COUNT + 2 + PREFIX + / + FILES + shaders/example.vert + shaders/example.frag + OUTPUTS + shaders/multiview/example.vert + shaders/multiview/example.frag + ) + \endcode + + \note The fragment shader should be treated the same way the vertex shader + is, even though the fragment shader code cannot have any dependency on the + view count (\c{gl_ViewIndex}), for maximum portability. There are two + reasons for including fragment shaders too in the multiview set. One is that + mixing different shader versions within the same graphics pipeline can be + problematic, depending on the underlying graphics API: with D3D12 for + example, mixing HLSL shaders for shader model 5.0 and 6.1 would generate an + error. The other is that having \c QSHADER_VIEW_COUNT defined in fragment + shaders can be very useful, for example when sharing a uniform buffer layout + between the vertex and fragment stages. + + \note For OpenGL the minimum GLSL version for vertex shaders relying on + \c{gl_ViewIndex} is \c 330. Lower versions may be accepted at build time, + but may lead to an error at run time, depending on the OpenGL implementation. + + As a convenience, there is also a \c MULTIVIEW option for qt_add_shaders(). + This first runs the \c qsb tool normally, then overrides \c VIEW_COUNT to + \c 2, sets \c GLSL, \c HLSL, \c MSL to some suitable defaults, and runs \c + qsb again, this time outputting .qsb files with a suffix added. The material + implementation can then use the \l QSGMaterialShader::setShaderFileName() + overload taking a \c viewCount argument, that automatically picks the + correct .qsb file. + + The following is therefore mostly equivalent to the example call shown + above, except that no manually managed output files need to be specified. + Note that there can be cases when the automatically chosen shading language + versions are not sufficient, in which case applications should continue + specify everything explicitly. + + \badcode + qt_add_shaders(application "application_multiview_shaders" + MULTIVIEW + PREFIX + / + FILES + shaders/example.vert + shaders/example.frag + ) + \endcode + + See \l QRhi::MultiView, \l QRhiColorAttachment::setMultiViewCount(), and + \l QRhiGraphicsPipeline::setMultiViewCount() for further, lower-level details + on multiview support in Qt. The Qt Quick scene graph renderer is prepared to + recognize multiview render targets, when specified via \l + QQuickRenderTarget::fromRhiRenderTarget() or the 3D API specific functions, + such as \l{QQuickRenderTarget::}{fromVulkanImage()} with an \c arraySize + argument greater than 1. The renderer will then propagate the view count to + graphics pipelines and the materials. + + \since 6.8 + */ +int QSGMaterial::viewCount() const +{ + if (m_flags.testFlag(MultiView4)) + return 4; + if (m_flags.testFlag(MultiView3)) + return 3; + if (m_flags.testFlag(MultiView2)) + return 2; + return 1; +} + QT_END_NAMESPACE |