aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickgenericshadereffect.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickgenericshadereffect.cpp')
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp165
1 files changed, 107 insertions, 58 deletions
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index 47272a2eac..2f8d71fc11 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -59,9 +59,13 @@ QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, Q
, m_blending(true)
, m_supportsAtlasTextures(false)
, m_mgr(nullptr)
+ , m_fragNeedsUpdate(true)
+ , m_vertNeedsUpdate(true)
, m_dirty(0)
{
- connect(m_item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(itemWindowChanged(QQuickWindow*)));
+ qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
+ for (int i = 0; i < NShader; ++i)
+ m_inProgress[i] = nullptr;
}
QQuickGenericShaderEffect::~QQuickGenericShaderEffect()
@@ -85,12 +89,11 @@ void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src)
return;
m_fragShader = src;
- m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_fragNeedsUpdate = true;
if (m_item->isComponentComplete())
- updateShader(Fragment, src);
+ maybeUpdateShaders();
- m_item->update();
emit m_item->fragmentShaderChanged();
}
@@ -100,12 +103,11 @@ void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
return;
m_vertShader = src;
- m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_vertNeedsUpdate = true;
if (m_item->isComponentComplete())
- updateShader(Vertex, src);
+ maybeUpdateShaders();
- m_item->update();
emit m_item->vertexShaderChanged();
}
@@ -184,6 +186,12 @@ void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports)
emit m_item->supportsAtlasTexturesChanged();
}
+QString QQuickGenericShaderEffect::parseLog()
+{
+ maybeUpdateShaders();
+ return log();
+}
+
QString QQuickGenericShaderEffect::log() const
{
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
@@ -232,6 +240,10 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui
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) {
@@ -287,10 +299,22 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui
return node;
}
-void QQuickGenericShaderEffect::handleComponentComplete()
+void QQuickGenericShaderEffect::maybeUpdateShaders()
{
- updateShader(Vertex, m_vertShader);
- updateShader(Fragment, m_fragShader);
+ 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)
@@ -319,43 +343,21 @@ QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager(
// return null if this is not the gui thread and not already created
if (QThread::currentThread() != m_item->thread())
return m_mgr;
- // need a window and a rendercontext (i.e. the scenegraph backend is ready)
QQuickWindow *w = m_item->window();
- if (w && w->isSceneGraphInitialized()) {
+ 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, SIGNAL(textureChanged()), this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas()));
+ connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickGenericShaderEffect::shaderCodePrepared);
}
- } else if (!w) {
- // Wait until itemWindowChanged() gets called. Return null for now.
- } else {
- // Have window, but no scenegraph -> ensure the signal is connected. Return null for now.
- const_cast<QQuickGenericShaderEffect *>(this)->itemWindowChanged(w);
}
}
return m_mgr;
}
-void QQuickGenericShaderEffect::itemWindowChanged(QQuickWindow *w)
-{
- if (w) {
- if (w->isSceneGraphInitialized())
- backendChanged();
- else
- connect(w, SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged()), Qt::UniqueConnection);
- }
-}
-
-void QQuickGenericShaderEffect::backendChanged()
-{
- disconnect(m_item->window(), SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged()));
- emit m_item->logChanged();
- emit m_item->statusChanged();
-}
-
void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
{
for (auto &sm : m_signalMappers[shaderType]) {
@@ -377,33 +379,33 @@ void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
}
}
-struct ReflectCache
+struct ShaderInfoCache
{
bool contains(const QByteArray &key) const
{
- return m_reflectCache.contains(key);
+ return m_shaderInfoCache.contains(key);
}
QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
{
- return m_reflectCache.value(key);
+ return m_shaderInfoCache.value(key);
}
void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
{
- m_reflectCache.insert(key, value);
+ m_shaderInfoCache.insert(key, value);
}
- QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_reflectCache;
+ QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
};
-Q_GLOBAL_STATIC(ReflectCache, reflectCache)
+Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
-void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
+bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
{
QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
if (!mgr)
- return;
+ return false;
const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
@@ -413,22 +415,26 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray
m_shaders[shaderType].varData.clear();
if (!src.isEmpty()) {
- // 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.
- if (reflectCache()->contains(src)) {
- m_shaders[shaderType].shaderInfo = reflectCache()->value(src);
+ if (shaderInfoCache()->contains(src)) {
+ m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src);
+ m_shaders[shaderType].hasShaderCode = true;
} else {
- QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo;
- if (!mgr->reflect(src, &shaderInfo)) {
- qWarning("ShaderEffect: shader reflection failed for %s", src.constData());
- m_shaders[shaderType].hasShaderCode = false;
- return;
- }
- m_shaders[shaderType].shaderInfo = shaderInfo;
- reflectCache()->insert(src, shaderInfo);
+ // 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;
}
- m_shaders[shaderType].hasShaderCode = true;
} else {
m_shaders[shaderType].hasShaderCode = false;
if (shaderType == Fragment) {
@@ -446,6 +452,49 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray
}
}
+ 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);
@@ -461,9 +510,9 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray
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 == QByteArrayLiteral("qt_Opacity"))
+ if (v.name == "qt_Opacity")
vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
- else if (v.name == QByteArrayLiteral("qt_Matrix"))
+ else if (v.name == "qt_Matrix")
vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
else if (v.name.startsWith("qt_SubRect_"))
vd.specialType = QSGShaderEffectNode::VariableData::SubRect;