aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickshadereffect.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickshadereffect.cpp')
-rw-r--r--src/quick/items/qquickshadereffect.cpp574
1 files changed, 257 insertions, 317 deletions
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index debe00cbcb..7dea334785 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -1,50 +1,10 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include <private/qquickshadereffect_p.h>
+// 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 <private/qquickshadereffect_p_p.h>
#include <private/qsgcontextplugin_p.h>
-#include <private/qquickitem_p.h>
#include <private/qsgrhisupport_p.h>
-
#include <private/qquickwindow_p.h>
-#include <private/qquickitem_p.h>
-#include "qquickshadereffectmesh_p.h"
QT_BEGIN_NAMESPACE
@@ -110,7 +70,10 @@ QT_BEGIN_NAMESPACE
\note It is only the vertex input location that matters in practice. The
names are freely changeable, while the location must always be \c 0 for
- vertex position, \c 1 for texture coordinates.
+ vertex position, \c 1 for texture coordinates. However, be aware that this
+ applies to vertex inputs only, and is not necessarily true for output
+ variables from the vertex shader that are then used as inputs in the
+ fragment shader (typically, the interpolated texture coordinates).
The following uniforms are predefined:
@@ -267,6 +230,18 @@ QT_BEGIN_NAMESPACE
or the opacity, because at run time there is one single uniform buffer that
is exposed to both the vertex and fragment shader.
+ \warning Unlike with vertex inputs, passing data between the vertex and
+ fragment shader may, depending on the underlying graphics API, require the
+ same names to be used, a matching location is not always sufficient. Most
+ prominently, when specifying a fragment shader while relying on the default,
+ built-in vertex shader, the texture coordinates are passed on as \c
+ qt_TexCoord0 at location \c 0, and therefore it is strongly advised that the
+ fragment shader declares the input with the same name
+ (qt_TexCoord0). Failing to do so may lead to issues on some platforms, for
+ example when running with a non-core profile OpenGL context where the
+ underlying GLSL shader source code has no location qualifiers and matching
+ is based on the variable names during to shader linking process.
+
\section1 ShaderEffect and Item Layers
The ShaderEffect type can be combined with \l {Item Layers} {layered items}.
@@ -281,7 +256,8 @@ QT_BEGIN_NAMESPACE
id: layerRoot
layer.enabled: true
layer.effect: ShaderEffect {
- fragmentShader: "effect.frag.qsb"
+ fragmentShader: "effect.frag.qsb"
+ }
}
\endqml
@@ -435,7 +411,11 @@ QT_BEGIN_NAMESPACE
\li The vertex shader outputs and fragment shader inputs are up to the
shader code to define. The fragment shader must have a \c vec4 output at
- location 0 (typically called \c fragColor).
+ location 0 (typically called \c fragColor). For maximum portability, vertex
+ outputs and fragment inputs should use both the same location number and the
+ same name. When specifying only a fragment shader, the texture coordinates
+ are passed in from the built-in vertex shader as \c{vec2 qt_TexCoord0} at
+ location \c 0, as shown in the example snippets above.
\li Uniform variables outside a uniform block are not legal. Rather,
uniform data must be declared in a uniform block with binding point \c 0.
@@ -506,6 +486,15 @@ QT_BEGIN_NAMESPACE
\li Samplers must use binding points starting from 1.
+ \li When Qt Quick is rendering with \c multiview enabled, e.g. because it is
+ part of a 3D scene rendering in a VR/AR environment where the left and right
+ eye content are generated in a single pass, the ShaderEffect's shaders have
+ to be written with this in mind. With a view count of 2 for example, there
+ will be \c 2 matrices (qt_Matrix is an array of mat4 with two elements). The
+ vertex shader is expected to take \c gl_ViewIndex into account. See the \c
+ Multiview section in the \l{QSB Manual} for general information on creating
+ multiview-capable shaders.
+
\endlist
\sa {Item Layers}, {QSB Manual}, {Qt Shader Tools Build System Integration}
@@ -548,118 +537,16 @@ private:
};
} // namespace QtPrivate
-class QQuickShaderEffectImpl : public QObject
-{
- Q_OBJECT
-
-public:
- QQuickShaderEffectImpl(QQuickShaderEffect *item);
- ~QQuickShaderEffectImpl();
-
- QUrl fragmentShader() const { return m_fragShader; }
- void setFragmentShader(const QUrl &fileUrl);
-
- QUrl vertexShader() const { return m_vertShader; }
- void setVertexShader(const QUrl &fileUrl);
-
- bool blending() const { return m_blending; }
- void setBlending(bool enable);
-
- QVariant mesh() const;
- void setMesh(const QVariant &mesh);
-
- QQuickShaderEffect::CullMode cullMode() const { return m_cullMode; }
- void setCullMode(QQuickShaderEffect::CullMode face);
-
- QString log() const;
- QQuickShaderEffect::Status status() const;
-
- bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
- void setSupportsAtlasTextures(bool supports);
-
- QString parseLog();
-
- void handleEvent(QEvent *);
- void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
- QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *);
- void handleComponentComplete();
- void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
- void maybeUpdateShaders();
- bool updateUniformValue(const QByteArray &name, const QVariant &value,
- QSGShaderEffectNode *node);
-
-private slots:
- void propertyChanged(int mappedId);
- void sourceDestroyed(QObject *object);
- void markGeometryDirtyAndUpdate();
- void markGeometryDirtyAndUpdateIfSupportsAtlas();
- void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
- const QUrl &loadUrl, QSGGuiThreadShaderEffectManager::ShaderInfo *result);
-
-private:
- QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
-
- enum Shader {
- Vertex,
- Fragment,
-
- NShader
- };
- bool updateShader(Shader shaderType, const QUrl &fileUrl);
- void updateShaderVars(Shader shaderType);
- void disconnectSignals(Shader shaderType);
- void clearMappers(Shader shaderType);
- bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
- std::optional<int> findMappedShaderVariableId(const QByteArray &name) const;
-
- QQuickShaderEffect *m_item;
- const QMetaObject *m_itemMetaObject = nullptr;
- QSize m_meshResolution;
- QQuickShaderEffectMesh *m_mesh;
- QQuickGridMesh m_defaultMesh;
- QQuickShaderEffect::CullMode m_cullMode;
- bool m_blending;
- bool m_supportsAtlasTextures;
- mutable QSGGuiThreadShaderEffectManager *m_mgr;
- QUrl m_fragShader;
- bool m_fragNeedsUpdate;
- QUrl m_vertShader;
- bool m_vertNeedsUpdate;
-
- QSGShaderEffectNode::ShaderData m_shaders[NShader];
- QSGShaderEffectNode::DirtyShaderFlags m_dirty;
- QSet<int> m_dirtyConstants[NShader];
- QSet<int> m_dirtyTextures[NShader];
- QSGGuiThreadShaderEffectManager::ShaderInfo *m_inProgress[NShader];
-
- QVector<QtPrivate::EffectSlotMapper*> m_mappers[NShader];
-};
-
-
-class QQuickShaderEffectPrivate : public QQuickItemPrivate
-{
- Q_DECLARE_PUBLIC(QQuickShaderEffect)
-
-public:
- void updatePolish() override;
-};
-
QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
- : QQuickItem(*new QQuickShaderEffectPrivate, parent),
- m_impl(nullptr)
+ : QQuickItem(*new QQuickShaderEffectPrivate, parent)
{
setFlag(QQuickItem::ItemHasContents);
-
- m_impl = new QQuickShaderEffectImpl(this);
}
QQuickShaderEffect::~QQuickShaderEffect()
{
- // Delete the implementations now, while they still have have
- // valid references back to us.
- auto *impl = m_impl;
- m_impl = nullptr;
- delete impl;
+ Q_D(QQuickShaderEffect);
+ d->inDestructor = true;
}
/*!
@@ -677,12 +564,14 @@ QQuickShaderEffect::~QQuickShaderEffect()
QUrl QQuickShaderEffect::fragmentShader() const
{
- return m_impl->fragmentShader();
+ Q_D(const QQuickShaderEffect);
+ return d->fragmentShader();
}
void QQuickShaderEffect::setFragmentShader(const QUrl &fileUrl)
{
- m_impl->setFragmentShader(fileUrl);
+ Q_D(QQuickShaderEffect);
+ d->setFragmentShader(fileUrl);
}
/*!
@@ -700,12 +589,14 @@ void QQuickShaderEffect::setFragmentShader(const QUrl &fileUrl)
QUrl QQuickShaderEffect::vertexShader() const
{
- return m_impl->vertexShader();
+ Q_D(const QQuickShaderEffect);
+ return d->vertexShader();
}
void QQuickShaderEffect::setVertexShader(const QUrl &fileUrl)
{
- m_impl->setVertexShader(fileUrl);
+ Q_D(QQuickShaderEffect);
+ d->setVertexShader(fileUrl);
}
/*!
@@ -719,12 +610,14 @@ void QQuickShaderEffect::setVertexShader(const QUrl &fileUrl)
bool QQuickShaderEffect::blending() const
{
- return m_impl->blending();
+ Q_D(const QQuickShaderEffect);
+ return d->blending();
}
void QQuickShaderEffect::setBlending(bool enable)
{
- m_impl->setBlending(enable);
+ Q_D(QQuickShaderEffect);
+ d->setBlending(enable);
}
/*!
@@ -742,12 +635,14 @@ void QQuickShaderEffect::setBlending(bool enable)
QVariant QQuickShaderEffect::mesh() const
{
- return m_impl->mesh();
+ Q_D(const QQuickShaderEffect);
+ return d->mesh();
}
void QQuickShaderEffect::setMesh(const QVariant &mesh)
{
- m_impl->setMesh(mesh);
+ Q_D(QQuickShaderEffect);
+ d->setMesh(mesh);
}
/*!
@@ -755,23 +650,23 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
This property defines which sides of the item should be visible.
- \list
- \li ShaderEffect.NoCulling - Both sides are visible
- \li ShaderEffect.BackFaceCulling - only front side is visible
- \li ShaderEffect.FrontFaceCulling - only back side is visible
- \endlist
+ \value ShaderEffect.NoCulling Both sides are visible
+ \value ShaderEffect.BackFaceCulling only the front side is visible
+ \value ShaderEffect.FrontFaceCulling only the back side is visible
The default is NoCulling.
*/
QQuickShaderEffect::CullMode QQuickShaderEffect::cullMode() const
{
- return m_impl->cullMode();
+ Q_D(const QQuickShaderEffect);
+ return d->cullMode();
}
void QQuickShaderEffect::setCullMode(CullMode face)
{
- return m_impl->setCullMode(face);
+ Q_D(QQuickShaderEffect);
+ return d->setCullMode(face);
}
/*!
@@ -797,12 +692,14 @@ void QQuickShaderEffect::setCullMode(CullMode face)
bool QQuickShaderEffect::supportsAtlasTextures() const
{
- return m_impl->supportsAtlasTextures();
+ Q_D(const QQuickShaderEffect);
+ return d->supportsAtlasTextures();
}
void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
{
- m_impl->setSupportsAtlasTextures(supports);
+ Q_D(QQuickShaderEffect);
+ d->setSupportsAtlasTextures(supports);
}
/*!
@@ -810,11 +707,9 @@ void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
This property tells the current status of the shaders.
- \list
- \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
- \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
- \li ShaderEffect.Error - the shader program failed to compile or link.
- \endlist
+ \value ShaderEffect.Compiled the shader program was successfully compiled and linked.
+ \value ShaderEffect.Uncompiled the shader program has not yet been compiled.
+ \value ShaderEffect.Error the shader program failed to compile or link.
When setting the fragment or vertex shader source code, the status will
become Uncompiled. The first time the ShaderEffect is rendered with new
@@ -850,45 +745,47 @@ void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
QString QQuickShaderEffect::log() const
{
- return m_impl->log();
+ Q_D(const QQuickShaderEffect);
+ return d->log();
}
QQuickShaderEffect::Status QQuickShaderEffect::status() const
{
- return m_impl->status();
+ Q_D(const QQuickShaderEffect);
+ return d->status();
}
bool QQuickShaderEffect::event(QEvent *e)
{
- if (m_impl)
- m_impl->handleEvent(e);
+ Q_D(QQuickShaderEffect);
+ d->handleEvent(e);
return QQuickItem::event(e);
}
void QQuickShaderEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- m_impl->handleGeometryChanged(newGeometry, oldGeometry);
+ Q_D(QQuickShaderEffect);
+ d->handleGeometryChanged(newGeometry, oldGeometry);
QQuickItem::geometryChange(newGeometry, oldGeometry);
}
QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
- return m_impl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
+ Q_D(QQuickShaderEffect);
+ return d->handleUpdatePaintNode(oldNode, updatePaintNodeData);
}
void QQuickShaderEffect::componentComplete()
{
- m_impl->maybeUpdateShaders();
+ Q_D(QQuickShaderEffect);
+ d->maybeUpdateShaders();
QQuickItem::componentComplete();
}
void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
{
- // It's possible for itemChange to be called during destruction when deleting
- // the QQuickShaderEffectImpl. We nullify m_impl before deleting it via another pointer
- // to it, so we must check that it's not null before trying to use it here.
- if (m_impl)
- m_impl->handleItemChange(change, value);
+ Q_D(QQuickShaderEffect);
+ d->handleItemChange(change, value);
QQuickItem::itemChange(change, value);
}
@@ -897,18 +794,14 @@ bool QQuickShaderEffect::isComponentComplete() const
return QQuickItem::isComponentComplete();
}
-QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests
-{
- return m_impl->parseLog();
-}
-
bool QQuickShaderEffect::updateUniformValue(const QByteArray &name, const QVariant &value)
{
auto node = static_cast<QSGShaderEffectNode *>(QQuickItemPrivate::get(this)->paintNode);
if (!node)
return false;
- return m_impl->updateUniformValue(name, value, node);
+ Q_D(QQuickShaderEffect);
+ return d->updateUniformValue(name, value, node);
}
void QQuickShaderEffectPrivate::updatePolish()
@@ -916,7 +809,7 @@ void QQuickShaderEffectPrivate::updatePolish()
Q_Q(QQuickShaderEffect);
if (!qmlEngine(q))
return;
- q->m_impl->maybeUpdateShaders();
+ maybeUpdateShaders();
}
constexpr int indexToMappedId(const int shaderType, const int idx)
@@ -934,10 +827,8 @@ constexpr int mappedIdToShaderType(const int mappedId)
return mappedId >> 16;
}
-QQuickShaderEffectImpl::QQuickShaderEffectImpl(QQuickShaderEffect *item)
- : QObject(item)
- , m_item(item)
- , m_meshResolution(1, 1)
+QQuickShaderEffectPrivate::QQuickShaderEffectPrivate()
+ : m_meshResolution(1, 1)
, m_mesh(nullptr)
, m_cullMode(QQuickShaderEffect::NoCulling)
, m_blending(true)
@@ -951,7 +842,7 @@ QQuickShaderEffectImpl::QQuickShaderEffectImpl(QQuickShaderEffect *item)
m_inProgress[i] = nullptr;
}
-QQuickShaderEffectImpl::~QQuickShaderEffectImpl()
+QQuickShaderEffectPrivate::~QQuickShaderEffectPrivate()
{
for (int i = 0; i < NShader; ++i) {
disconnectSignals(Shader(i));
@@ -961,63 +852,68 @@ QQuickShaderEffectImpl::~QQuickShaderEffectImpl()
delete m_mgr;
}
-void QQuickShaderEffectImpl::setFragmentShader(const QUrl &fileUrl)
+void QQuickShaderEffectPrivate::setFragmentShader(const QUrl &fileUrl)
{
+ Q_Q(QQuickShaderEffect);
if (m_fragShader == fileUrl)
return;
m_fragShader = fileUrl;
m_fragNeedsUpdate = true;
- if (m_item->isComponentComplete())
+ if (q->isComponentComplete())
maybeUpdateShaders();
- emit m_item->fragmentShaderChanged();
+ emit q->fragmentShaderChanged();
}
-void QQuickShaderEffectImpl::setVertexShader(const QUrl &fileUrl)
+void QQuickShaderEffectPrivate::setVertexShader(const QUrl &fileUrl)
{
+ Q_Q(QQuickShaderEffect);
if (m_vertShader == fileUrl)
return;
m_vertShader = fileUrl;
m_vertNeedsUpdate = true;
- if (m_item->isComponentComplete())
+ if (q->isComponentComplete())
maybeUpdateShaders();
- emit m_item->vertexShaderChanged();
+ emit q->vertexShaderChanged();
}
-void QQuickShaderEffectImpl::setBlending(bool enable)
+void QQuickShaderEffectPrivate::setBlending(bool enable)
{
+ Q_Q(QQuickShaderEffect);
if (m_blending == enable)
return;
m_blending = enable;
- m_item->update();
- emit m_item->blendingChanged();
+ q->update();
+ emit q->blendingChanged();
}
-QVariant QQuickShaderEffectImpl::mesh() const
+QVariant QQuickShaderEffectPrivate::mesh() const
{
return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
: QVariant::fromValue(m_meshResolution);
}
-void QQuickShaderEffectImpl::setMesh(const QVariant &mesh)
+void QQuickShaderEffectPrivate::setMesh(const QVariant &mesh)
{
+ Q_Q(QQuickShaderEffect);
QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
if (newMesh && newMesh == m_mesh)
return;
if (m_mesh)
- disconnect(m_mesh, SIGNAL(geometryChanged()), this, nullptr);
+ QObject::disconnect(m_meshConnection);
m_mesh = newMesh;
if (m_mesh) {
- connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(markGeometryDirtyAndUpdate()));
+ m_meshConnection = QObject::connect(m_mesh, &QQuickShaderEffectMesh::geometryChanged, q,
+ [this] { markGeometryDirtyAndUpdate(); });
} else {
if (mesh.canConvert<QSize>()) {
m_meshResolution = mesh.toSize();
@@ -1039,38 +935,40 @@ void QQuickShaderEffectImpl::setMesh(const QVariant &mesh)
}
m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
- m_item->update();
+ q->update();
- emit m_item->meshChanged();
+ emit q->meshChanged();
}
-void QQuickShaderEffectImpl::setCullMode(QQuickShaderEffect::CullMode face)
+void QQuickShaderEffectPrivate::setCullMode(QQuickShaderEffect::CullMode face)
{
+ Q_Q(QQuickShaderEffect);
if (m_cullMode == face)
return;
m_cullMode = face;
- m_item->update();
- emit m_item->cullModeChanged();
+ q->update();
+ emit q->cullModeChanged();
}
-void QQuickShaderEffectImpl::setSupportsAtlasTextures(bool supports)
+void QQuickShaderEffectPrivate::setSupportsAtlasTextures(bool supports)
{
+ Q_Q(QQuickShaderEffect);
if (m_supportsAtlasTextures == supports)
return;
m_supportsAtlasTextures = supports;
markGeometryDirtyAndUpdate();
- emit m_item->supportsAtlasTexturesChanged();
+ emit q->supportsAtlasTexturesChanged();
}
-QString QQuickShaderEffectImpl::parseLog()
+QString QQuickShaderEffectPrivate::parseLog()
{
maybeUpdateShaders();
return log();
}
-QString QQuickShaderEffectImpl::log() const
+QString QQuickShaderEffectPrivate::log() const
{
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
if (!mgr)
@@ -1079,7 +977,7 @@ QString QQuickShaderEffectImpl::log() const
return mgr->log();
}
-QQuickShaderEffect::Status QQuickShaderEffectImpl::status() const
+QQuickShaderEffect::Status QQuickShaderEffectPrivate::status() const
{
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
if (!mgr)
@@ -1088,26 +986,29 @@ QQuickShaderEffect::Status QQuickShaderEffectImpl::status() const
return QQuickShaderEffect::Status(mgr->status());
}
-void QQuickShaderEffectImpl::handleEvent(QEvent *event)
+void QQuickShaderEffectPrivate::handleEvent(QEvent *event)
{
if (event->type() == QEvent::DynamicPropertyChange) {
const auto propertyName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
- const auto mappedId = findMappedShaderVariableId(propertyName);
- if (mappedId)
- propertyChanged(*mappedId);
+ for (int i = 0; i < NShader; ++i) {
+ const auto mappedId = findMappedShaderVariableId(propertyName, Shader(i));
+ if (mappedId)
+ propertyChanged(*mappedId);
+ }
}
}
-void QQuickShaderEffectImpl::handleGeometryChanged(const QRectF &, const QRectF &)
+void QQuickShaderEffectPrivate::handleGeometryChanged(const QRectF &, const QRectF &)
{
m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
}
-QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+QSGNode *QQuickShaderEffectPrivate::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
+ Q_Q(QQuickShaderEffect);
QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
- if (m_item->width() <= 0 || m_item->height() <= 0) {
+ if (q->width() <= 0 || q->height() <= 0) {
delete node;
return nullptr;
}
@@ -1124,15 +1025,14 @@ QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickI
}
if (!node) {
- QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->window())->context;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(q->window())->context;
node = rc->sceneGraphContext()->createShaderEffectNode(rc);
if (!node) {
qWarning("No shader effect node");
return nullptr;
}
m_dirty = QSGShaderEffectNode::DirtyShaderAll;
- connect(node, &QSGShaderEffectNode::textureChanged,
- this, &QQuickShaderEffectImpl::markGeometryDirtyAndUpdateIfSupportsAtlas);
+ QObject::connect(node, &QSGShaderEffectNode::textureChanged, q, [this] { markGeometryDirtyAndUpdateIfSupportsAtlas(); });
}
QSGShaderEffectNode::SyncData sd;
@@ -1145,6 +1045,9 @@ QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickI
sd.fragment.shader = &m_shaders[Fragment];
sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
+ sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
+
node->syncMaterial(&sd);
if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
@@ -1154,7 +1057,7 @@ QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickI
}
if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
- const QRectF rect(0, 0, m_item->width(), m_item->height());
+ const QRectF rect(0, 0, q->width(), q->height());
QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
QSGGeometry *geometry = node->geometry();
@@ -1177,8 +1080,9 @@ QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickI
return node;
}
-void QQuickShaderEffectImpl::maybeUpdateShaders()
+void QQuickShaderEffectPrivate::maybeUpdateShaders()
{
+ Q_Q(QQuickShaderEffect);
if (m_vertNeedsUpdate)
m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
if (m_fragNeedsUpdate)
@@ -1190,14 +1094,15 @@ void QQuickShaderEffectImpl::maybeUpdateShaders()
// scenegraph ready. Schedule the polish to try again later. In case #2
// the backend probably does not have shadereffect support so there is
// nothing to do for us here.
- if (!m_item->window() || !m_item->window()->isSceneGraphInitialized())
- m_item->polish();
+ if (!q->window() || !q->window()->isSceneGraphInitialized())
+ q->polish();
}
}
-bool QQuickShaderEffectImpl::updateUniformValue(const QByteArray &name, const QVariant &value,
+bool QQuickShaderEffectPrivate::updateUniformValue(const QByteArray &name, const QVariant &value,
QSGShaderEffectNode *node)
{
+ Q_Q(QQuickShaderEffect);
const auto mappedId = findMappedShaderVariableId(name);
if (!mappedId)
return false;
@@ -1223,18 +1128,23 @@ bool QQuickShaderEffectImpl::updateUniformValue(const QByteArray &name, const QV
sd.fragment.shader = &m_shaders[Fragment];
sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
sd.fragment.dirtyTextures = {};
+ sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
node->syncMaterial(&sd);
return true;
}
-void QQuickShaderEffectImpl::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+void QQuickShaderEffectPrivate::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
{
+ if (inDestructor)
+ return;
+
// Move the window ref.
if (change == QQuickItem::ItemSceneChange) {
for (int shaderType = 0; shaderType < NShader; ++shaderType) {
- for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
if (source) {
@@ -1249,48 +1159,56 @@ void QQuickShaderEffectImpl::handleItemChange(QQuickItem::ItemChange change, con
}
}
-QSGGuiThreadShaderEffectManager *QQuickShaderEffectImpl::shaderEffectManager() const
+QSGGuiThreadShaderEffectManager *QQuickShaderEffectPrivate::shaderEffectManager() const
{
+ Q_Q(const QQuickShaderEffect);
if (!m_mgr) {
// return null if this is not the gui thread and not already created
- if (QThread::currentThread() != m_item->thread())
+ if (QThread::currentThread() != q->thread())
return m_mgr;
- QQuickWindow *w = m_item->window();
+ QQuickWindow *w = q->window();
if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
m_mgr = QQuickWindowPrivate::get(w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager();
if (m_mgr) {
- connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(logChanged()));
- connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(statusChanged()));
- connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickShaderEffectImpl::shaderCodePrepared);
+ QObject::connect(m_mgr, &QSGGuiThreadShaderEffectManager::logAndStatusChanged, q, &QQuickShaderEffect::logChanged);
+ QObject::connect(m_mgr, &QSGGuiThreadShaderEffectManager::logAndStatusChanged, q, &QQuickShaderEffect::statusChanged);
+ QObject::connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, q,
+ [this](bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QUrl &loadUrl, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
+ { const_cast<QQuickShaderEffectPrivate *>(this)->shaderCodePrepared(ok, typeHint, loadUrl, result); });
}
}
}
-
return m_mgr;
}
-void QQuickShaderEffectImpl::disconnectSignals(Shader shaderType)
+void QQuickShaderEffectPrivate::disconnectSignals(Shader shaderType)
{
+ Q_Q(QQuickShaderEffect);
for (auto *mapper : m_mappers[shaderType]) {
void *a = mapper;
if (mapper)
- QObjectPrivate::disconnect(m_item, mapper->signalIndex(), &a);
+ QObjectPrivate::disconnect(q, mapper->signalIndex(), &a);
}
- for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ for (const auto &vd : std::as_const(m_shaders[shaderType].varData)) {
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
if (source) {
- if (m_item->window())
+ if (q->window())
QQuickItemPrivate::get(source)->derefWindow();
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ auto it = m_destroyedConnections.constFind(source);
+ if (it != m_destroyedConnections.constEnd()) {
+ QObject::disconnect(*it);
+ m_destroyedConnections.erase(it);
+ }
}
}
}
}
-void QQuickShaderEffectImpl::clearMappers(QQuickShaderEffectImpl::Shader shaderType)
+void QQuickShaderEffectPrivate::clearMappers(QQuickShaderEffectPrivate::Shader shaderType)
{
- for (auto *mapper : qAsConst(m_mappers[shaderType])) {
+ for (auto *mapper : std::as_const(m_mappers[shaderType])) {
if (mapper)
mapper->destroyIfLastRef();
}
@@ -1317,8 +1235,9 @@ void qtquick_shadereffect_purge_gui_thread_shader_cache()
shaderInfoCache()->clear();
}
-bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl)
+bool QQuickShaderEffectPrivate::updateShader(Shader shaderType, const QUrl &fileUrl)
{
+ Q_Q(QQuickShaderEffect);
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
if (!mgr)
return false;
@@ -1327,11 +1246,11 @@ bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl
disconnectSignals(shaderType);
- m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
+ m_shaders[shaderType].shaderInfo.variables.clear();
m_shaders[shaderType].varData.clear();
if (!fileUrl.isEmpty()) {
- const QQmlContext *context = qmlContext(m_item);
+ const QQmlContext *context = qmlContext(q);
const QUrl loadUrl = context ? context->resolvedUrl(fileUrl) : fileUrl;
auto it = shaderInfoCache()->constFind(loadUrl);
if (it != shaderInfoCache()->cend()) {
@@ -1364,7 +1283,7 @@ bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
+ v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
@@ -1373,13 +1292,14 @@ bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl
updateShaderVars(shaderType);
m_dirty |= QSGShaderEffectNode::DirtyShaders;
- m_item->update();
+ q->update();
return true;
}
-void QQuickShaderEffectImpl::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+void QQuickShaderEffectPrivate::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
const QUrl &loadUrl, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
{
+ Q_Q(QQuickShaderEffect);
const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
// If another call was made to updateShader() for the same shader type in
@@ -1404,30 +1324,28 @@ void QQuickShaderEffectImpl::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffec
shaderInfoCache()->insert(loadUrl, m_shaders[shaderType].shaderInfo);
updateShaderVars(shaderType);
m_dirty |= QSGShaderEffectNode::DirtyShaders;
- m_item->update();
+ q->update();
}
-void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
+void QQuickShaderEffectPrivate::updateShaderVars(Shader shaderType)
{
+ Q_Q(QQuickShaderEffect);
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
if (!mgr)
return;
const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
- const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
+ const int varCount = m_shaders[shaderType].shaderInfo.variables.size();
m_shaders[shaderType].varData.resize(varCount);
- // Reuse signal mappers as much as possible since the mapping is based on
- // the index and shader type which are both constant.
- if (m_mappers[shaderType].count() < varCount)
- m_mappers[shaderType].resize(varCount);
+ // Recreate signal mappers when the shader has changed.
+ clearMappers(shaderType);
- auto *engine = qmlEngine(m_item);
- QQmlPropertyCache *propCache = engine ? QQmlData::ensurePropertyCache(engine, m_item) : nullptr;
+ QQmlPropertyCache::ConstPtr propCache = QQmlData::ensurePropertyCache(q);
if (!m_itemMetaObject)
- m_itemMetaObject = m_item->metaObject();
+ m_itemMetaObject = q->metaObject();
// Hook up the signals to get notified about changes for properties that
// correspond to variables in the shader. Store also the values.
@@ -1464,7 +1382,7 @@ void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
// Find the property on the ShaderEffect item.
int propIdx = -1;
- QQmlPropertyData *pd = nullptr;
+ const QQmlPropertyData *pd = nullptr;
if (propCache) {
pd = propCache->property(QLatin1String(v.name), nullptr, nullptr);
if (pd) {
@@ -1477,90 +1395,113 @@ void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
if (pd->notifyIndex() == -1) {
qWarning("QQuickShaderEffect: property '%s' does not have notification method!", v.name.constData());
} else {
- auto *&mapper = m_mappers[shaderType][i];
- if (!mapper) {
- const int mappedId = indexToMappedId(shaderType, i);
- mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
- this->propertyChanged(mappedId);
- });
- }
+ const int mappedId = indexToMappedId(shaderType, i);
+ auto mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
+ this->propertyChanged(mappedId);
+ });
+ m_mappers[shaderType].append(mapper);
mapper->setSignalIndex(m_itemMetaObject->property(propIdx).notifySignal().methodIndex());
- Q_ASSERT(m_item->metaObject() == m_itemMetaObject);
- bool ok = QObjectPrivate::connectImpl(m_item, pd->notifyIndex(), m_item, nullptr, mapper,
+ Q_ASSERT(q->metaObject() == m_itemMetaObject);
+ bool ok = QObjectPrivate::connectImpl(q, pd->notifyIndex(), q, nullptr, mapper,
Qt::AutoConnection, nullptr, m_itemMetaObject);
if (!ok)
qWarning() << "Failed to connect to property" << m_itemMetaObject->property(propIdx).name()
<< "(" << propIdx << ", signal index" << pd->notifyIndex()
- << ") of item" << m_item;
+ << ") of item" << q;
}
}
} else {
// Do not warn for dynamic properties.
- if (!m_item->property(v.name.constData()).isValid())
+ if (!q->property(v.name.constData()).isValid())
qWarning("ShaderEffect: '%s' does not have a matching property", v.name.constData());
}
vd.propertyIndex = propIdx;
- vd.value = getValueFromProperty(m_item, m_itemMetaObject, v.name, vd.propertyIndex);
+ vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
if (source) {
- if (m_item->window())
- QQuickItemPrivate::get(source)->refWindow(m_item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ if (q->window())
+ QQuickItemPrivate::get(source)->refWindow(q->window());
+
+ // Cannot just pass q as the 'context' for the connect(). The
+ // order of destruction is...complicated. Having an inline
+ // source (e.g. source: ShaderEffectSource { ... } in QML would
+ // emit destroyed() after the connection was already gone. To
+ // work that around, store the Connection and manually
+ // disconnect instead.
+ if (!m_destroyedConnections.contains(source))
+ m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
}
}
}
}
-bool QQuickShaderEffectImpl::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
+std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name) const
{
for (int shaderType = 0; shaderType < NShader; ++shaderType) {
- for (int idx = 0; idx < m_shaders[shaderType].varData.count(); ++idx) {
- if (shaderType != typeToSkip || idx != indexToSkip) {
- const auto &vd(m_shaders[shaderType].varData[idx]);
- if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
- return false;
- }
+ const auto &vars = m_shaders[shaderType].shaderInfo.variables;
+ for (int idx = 0; idx < vars.size(); ++idx) {
+ if (vars[idx].name == name)
+ return indexToMappedId(shaderType, idx);
}
}
- return true;
+
+ return {};
}
-std::optional<int> QQuickShaderEffectImpl::findMappedShaderVariableId(const QByteArray &name) const
+std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const
{
- for (int shaderType = 0; shaderType < NShader; ++shaderType) {
- const auto &vars = m_shaders[shaderType].shaderInfo.variables;
- for (int idx = 0; idx < vars.count(); ++idx) {
- if (vars[idx].name == name)
- return indexToMappedId(shaderType, idx);
- }
+ const auto &vars = m_shaders[shaderType].shaderInfo.variables;
+ for (int idx = 0; idx < vars.size(); ++idx) {
+ if (vars[idx].name == name)
+ return indexToMappedId(shaderType, idx);
}
return {};
}
-void QQuickShaderEffectImpl::propertyChanged(int mappedId)
+bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (int idx = 0; idx < m_shaders[shaderType].varData.size(); ++idx) {
+ if (shaderType != typeToSkip || idx != indexToSkip) {
+ const auto &vd(m_shaders[shaderType].varData[idx]);
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void QQuickShaderEffectPrivate::propertyChanged(int mappedId)
{
+ Q_Q(QQuickShaderEffect);
const Shader type = Shader(mappedIdToShaderType(mappedId));
const int idx = mappedIdToIndex(mappedId);
const auto &v(m_shaders[type].shaderInfo.variables[idx]);
auto &vd(m_shaders[type].varData[idx]);
- vd.value = getValueFromProperty(m_item, m_itemMetaObject, v.name, vd.propertyIndex);
+ QVariant oldValue = vd.value;
+ vd.value = getValueFromProperty(q, m_itemMetaObject, v.name, vd.propertyIndex);
if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(oldValue));
if (source) {
- if (m_item->window())
+ if (q->window())
QQuickItemPrivate::get(source)->derefWindow();
- // QObject::disconnect() will disconnect all matching connections.
// If the same source has been attached to two separate
// textures/samplers, then changing one of them would trigger both
// to be disconnected. So check first.
- if (sourceIsUnique(source, type, idx))
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ if (sourceIsUnique(source, type, idx)) {
+ auto it = m_destroyedConnections.constFind(source);
+ if (it != m_destroyedConnections.constEnd()) {
+ QObject::disconnect(*it);
+ m_destroyedConnections.erase(it);
+ }
+ }
}
source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
@@ -1569,9 +1510,10 @@ void QQuickShaderEffectImpl::propertyChanged(int mappedId)
// parent, but if the source item is "inline" rather than a reference -- i.e.
// "property variant source: Image { }" instead of "property variant source: foo" -- it
// will not get a parent. In those cases, 'source' should get the window from 'item'.
- if (m_item->window())
- QQuickItemPrivate::get(source)->refWindow(m_item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ if (q->window())
+ QQuickItemPrivate::get(source)->refWindow(q->window());
+ if (!m_destroyedConnections.contains(source))
+ m_destroyedConnections.insert(source, QObject::connect(source, &QObject::destroyed, [this](QObject *obj) { sourceDestroyed(obj); }));
}
m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
@@ -1582,10 +1524,10 @@ void QQuickShaderEffectImpl::propertyChanged(int mappedId)
m_dirtyConstants[type].insert(idx);
}
- m_item->update();
+ q->update();
}
-void QQuickShaderEffectImpl::sourceDestroyed(QObject *object)
+void QQuickShaderEffectPrivate::sourceDestroyed(QObject *object)
{
for (int shaderType = 0; shaderType < NShader; ++shaderType) {
for (auto &vd : m_shaders[shaderType].varData) {
@@ -1597,21 +1539,19 @@ void QQuickShaderEffectImpl::sourceDestroyed(QObject *object)
}
}
-void QQuickShaderEffectImpl::markGeometryDirtyAndUpdate()
+void QQuickShaderEffectPrivate::markGeometryDirtyAndUpdate()
{
+ Q_Q(QQuickShaderEffect);
m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
- m_item->update();
+ q->update();
}
-void QQuickShaderEffectImpl::markGeometryDirtyAndUpdateIfSupportsAtlas()
+void QQuickShaderEffectPrivate::markGeometryDirtyAndUpdateIfSupportsAtlas()
{
if (m_supportsAtlasTextures)
markGeometryDirtyAndUpdate();
}
-
-
QT_END_NAMESPACE
#include "moc_qquickshadereffect_p.cpp"
-#include "qquickshadereffect.moc"