aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)
};