aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2020-06-08 13:01:04 +0200
committerPaul Olav Tvete <paul.tvete@qt.io>2020-06-10 06:02:04 +0200
commitbb906cecec40b7de6a39d97f7c07a883de40195d (patch)
treec7fd7711bd24500ea4e5e941082504decad4f525
parent4fd3ad8c4689fc40c4ca549bba95632dc4d1a990 (diff)
Remove QQuickGenericShaderEffect
Keep it as an internal class for now, with the name QQuickShaderEffectImpl, and move it to qquickshadereffect.cpp. In the long term, we want to get rid of the extra QObject, but that requires careful untangling of the connections and the timing of cleanup at destruction. Task-number: QTBUG-83977 Change-Id: I6513bd0d8fc8522a15049b70ab43fc222088e7d0 Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r--src/quick/CMakeLists.txt1
-rw-r--r--src/quick/items/items.pri6
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp738
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h154
-rw-r--r--src/quick/items/qquickshadereffect.cpp780
-rw-r--r--src/quick/items/qquickshadereffect_p.h4
6 files changed, 782 insertions, 901 deletions
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index c1362e5bb6..f045a5f05d 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -495,7 +495,6 @@ qt_extend_target(Quick CONDITION QT_FEATURE_quick_repeater
qt_extend_target(Quick CONDITION QT_FEATURE_quick_shadereffect
SOURCES
- items/qquickgenericshadereffect.cpp items/qquickgenericshadereffect_p.h
items/qquickshadereffect.cpp items/qquickshadereffect_p.h
items/qquickshadereffectmesh.cpp items/qquickshadereffectmesh_p.h
items/qquickshadereffectsource.cpp items/qquickshadereffectsource_p.h
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index f514c306ff..c3f350809d 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -209,13 +209,11 @@ qtConfig(quick-shadereffect) {
HEADERS += \
$$PWD/qquickshadereffectsource_p.h \
$$PWD/qquickshadereffectmesh_p.h \
- $$PWD/qquickshadereffect_p.h \
- $$PWD/qquickgenericshadereffect_p.h
+ $$PWD/qquickshadereffect_p.h
SOURCES += \
$$PWD/qquickshadereffectsource.cpp \
$$PWD/qquickshadereffectmesh.cpp \
- $$PWD/qquickshadereffect.cpp \
- $$PWD/qquickgenericshadereffect.cpp
+ $$PWD/qquickshadereffect.cpp
qtConfig(opengl) {
OTHER_FILES += \
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
deleted file mode 100644
index 6f82d94ae9..0000000000
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ /dev/null
@@ -1,738 +0,0 @@
-/****************************************************************************
-**
-** 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/qquickgenericshadereffect_p.h>
-#include <private/qquickwindow_p.h>
-#include <private/qquickitem_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QtPrivate {
-class EffectSlotMapper: public QtPrivate::QSlotObjectBase
-{
-public:
- typedef std::function<void()> PropChangedFunc;
-
- explicit EffectSlotMapper(PropChangedFunc func)
- : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
- { ref(); }
-
- void setSignalIndex(int idx) { _signalIndex = idx; }
- int signalIndex() const { return _signalIndex; }
-
-private:
- int _signalIndex;
- PropChangedFunc func;
-
- static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
- {
- auto thiz = static_cast<EffectSlotMapper*>(this_);
- switch (which) {
- case Destroy:
- delete thiz;
- break;
- case Call:
- thiz->func();
- break;
- case Compare:
- *ret = thiz == reinterpret_cast<EffectSlotMapper *>(a[0]);
- break;
- case NumOperations: ;
- }
- }
-};
-} // namespace QtPrivate
-
-// The generic shader effect is used whenever on the RHI code path, or when the
-// scenegraph backend indicates SupportsShaderEffectNode. This, unlike the
-// monolithic and interconnected (e.g. with particles) OpenGL variant, passes
-// most of the work to a scenegraph node created via the adaptation layer, thus
-// allowing different implementation in the backends.
-
-QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent)
- : QObject(parent)
- , m_item(item)
- , m_meshResolution(1, 1)
- , m_mesh(nullptr)
- , m_cullMode(QQuickShaderEffect::NoCulling)
- , m_blending(true)
- , m_supportsAtlasTextures(false)
- , m_mgr(nullptr)
- , m_fragNeedsUpdate(true)
- , m_vertNeedsUpdate(true)
-{
- qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
- for (int i = 0; i < NShader; ++i)
- m_inProgress[i] = nullptr;
-}
-
-QQuickGenericShaderEffect::~QQuickGenericShaderEffect()
-{
- for (int i = 0; i < NShader; ++i) {
- disconnectSignals(Shader(i));
- clearMappers(Shader(i));
- }
-
- delete m_mgr;
-}
-
-void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src)
-{
- // Compare the actual values since they are often just filenames.
- // Optimizing by comparing constData() is a bad idea since seemingly static
- // strings in QML may in fact have different addresses when a binding
- // triggers assigning the "same" value to the property.
- if (m_fragShader == src)
- return;
-
- m_fragShader = src;
-
- m_fragNeedsUpdate = true;
- if (m_item->isComponentComplete())
- maybeUpdateShaders();
-
- emit m_item->fragmentShaderChanged();
-}
-
-void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
-{
- if (m_vertShader == src)
- return;
-
- m_vertShader = src;
-
- m_vertNeedsUpdate = true;
- if (m_item->isComponentComplete())
- maybeUpdateShaders();
-
- emit m_item->vertexShaderChanged();
-}
-
-void QQuickGenericShaderEffect::setBlending(bool enable)
-{
- if (m_blending == enable)
- return;
-
- m_blending = enable;
- m_item->update();
- emit m_item->blendingChanged();
-}
-
-QVariant QQuickGenericShaderEffect::mesh() const
-{
- return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
- : QVariant::fromValue(m_meshResolution);
-}
-
-void QQuickGenericShaderEffect::setMesh(const QVariant &mesh)
-{
- 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);
-
- m_mesh = newMesh;
-
- if (m_mesh) {
- connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(markGeometryDirtyAndUpdate()));
- } else {
- if (mesh.canConvert<QSize>()) {
- m_meshResolution = mesh.toSize();
- } else {
- QList<QByteArray> res = mesh.toByteArray().split('x');
- bool ok = res.size() == 2;
- if (ok) {
- int w = res.at(0).toInt(&ok);
- if (ok) {
- int h = res.at(1).toInt(&ok);
- if (ok)
- m_meshResolution = QSize(w, h);
- }
- }
- if (!ok)
- qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
- }
- m_defaultMesh.setResolution(m_meshResolution);
- }
-
- m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
- m_item->update();
-
- emit m_item->meshChanged();
-}
-
-void QQuickGenericShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
-{
- if (m_cullMode == face)
- return;
-
- m_cullMode = face;
- m_item->update();
- emit m_item->cullModeChanged();
-}
-
-void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports)
-{
- if (m_supportsAtlasTextures == supports)
- return;
-
- m_supportsAtlasTextures = supports;
- markGeometryDirtyAndUpdate();
- emit m_item->supportsAtlasTexturesChanged();
-}
-
-QString QQuickGenericShaderEffect::parseLog()
-{
- maybeUpdateShaders();
- return log();
-}
-
-QString QQuickGenericShaderEffect::log() const
-{
- QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
- if (!mgr)
- return QString();
-
- return mgr->log();
-}
-
-QQuickShaderEffect::Status QQuickGenericShaderEffect::status() const
-{
- QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
- if (!mgr)
- return QQuickShaderEffect::Uncompiled;
-
- return QQuickShaderEffect::Status(mgr->status());
-}
-
-void QQuickGenericShaderEffect::handleEvent(QEvent *event)
-{
- if (event->type() == QEvent::DynamicPropertyChange) {
- QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
- 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 == e->propertyName()) {
- propertyChanged((shaderType << 16) | idx);
- break;
- }
- }
- }
- }
-}
-
-void QQuickGenericShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
-{
- m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
-}
-
-QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
-{
- QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
-
- if (m_item->width() <= 0 || m_item->height() <= 0) {
- delete node;
- return nullptr;
- }
-
- // Do not change anything while a new shader is being reflected or compiled.
- if (m_inProgress[Vertex] || m_inProgress[Fragment])
- return node;
-
- // The manager should be already created on the gui thread. Just take that instance.
- QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
- if (!mgr) {
- delete node;
- return nullptr;
- }
-
- if (!node) {
- QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->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, &QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas);
- }
-
- QSGShaderEffectNode::SyncData sd;
- sd.dirty = m_dirty;
- sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
- sd.blending = m_blending;
- sd.vertex.shader = &m_shaders[Vertex];
- sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
- sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
- sd.fragment.shader = &m_shaders[Fragment];
- sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
- sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
- node->syncMaterial(&sd);
-
- if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
- node->setGeometry(nullptr);
- m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
- m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
- }
-
- if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
- const QRectF rect(0, 0, m_item->width(), m_item->height());
- QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
- QSGGeometry *geometry = node->geometry();
-
- const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
- geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
-
- node->setFlag(QSGNode::OwnsGeometry, false);
- node->setGeometry(geometry);
- node->setFlag(QSGNode::OwnsGeometry, true);
-
- m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
- }
-
- m_dirty = {};
- for (int i = 0; i < NShader; ++i) {
- m_dirtyConstants[i].clear();
- m_dirtyTextures[i].clear();
- }
-
- return node;
-}
-
-void QQuickGenericShaderEffect::maybeUpdateShaders()
-{
- if (m_vertNeedsUpdate)
- m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
- if (m_fragNeedsUpdate)
- m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
- if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
- // This function is invoked either from componentComplete or in a
- // response to a previous invocation's polish() request. If this is
- // case #1 then updateShader can fail due to not having a window or
- // 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();
- }
-}
-
-void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
-{
- // Move the window ref.
- if (change == QQuickItem::ItemSceneChange) {
- for (int shaderType = 0; shaderType < NShader; ++shaderType) {
- for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
- if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
- if (source) {
- if (value.window)
- QQuickItemPrivate::get(source)->refWindow(value.window);
- else
- QQuickItemPrivate::get(source)->derefWindow();
- }
- }
- }
- }
- }
-}
-
-QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager() const
-{
- if (!m_mgr) {
- // return null if this is not the gui thread and not already created
- if (QThread::currentThread() != m_item->thread())
- return m_mgr;
- QQuickWindow *w = m_item->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, &QQuickGenericShaderEffect::shaderCodePrepared);
- }
- }
- }
-
- return m_mgr;
-}
-
-void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
-{
- for (auto *mapper : m_mappers[shaderType]) {
- void *a = mapper;
- if (mapper)
- QObjectPrivate::disconnect(m_item, mapper->signalIndex(), &a);
- }
- for (const auto &vd : qAsConst(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())
- QQuickItemPrivate::get(source)->derefWindow();
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
- }
- }
- }
-}
-
-void QQuickGenericShaderEffect::clearMappers(QQuickGenericShaderEffect::Shader shaderType)
-{
- for (auto *mapper : qAsConst(m_mappers[shaderType])) {
- if (mapper)
- mapper->destroyIfLastRef();
- }
- m_mappers[shaderType].clear();
-}
-
-static inline QVariant getValueFromProperty(QObject *item, const QMetaObject *itemMetaObject,
- const QByteArray &name, int propertyIndex)
-{
- QVariant value;
- if (propertyIndex == -1) {
- value = item->property(name);
- } else {
- value = itemMetaObject->property(propertyIndex).read(item);
- }
- return value;
-}
-
-struct ShaderInfoCache
-{
- bool contains(const QByteArray &key) const
- {
- return m_shaderInfoCache.contains(key);
- }
-
- QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
- {
- return m_shaderInfoCache.value(key);
- }
-
- void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
- {
- m_shaderInfoCache.insert(key, value);
- }
-
- QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
-};
-
-Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
-
-bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
-{
- QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
- if (!mgr)
- return false;
-
- const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
-
- disconnectSignals(shaderType);
-
- m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
- m_shaders[shaderType].varData.clear();
-
- if (!src.isEmpty()) {
- if (shaderInfoCache()->contains(src)) {
- m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src);
- m_shaders[shaderType].hasShaderCode = true;
- } else {
- // Each prepareShaderCode call needs its own work area, hence the
- // dynamic alloc. If there are calls in progress, let those run to
- // finish, their results can then simply be ignored because
- // m_inProgress indicates what we care about.
- m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
- const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint =
- shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex
- : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
- // Figure out what input parameters and variables are used in the
- // shader. For file-based shader source/bytecode this is where the data
- // is pulled in from the file. Some backends may choose to do
- // source->bytecode compilation as well in this step.
- mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]);
- // the rest is handled in shaderCodePrepared()
- return true;
- }
- } else {
- m_shaders[shaderType].hasShaderCode = false;
- if (shaderType == Fragment) {
- // With built-in shaders hasShaderCode is set to false and all
- // metadata is empty, as it is left up to the node to provide a
- // built-in default shader and its metadata. However, in case of
- // the built-in fragment shader the value for 'source' has to be
- // provided and monitored like with an application-provided shader.
- QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
- v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
- v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
- : QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
- m_shaders[shaderType].shaderInfo.variables.append(v);
- }
- }
-
- updateShaderVars(shaderType);
- m_dirty |= QSGShaderEffectNode::DirtyShaders;
- m_item->update();
- return true;
-}
-
-void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
- const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
-{
- const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
-
- // If another call was made to updateShader() for the same shader type in
- // the meantime then our results are useless, just drop them.
- if (result != m_inProgress[shaderType]) {
- delete result;
- return;
- }
-
- m_shaders[shaderType].shaderInfo = *result;
- delete result;
- m_inProgress[shaderType] = nullptr;
-
- if (!ok) {
- qWarning("ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log()));
- m_shaders[shaderType].hasShaderCode = false;
- return;
- }
-
- m_shaders[shaderType].hasShaderCode = true;
- shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo);
- updateShaderVars(shaderType);
- m_dirty |= QSGShaderEffectNode::DirtyShaders;
- m_item->update();
-}
-
-void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
-{
- QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
- if (!mgr)
- return;
-
- const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
-
- const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
- 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);
-
- auto *engine = qmlEngine(m_item);
- QQmlPropertyCache *propCache = engine ? QQmlData::ensurePropertyCache(engine, m_item) : nullptr;
-
- if (!m_itemMetaObject)
- m_itemMetaObject = m_item->metaObject();
-
- // Hook up the signals to get notified about changes for properties that
- // correspond to variables in the shader. Store also the values.
- for (int i = 0; i < varCount; ++i) {
- const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
- QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
- const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
- if (isSpecial) {
- if (v.name == "qt_Opacity")
- vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
- else if (v.name == "qt_Matrix")
- vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
- else if (v.name.startsWith("qt_SubRect_"))
- vd.specialType = QSGShaderEffectNode::VariableData::SubRect;
- continue;
- }
-
- // The value of a property corresponding to a sampler is the source
- // item ref, unless there are separate texture objects in which case
- // the sampler is ignored (here).
- if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
- if (texturesSeparate) {
- vd.specialType = QSGShaderEffectNode::VariableData::Unused;
- continue;
- } else {
- vd.specialType = QSGShaderEffectNode::VariableData::Source;
- }
- } else if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
- Q_ASSERT(texturesSeparate);
- vd.specialType = QSGShaderEffectNode::VariableData::Source;
- } else {
- vd.specialType = QSGShaderEffectNode::VariableData::None;
- }
-
- // Find the property on the ShaderEffect item.
- int propIdx = -1;
- QQmlPropertyData *pd = nullptr;
- if (propCache) {
- pd = propCache->property(QLatin1String(v.name), nullptr, nullptr);
- if (pd) {
- if (!pd->isFunction())
- propIdx = pd->coreIndex();
- }
- }
- if (propIdx >= 0) {
- if (pd && !pd->isFunction()) {
- if (pd->notifyIndex() == -1) {
- qWarning("QQuickGenericShaderEffect: property '%s' does not have notification method!", v.name.constData());
- } else {
- auto *&mapper = m_mappers[shaderType][i];
- if (!mapper) {
- const int mappedId = i | (shaderType << 16);
- mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
- this->propertyChanged(mappedId);
- });
- }
- 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,
- 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;
- }
- }
- } else {
- // Do not warn for dynamic properties.
- if (!m_item->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);
- 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*)));
- }
- }
- }
-}
-
-bool QQuickGenericShaderEffect::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) 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;
- }
- }
- }
- return true;
-}
-
-void QQuickGenericShaderEffect::propertyChanged(int mappedId)
-{
- const Shader type = Shader(mappedId >> 16);
- const int idx = mappedId & 0xFFFF;
- 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);
-
- 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)->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*)));
- }
-
- source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
- if (source) {
- // 'source' needs a window to get a scene graph node. It usually gets one through its
- // 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*)));
- }
-
- m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
- m_dirtyTextures[type].insert(idx);
-
- } else {
- m_dirty |= QSGShaderEffectNode::DirtyShaderConstant;
- m_dirtyConstants[type].insert(idx);
- }
-
- m_item->update();
-}
-
-void QQuickGenericShaderEffect::sourceDestroyed(QObject *object)
-{
- for (int shaderType = 0; shaderType < NShader; ++shaderType) {
- for (auto &vd : m_shaders[shaderType].varData) {
- if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
- if (qvariant_cast<QObject *>(vd.value) == object)
- vd.value = QVariant();
- }
- }
- }
-}
-
-void QQuickGenericShaderEffect::markGeometryDirtyAndUpdate()
-{
- m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
- m_item->update();
-}
-
-void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
-{
- if (m_supportsAtlasTextures)
- markGeometryDirtyAndUpdate();
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qquickgenericshadereffect_p.cpp"
diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h
deleted file mode 100644
index 076131f8e1..0000000000
--- a/src/quick/items/qquickgenericshadereffect_p.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QQUICKGENERICSHADEREFFECT_P_H
-#define QQUICKGENERICSHADEREFFECT_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtQuick/qquickitem.h>
-#include <private/qtquickglobal_p.h>
-#include <private/qsgadaptationlayer_p.h>
-#include "qquickshadereffect_p.h"
-#include "qquickshadereffectmesh_p.h"
-
-#include <functional>
-
-QT_BEGIN_NAMESPACE
-
-namespace QtPrivate {
-class EffectSlotMapper;
-}
-
-class Q_QUICK_PRIVATE_EXPORT QQuickGenericShaderEffect : public QObject
-{
- Q_OBJECT
-
-public:
- QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent = nullptr);
- ~QQuickGenericShaderEffect();
-
- QByteArray fragmentShader() const { return m_fragShader; }
- void setFragmentShader(const QByteArray &src);
-
- QByteArray vertexShader() const { return m_vertShader; }
- void setVertexShader(const QByteArray &src);
-
- 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();
-
-private slots:
- void propertyChanged(int mappedId);
- void sourceDestroyed(QObject *object);
- void markGeometryDirtyAndUpdate();
- void markGeometryDirtyAndUpdateIfSupportsAtlas();
- void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
- const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result);
-
-private:
- QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
-
- enum Shader {
- Vertex,
- Fragment,
-
- NShader
- };
- bool updateShader(Shader shaderType, const QByteArray &src);
- void updateShaderVars(Shader shaderType);
- void disconnectSignals(Shader shaderType);
- void clearMappers(Shader shaderType);
- bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) 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;
- QByteArray m_fragShader;
- bool m_fragNeedsUpdate;
- QByteArray 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];
-};
-
-QT_END_NAMESPACE
-
-#endif // QQUICKGENERICSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index 7a37410faa..c4c48fdea5 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -40,11 +40,14 @@
#include <private/qquickshadereffect_p.h>
#include <private/qsgcontextplugin_p.h>
#include <private/qquickitem_p.h>
-#include <private/qquickgenericshadereffect_p.h>
#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
#include <private/qsgrhisupport_p.h>
#endif
+#include <private/qquickwindow_p.h>
+#include <private/qquickitem_p.h>
+#include "qquickshadereffectmesh_p.h"
+
QT_BEGIN_NAMESPACE
/*!
@@ -284,6 +287,128 @@ QT_BEGIN_NAMESPACE
\sa {Item Layers}
*/
+
+namespace QtPrivate {
+class EffectSlotMapper: public QtPrivate::QSlotObjectBase
+{
+public:
+ typedef std::function<void()> PropChangedFunc;
+
+ explicit EffectSlotMapper(PropChangedFunc func)
+ : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
+ { ref(); }
+
+ void setSignalIndex(int idx) { _signalIndex = idx; }
+ int signalIndex() const { return _signalIndex; }
+
+private:
+ int _signalIndex;
+ PropChangedFunc func;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
+ {
+ auto thiz = static_cast<EffectSlotMapper*>(this_);
+ switch (which) {
+ case Destroy:
+ delete thiz;
+ break;
+ case Call:
+ thiz->func();
+ break;
+ case Compare:
+ *ret = thiz == reinterpret_cast<EffectSlotMapper *>(a[0]);
+ break;
+ case NumOperations: ;
+ }
+ }
+};
+} // namespace QtPrivate
+
+class QQuickShaderEffectImpl : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickShaderEffectImpl(QQuickShaderEffect *item);
+ ~QQuickShaderEffectImpl();
+
+ QByteArray fragmentShader() const { return m_fragShader; }
+ void setFragmentShader(const QByteArray &src);
+
+ QByteArray vertexShader() const { return m_vertShader; }
+ void setVertexShader(const QByteArray &src);
+
+ 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();
+
+private slots:
+ void propertyChanged(int mappedId);
+ void sourceDestroyed(QObject *object);
+ void markGeometryDirtyAndUpdate();
+ void markGeometryDirtyAndUpdateIfSupportsAtlas();
+ void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result);
+
+private:
+ QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
+
+ enum Shader {
+ Vertex,
+ Fragment,
+
+ NShader
+ };
+ bool updateShader(Shader shaderType, const QByteArray &src);
+ void updateShaderVars(Shader shaderType);
+ void disconnectSignals(Shader shaderType);
+ void clearMappers(Shader shaderType);
+ bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) 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;
+ QByteArray m_fragShader;
+ bool m_fragNeedsUpdate;
+ QByteArray 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)
@@ -300,7 +425,7 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
{
setFlag(QQuickItem::ItemHasContents);
- m_impl = new QQuickGenericShaderEffect(this, this);
+ m_impl = new QQuickShaderEffectImpl(this);
}
QQuickShaderEffect::~QQuickShaderEffect()
@@ -558,6 +683,657 @@ void QQuickShaderEffectPrivate::updatePolish()
q->m_impl->maybeUpdateShaders();
}
+QQuickShaderEffectImpl::QQuickShaderEffectImpl(QQuickShaderEffect *item)
+ : QObject(item)
+ , m_item(item)
+ , m_meshResolution(1, 1)
+ , m_mesh(nullptr)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_blending(true)
+ , m_supportsAtlasTextures(false)
+ , m_mgr(nullptr)
+ , m_fragNeedsUpdate(true)
+ , m_vertNeedsUpdate(true)
+{
+ qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
+ for (int i = 0; i < NShader; ++i)
+ m_inProgress[i] = nullptr;
+}
+
+QQuickShaderEffectImpl::~QQuickShaderEffectImpl()
+{
+ for (int i = 0; i < NShader; ++i) {
+ disconnectSignals(Shader(i));
+ clearMappers(Shader(i));
+ }
+
+ delete m_mgr;
+}
+
+void QQuickShaderEffectImpl::setFragmentShader(const QByteArray &src)
+{
+ // Compare the actual values since they are often just filenames.
+ // Optimizing by comparing constData() is a bad idea since seemingly static
+ // strings in QML may in fact have different addresses when a binding
+ // triggers assigning the "same" value to the property.
+ if (m_fragShader == src)
+ return;
+
+ m_fragShader = src;
+
+ m_fragNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickShaderEffectImpl::setVertexShader(const QByteArray &src)
+{
+ if (m_vertShader == src)
+ return;
+
+ m_vertShader = src;
+
+ m_vertNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ emit m_item->vertexShaderChanged();
+}
+
+void QQuickShaderEffectImpl::setBlending(bool enable)
+{
+ if (m_blending == enable)
+ return;
+
+ m_blending = enable;
+ m_item->update();
+ emit m_item->blendingChanged();
+}
+
+QVariant QQuickShaderEffectImpl::mesh() const
+{
+ return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
+ : QVariant::fromValue(m_meshResolution);
+}
+
+void QQuickShaderEffectImpl::setMesh(const QVariant &mesh)
+{
+ 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);
+
+ m_mesh = newMesh;
+
+ if (m_mesh) {
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(markGeometryDirtyAndUpdate()));
+ } else {
+ if (mesh.canConvert<QSize>()) {
+ m_meshResolution = mesh.toSize();
+ } else {
+ QList<QByteArray> res = mesh.toByteArray().split('x');
+ bool ok = res.size() == 2;
+ if (ok) {
+ int w = res.at(0).toInt(&ok);
+ if (ok) {
+ int h = res.at(1).toInt(&ok);
+ if (ok)
+ m_meshResolution = QSize(w, h);
+ }
+ }
+ if (!ok)
+ qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
+ }
+ m_defaultMesh.setResolution(m_meshResolution);
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
+ m_item->update();
+
+ emit m_item->meshChanged();
+}
+
+void QQuickShaderEffectImpl::setCullMode(QQuickShaderEffect::CullMode face)
+{
+ if (m_cullMode == face)
+ return;
+
+ m_cullMode = face;
+ m_item->update();
+ emit m_item->cullModeChanged();
+}
+
+void QQuickShaderEffectImpl::setSupportsAtlasTextures(bool supports)
+{
+ if (m_supportsAtlasTextures == supports)
+ return;
+
+ m_supportsAtlasTextures = supports;
+ markGeometryDirtyAndUpdate();
+ emit m_item->supportsAtlasTexturesChanged();
+}
+
+QString QQuickShaderEffectImpl::parseLog()
+{
+ maybeUpdateShaders();
+ return log();
+}
+
+QString QQuickShaderEffectImpl::log() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QString();
+
+ return mgr->log();
+}
+
+QQuickShaderEffect::Status QQuickShaderEffectImpl::status() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::Uncompiled;
+
+ return QQuickShaderEffect::Status(mgr->status());
+}
+
+void QQuickShaderEffectImpl::handleEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
+ 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 == e->propertyName()) {
+ propertyChanged((shaderType << 16) | idx);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void QQuickShaderEffectImpl::handleGeometryChanged(const QRectF &, const QRectF &)
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+}
+
+QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
+
+ if (m_item->width() <= 0 || m_item->height() <= 0) {
+ delete node;
+ return nullptr;
+ }
+
+ // Do not change anything while a new shader is being reflected or compiled.
+ if (m_inProgress[Vertex] || m_inProgress[Fragment])
+ return node;
+
+ // The manager should be already created on the gui thread. Just take that instance.
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr) {
+ delete node;
+ return nullptr;
+ }
+
+ if (!node) {
+ QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->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);
+ }
+
+ QSGShaderEffectNode::SyncData sd;
+ sd.dirty = m_dirty;
+ sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
+ sd.blending = m_blending;
+ sd.vertex.shader = &m_shaders[Vertex];
+ sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
+ sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
+ sd.fragment.shader = &m_shaders[Fragment];
+ sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
+ sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
+ node->syncMaterial(&sd);
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
+ node->setGeometry(nullptr);
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
+ const QRectF rect(0, 0, m_item->width(), m_item->height());
+ QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+ QSGGeometry *geometry = node->geometry();
+
+ const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
+ geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
+
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ m_dirty = {};
+ for (int i = 0; i < NShader; ++i) {
+ m_dirtyConstants[i].clear();
+ m_dirtyTextures[i].clear();
+ }
+
+ return node;
+}
+
+void QQuickShaderEffectImpl::maybeUpdateShaders()
+{
+ if (m_vertNeedsUpdate)
+ m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
+ if (m_fragNeedsUpdate)
+ m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
+ if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
+ // This function is invoked either from componentComplete or in a
+ // response to a previous invocation's polish() request. If this is
+ // case #1 then updateShader can fail due to not having a window or
+ // 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();
+ }
+}
+
+void QQuickShaderEffectImpl::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ // Move the window ref.
+ if (change == QQuickItem::ItemSceneChange) {
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (value.window)
+ QQuickItemPrivate::get(source)->refWindow(value.window);
+ else
+ QQuickItemPrivate::get(source)->derefWindow();
+ }
+ }
+ }
+ }
+ }
+}
+
+QSGGuiThreadShaderEffectManager *QQuickShaderEffectImpl::shaderEffectManager() const
+{
+ if (!m_mgr) {
+ // return null if this is not the gui thread and not already created
+ if (QThread::currentThread() != m_item->thread())
+ return m_mgr;
+ QQuickWindow *w = m_item->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);
+ }
+ }
+ }
+
+ return m_mgr;
+}
+
+void QQuickShaderEffectImpl::disconnectSignals(Shader shaderType)
+{
+ for (auto *mapper : m_mappers[shaderType]) {
+ void *a = mapper;
+ if (mapper)
+ QObjectPrivate::disconnect(m_item, mapper->signalIndex(), &a);
+ }
+ for (const auto &vd : qAsConst(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())
+ QQuickItemPrivate::get(source)->derefWindow();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+void QQuickShaderEffectImpl::clearMappers(QQuickShaderEffectImpl::Shader shaderType)
+{
+ for (auto *mapper : qAsConst(m_mappers[shaderType])) {
+ if (mapper)
+ mapper->destroyIfLastRef();
+ }
+ m_mappers[shaderType].clear();
+}
+
+static inline QVariant getValueFromProperty(QObject *item, const QMetaObject *itemMetaObject,
+ const QByteArray &name, int propertyIndex)
+{
+ QVariant value;
+ if (propertyIndex == -1) {
+ value = item->property(name);
+ } else {
+ value = itemMetaObject->property(propertyIndex).read(item);
+ }
+ return value;
+}
+
+struct ShaderInfoCache
+{
+ bool contains(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.contains(key);
+ }
+
+ QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.value(key);
+ }
+
+ void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
+ {
+ m_shaderInfoCache.insert(key, value);
+ }
+
+ QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
+};
+
+Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
+
+bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QByteArray &src)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return false;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ disconnectSignals(shaderType);
+
+ m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
+ m_shaders[shaderType].varData.clear();
+
+ if (!src.isEmpty()) {
+ if (shaderInfoCache()->contains(src)) {
+ m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src);
+ m_shaders[shaderType].hasShaderCode = true;
+ } else {
+ // Each prepareShaderCode call needs its own work area, hence the
+ // dynamic alloc. If there are calls in progress, let those run to
+ // finish, their results can then simply be ignored because
+ // m_inProgress indicates what we care about.
+ m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint =
+ shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+ // Figure out what input parameters and variables are used in the
+ // shader. For file-based shader source/bytecode this is where the data
+ // is pulled in from the file. Some backends may choose to do
+ // source->bytecode compilation as well in this step.
+ mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]);
+ // the rest is handled in shaderCodePrepared()
+ return true;
+ }
+ } else {
+ m_shaders[shaderType].hasShaderCode = false;
+ if (shaderType == Fragment) {
+ // With built-in shaders hasShaderCode is set to false and all
+ // metadata is empty, as it is left up to the node to provide a
+ // built-in default shader and its metadata. However, in case of
+ // the built-in fragment shader the value for 'source' has to be
+ // provided and monitored like with an application-provided shader.
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 0; // fake
+ v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ m_shaders[shaderType].shaderInfo.variables.append(v);
+ }
+ }
+
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+ return true;
+}
+
+void QQuickShaderEffectImpl::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
+{
+ const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
+
+ // If another call was made to updateShader() for the same shader type in
+ // the meantime then our results are useless, just drop them.
+ if (result != m_inProgress[shaderType]) {
+ delete result;
+ return;
+ }
+
+ m_shaders[shaderType].shaderInfo = *result;
+ delete result;
+ m_inProgress[shaderType] = nullptr;
+
+ if (!ok) {
+ qWarning("ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log()));
+ m_shaders[shaderType].hasShaderCode = false;
+ return;
+ }
+
+ m_shaders[shaderType].hasShaderCode = true;
+ shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo);
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+}
+
+void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
+ 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);
+
+ auto *engine = qmlEngine(m_item);
+ QQmlPropertyCache *propCache = engine ? QQmlData::ensurePropertyCache(engine, m_item) : nullptr;
+
+ if (!m_itemMetaObject)
+ m_itemMetaObject = m_item->metaObject();
+
+ // Hook up the signals to get notified about changes for properties that
+ // correspond to variables in the shader. Store also the values.
+ for (int i = 0; i < varCount; ++i) {
+ const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
+ QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
+ const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
+ if (isSpecial) {
+ if (v.name == "qt_Opacity")
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ else if (v.name == "qt_Matrix")
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ else if (v.name.startsWith("qt_SubRect_"))
+ vd.specialType = QSGShaderEffectNode::VariableData::SubRect;
+ continue;
+ }
+
+ // The value of a property corresponding to a sampler is the source
+ // item ref, unless there are separate texture objects in which case
+ // the sampler is ignored (here).
+ if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ if (texturesSeparate) {
+ vd.specialType = QSGShaderEffectNode::VariableData::Unused;
+ continue;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ }
+ } else if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
+ Q_ASSERT(texturesSeparate);
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::None;
+ }
+
+ // Find the property on the ShaderEffect item.
+ int propIdx = -1;
+ QQmlPropertyData *pd = nullptr;
+ if (propCache) {
+ pd = propCache->property(QLatin1String(v.name), nullptr, nullptr);
+ if (pd) {
+ if (!pd->isFunction())
+ propIdx = pd->coreIndex();
+ }
+ }
+ if (propIdx >= 0) {
+ if (pd && !pd->isFunction()) {
+ 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 = i | (shaderType << 16);
+ mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
+ this->propertyChanged(mappedId);
+ });
+ }
+ 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,
+ 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;
+ }
+ }
+ } else {
+ // Do not warn for dynamic properties.
+ if (!m_item->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);
+ 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*)));
+ }
+ }
+ }
+}
+
+bool QQuickShaderEffectImpl::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) 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;
+ }
+ }
+ }
+ return true;
+}
+
+void QQuickShaderEffectImpl::propertyChanged(int mappedId)
+{
+ const Shader type = Shader(mappedId >> 16);
+ const int idx = mappedId & 0xFFFF;
+ 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);
+
+ 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)->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*)));
+ }
+
+ source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ // 'source' needs a window to get a scene graph node. It usually gets one through its
+ // 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*)));
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
+ m_dirtyTextures[type].insert(idx);
+
+ } else {
+ m_dirty |= QSGShaderEffectNode::DirtyShaderConstant;
+ m_dirtyConstants[type].insert(idx);
+ }
+
+ m_item->update();
+}
+
+void QQuickShaderEffectImpl::sourceDestroyed(QObject *object)
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (auto &vd : m_shaders[shaderType].varData) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
+ if (qvariant_cast<QObject *>(vd.value) == object)
+ vd.value = QVariant();
+ }
+ }
+ }
+}
+
+void QQuickShaderEffectImpl::markGeometryDirtyAndUpdate()
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ m_item->update();
+}
+
+void QQuickShaderEffectImpl::markGeometryDirtyAndUpdateIfSupportsAtlas()
+{
+ if (m_supportsAtlasTextures)
+ markGeometryDirtyAndUpdate();
+}
+
+
+
QT_END_NAMESPACE
#include "moc_qquickshadereffect_p.cpp"
+#include "qquickshadereffect.moc"
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index 504a1efcf1..b895353272 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -60,7 +60,7 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
-class QQuickGenericShaderEffect;
+class QQuickShaderEffectImpl;
class QQuickShaderEffectPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
@@ -137,7 +137,7 @@ protected:
void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
- QQuickGenericShaderEffect *m_impl;
+ QQuickShaderEffectImpl *m_impl;
Q_DECLARE_PRIVATE(QQuickShaderEffect)
};