1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsgrhishadereffectnode_p.h"
41 #include "qsgdefaultrendercontext_p.h"
42 #include "qsgrhisupport_p.h"
43 #include <qsgmaterialrhishader.h>
44 #include <qsgtextureprovider.h>
45 #include <private/qsgplaintexture_p.h>
46 #include <QtGui/private/qshaderdescription_p.h>
47 #include <QQmlFile>
48 #include <QFile>
49 #include <QFileSelector>
50 
51 QT_BEGIN_NAMESPACE
52 
reset(const QShader & vs,const QShader & fs)53 void QSGRhiShaderLinker::reset(const QShader &vs, const QShader &fs)
54 {
55     Q_ASSERT(vs.isValid() && fs.isValid());
56     m_vs = vs;
57     m_fs = fs;
58 
59     m_error = false;
60 
61     m_constantBufferSize = 0;
62     m_constants.clear();
63     m_samplers.clear();
64     m_samplerNameMap.clear();
65 }
66 
feedConstants(const QSGShaderEffectNode::ShaderData & shader,const QSet<int> * dirtyIndices)67 void QSGRhiShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
68 {
69     Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count());
70     if (!dirtyIndices) {
71         m_constantBufferSize = qMax(m_constantBufferSize, shader.shaderInfo.constantDataSize);
72         for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
73             const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
74             if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) {
75                 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
76                 Constant c;
77                 c.size = var.size;
78                 c.specialType = vd.specialType;
79                 if (c.specialType != QSGShaderEffectNode::VariableData::SubRect) {
80                     c.value = vd.value;
81                     if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
82                         if (c.specialType == QSGShaderEffectNode::VariableData::None) {
83                             qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
84                                      << "offset" << var.offset << "value" << c.value;
85                         } else {
86                             qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
87                                      << "offset" << var.offset << "special" << c.specialType;
88                         }
89                     }
90                 } else {
91                     Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
92                     c.value = var.name.mid(11);
93                 }
94                 m_constants[var.offset] = c;
95             }
96         }
97     } else {
98         for (int idx : *dirtyIndices) {
99             const int offset = shader.shaderInfo.variables.at(idx).offset;
100             const QVariant value = shader.varData.at(idx).value;
101             m_constants[offset].value = value;
102             if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
103                 qDebug() << "cbuf update" << shader.shaderInfo.name
104                          << "offset" << offset << "value" << value;
105             }
106         }
107     }
108 }
109 
feedSamplers(const QSGShaderEffectNode::ShaderData & shader,const QSet<int> * dirtyIndices)110 void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
111 {
112     if (!dirtyIndices) {
113         for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
114             const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
115             const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
116             if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
117                 Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
118                 m_samplers.insert(var.bindPoint, vd.value);
119                 m_samplerNameMap.insert(var.name, var.bindPoint);
120             }
121         }
122     } else {
123         for (int idx : *dirtyIndices) {
124             const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
125             const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
126             m_samplers.insert(var.bindPoint, vd.value);
127             m_samplerNameMap.insert(var.name, var.bindPoint);
128         }
129     }
130 }
131 
linkTextureSubRects()132 void QSGRhiShaderLinker::linkTextureSubRects()
133 {
134     // feedConstants stores <name> in Constant::value for subrect entries. Now
135     // that both constants and textures are known, replace the name with the
136     // texture binding point.
137     for (Constant &c : m_constants) {
138         if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
139             if (c.value.userType() == QMetaType::QByteArray) {
140                 const QByteArray name = c.value.toByteArray();
141                 if (!m_samplerNameMap.contains(name))
142                     qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", name.constData());
143                 c.value = m_samplerNameMap[name];
144             }
145         }
146     }
147 }
148 
dump()149 void QSGRhiShaderLinker::dump()
150 {
151     if (m_error) {
152         qDebug() << "Failed to generate program data";
153         return;
154     }
155     qDebug() << "Combined shader data" << m_vs << m_fs << "cbuffer size" << m_constantBufferSize;
156     qDebug() << " - constants" << m_constants;
157     qDebug() << " - samplers" << m_samplers;
158 }
159 
operator <<(QDebug debug,const QSGRhiShaderLinker::Constant & c)160 QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c)
161 {
162     QDebugStateSaver saver(debug);
163     debug.space();
164     debug << "size" << c.size;
165     if (c.specialType != QSGShaderEffectNode::VariableData::None)
166         debug << "special" << c.specialType;
167     else
168         debug << "value" << c.value;
169     return debug;
170 }
171 
172 struct QSGRhiShaderMaterialTypeCache
173 {
174     QSGMaterialType *get(const QShader &vs, const QShader &fs);
resetQSGRhiShaderMaterialTypeCache175     void reset() { qDeleteAll(m_types); m_types.clear(); }
176 
177     struct Key {
178         QShader blob[2];
KeyQSGRhiShaderMaterialTypeCache::Key179         Key() { }
KeyQSGRhiShaderMaterialTypeCache::Key180         Key(const QShader &vs, const QShader &fs) { blob[0] = vs; blob[1] = fs; }
operator ==QSGRhiShaderMaterialTypeCache::Key181         bool operator==(const Key &other) const {
182             return blob[0] == other.blob[0] && blob[1] == other.blob[1];
183         }
184     };
185     QHash<Key, QSGMaterialType *> m_types;
186 };
187 
qHash(const QSGRhiShaderMaterialTypeCache::Key & key,uint seed=0)188 uint qHash(const QSGRhiShaderMaterialTypeCache::Key &key, uint seed = 0)
189 {
190     uint hash = seed;
191     for (int i = 0; i < 2; ++i)
192         hash = hash * 31337 + qHash(key.blob[i]);
193     return hash;
194 }
195 
get(const QShader & vs,const QShader & fs)196 QSGMaterialType *QSGRhiShaderMaterialTypeCache::get(const QShader &vs, const QShader &fs)
197 {
198     const Key k(vs, fs);
199     if (m_types.contains(k))
200         return m_types.value(k);
201 
202     QSGMaterialType *t = new QSGMaterialType;
203     m_types.insert(k, t);
204     return t;
205 }
206 
207 static QSGRhiShaderMaterialTypeCache shaderMaterialTypeCache;
208 
209 class QSGRhiShaderEffectMaterialShader : public QSGMaterialRhiShader
210 {
211 public:
212     QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material);
213 
214     bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
215     void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
216     bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
217 };
218 
QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial * material)219 QSGRhiShaderEffectMaterialShader::QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material)
220 {
221     setFlag(UpdatesGraphicsPipelineState, true);
222     setShader(VertexStage, material->m_vertexShader);
223     setShader(FragmentStage, material->m_fragmentShader);
224 }
225 
qsg_premultiply_color(const QColor & c)226 static inline QColor qsg_premultiply_color(const QColor &c)
227 {
228     return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
229 }
230 
updateUniformData(RenderState & state,QSGMaterial * newMaterial,QSGMaterial * oldMaterial)231 bool QSGRhiShaderEffectMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
232 {
233     Q_UNUSED(oldMaterial);
234     QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
235 
236     bool changed = false;
237     QByteArray *buf = state.uniformData();
238 
239     for (auto it = mat->m_linker.m_constants.constBegin(), itEnd = mat->m_linker.m_constants.constEnd(); it != itEnd; ++it) {
240         const int offset = it.key();
241         char *dst = buf->data() + offset;
242         const QSGRhiShaderLinker::Constant &c(it.value());
243         if (c.specialType == QSGShaderEffectNode::VariableData::Opacity) {
244             if (state.isOpacityDirty()) {
245                 const float f = state.opacity();
246                 Q_ASSERT(sizeof(f) == c.size);
247                 memcpy(dst, &f, sizeof(f));
248                 changed = true;
249             }
250         } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
251             if (state.isMatrixDirty()) {
252                 const int sz = 16 * sizeof(float);
253                 Q_ASSERT(sz == c.size);
254                 memcpy(dst, state.combinedMatrix().constData(), sz);
255                 changed = true;
256             }
257         } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
258             // vec4
259             QRectF subRect(0, 0, 1, 1);
260             const int binding = c.value.toInt(); // filled in by linkTextureSubRects
261             if (binding < QSGRhiShaderEffectMaterial::MAX_BINDINGS) {
262                 if (QSGTextureProvider *tp = mat->m_textureProviders.at(binding)) {
263                     if (QSGTexture *t = tp->texture())
264                         subRect = t->normalizedTextureSubRect();
265                 }
266             }
267             const float f[4] = { float(subRect.x()), float(subRect.y()),
268                                  float(subRect.width()), float(subRect.height()) };
269             Q_ASSERT(sizeof(f) == c.size);
270             memcpy(dst, f, sizeof(f));
271         } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
272             changed = true;
273             switch (int(c.value.userType())) {
274             case QMetaType::QColor: {
275                 const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value));
276                 const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
277                 Q_ASSERT(sizeof(f) == c.size);
278                 memcpy(dst, f, sizeof(f));
279                 break;
280             }
281             case QMetaType::Float: {
282                 const float f = qvariant_cast<float>(c.value);
283                 Q_ASSERT(sizeof(f) == c.size);
284                 memcpy(dst, &f, sizeof(f));
285                 break;
286             }
287             case QMetaType::Double: {
288                 const float f = float(qvariant_cast<double>(c.value));
289                 Q_ASSERT(sizeof(f) == c.size);
290                 memcpy(dst, &f, sizeof(f));
291                 break;
292             }
293             case QMetaType::Int: {
294                 const int i = c.value.toInt();
295                 Q_ASSERT(sizeof(i) == c.size);
296                 memcpy(dst, &i, sizeof(i));
297                 break;
298             }
299             case QMetaType::Bool: {
300                 const bool b = c.value.toBool();
301                 Q_ASSERT(sizeof(b) == c.size);
302                 memcpy(dst, &b, sizeof(b));
303                 break;
304             }
305             case QMetaType::QTransform: { // mat3
306                 const QTransform v = qvariant_cast<QTransform>(c.value);
307                 const float m[3][3] = {
308                     { float(v.m11()), float(v.m12()), float(v.m13()) },
309                     { float(v.m21()), float(v.m22()), float(v.m23()) },
310                     { float(v.m31()), float(v.m32()), float(v.m33()) }
311                 };
312                 Q_ASSERT(sizeof(m) == c.size);
313                 memcpy(dst, m[0], sizeof(m));
314                 break;
315             }
316             case QMetaType::QSize:
317             case QMetaType::QSizeF: { // vec2
318                 const QSizeF v = c.value.toSizeF();
319                 const float f[2] = { float(v.width()), float(v.height()) };
320                 Q_ASSERT(sizeof(f) == c.size);
321                 memcpy(dst, f, sizeof(f));
322                 break;
323             }
324             case QMetaType::QPoint:
325             case QMetaType::QPointF: { // vec2
326                 const QPointF v = c.value.toPointF();
327                 const float f[2] = { float(v.x()), float(v.y()) };
328                 Q_ASSERT(sizeof(f) == c.size);
329                 memcpy(dst, f, sizeof(f));
330                 break;
331             }
332             case QMetaType::QRect:
333             case QMetaType::QRectF: { // vec4
334                 const QRectF v = c.value.toRectF();
335                 const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
336                 Q_ASSERT(sizeof(f) == c.size);
337                 memcpy(dst, f, sizeof(f));
338                 break;
339             }
340             case QMetaType::QVector2D: { // vec2
341                 const QVector2D v = qvariant_cast<QVector2D>(c.value);
342                 const float f[2] = { float(v.x()), float(v.y()) };
343                 Q_ASSERT(sizeof(f) == c.size);
344                 memcpy(dst, f, sizeof(f));
345                 break;
346             }
347             case QMetaType::QVector3D: { // vec3
348                 const QVector3D v = qvariant_cast<QVector3D>(c.value);
349                 const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
350                 Q_ASSERT(sizeof(f) == c.size);
351                 memcpy(dst, f, sizeof(f));
352                 break;
353             }
354             case QMetaType::QVector4D: { // vec4
355                 const QVector4D v = qvariant_cast<QVector4D>(c.value);
356                 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
357                 Q_ASSERT(sizeof(f) == c.size);
358                 memcpy(dst, f, sizeof(f));
359                 break;
360             }
361             case QMetaType::QQuaternion: { // vec4
362                 const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
363                 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
364                 Q_ASSERT(sizeof(f) == c.size);
365                 memcpy(dst, f, sizeof(f));
366                 break;
367             }
368             case QMetaType::QMatrix4x4: { // mat4
369                 const QMatrix4x4 v = qvariant_cast<QMatrix4x4>(c.value);
370                 const int sz = 16 * sizeof(float);
371                 Q_ASSERT(sz == c.size);
372                 memcpy(dst, v.constData(), sz);
373                 break;
374             }
375             default:
376                 break;
377             }
378         }
379     }
380 
381     return changed;
382 }
383 
updateSampledImage(RenderState & state,int binding,QSGTexture ** texture,QSGMaterial * newMaterial,QSGMaterial * oldMaterial)384 void QSGRhiShaderEffectMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
385                                                           QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
386 {
387     Q_UNUSED(oldMaterial);
388     QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
389 
390     if (binding >= QSGRhiShaderEffectMaterial::MAX_BINDINGS)
391         return;
392 
393     QSGTextureProvider *tp = mat->m_textureProviders.at(binding);
394     if (tp) {
395         if (QSGTexture *t = tp->texture()) {
396             t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
397             if (t->isAtlasTexture() && !mat->m_geometryUsesTextureSubRect) {
398                 // Why the hassle with the batch: while removedFromAtlas() is
399                 // able to operate with its own resource update batch (which is
400                 // then committed immediately), that approach is wrong when the
401                 // atlas enqueued (in the updateRhiTexture() above) not yet
402                 // committed operations to state.resourceUpdateBatch()... The
403                 // only safe way then is to use the same batch the atlas'
404                 // updateRhiTexture() used.
405                 t->setWorkResourceUpdateBatch(state.resourceUpdateBatch());
406                 QSGTexture *newTexture = t->removedFromAtlas();
407                 t->setWorkResourceUpdateBatch(nullptr);
408                 if (newTexture)
409                     t = newTexture;
410             }
411             *texture = t;
412             return;
413         }
414     }
415 
416     if (!mat->m_dummyTexture) {
417         mat->m_dummyTexture = new QSGPlainTexture;
418         mat->m_dummyTexture->setFiltering(QSGTexture::Nearest);
419         mat->m_dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
420         mat->m_dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
421         QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
422         img.fill(0);
423         mat->m_dummyTexture->setImage(img);
424         mat->m_dummyTexture->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
425     }
426     *texture = mat->m_dummyTexture;
427 }
428 
updateGraphicsPipelineState(RenderState & state,GraphicsPipelineState * ps,QSGMaterial * newMaterial,QSGMaterial * oldMaterial)429 bool QSGRhiShaderEffectMaterialShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
430                                                                    QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
431 {
432     Q_UNUSED(state);
433     Q_UNUSED(oldMaterial);
434     QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
435 
436     switch (mat->m_cullMode) {
437     case QSGShaderEffectNode::FrontFaceCulling:
438         ps->cullMode = GraphicsPipelineState::CullFront;
439         return true;
440     case QSGShaderEffectNode::BackFaceCulling:
441         ps->cullMode = GraphicsPipelineState::CullBack;
442         return true;
443     default:
444         return false;
445     }
446 }
447 
QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode * node)448 QSGRhiShaderEffectMaterial::QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node)
449     : m_node(node)
450 {
451     setFlag(SupportsRhiShader | Blending | RequiresFullMatrix, true); // may be changed in syncMaterial()
452 }
453 
~QSGRhiShaderEffectMaterial()454 QSGRhiShaderEffectMaterial::~QSGRhiShaderEffectMaterial()
455 {
456     delete m_dummyTexture;
457 }
458 
hasAtlasTexture(const QVector<QSGTextureProvider * > & textureProviders)459 static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
460 {
461     for (QSGTextureProvider *tp : textureProviders) {
462         if (tp && tp->texture() && tp->texture()->isAtlasTexture())
463             return true;
464     }
465     return false;
466 }
467 
compare(const QSGMaterial * other) const468 int QSGRhiShaderEffectMaterial::compare(const QSGMaterial *other) const
469 {
470     Q_ASSERT(other && type() == other->type());
471     const QSGRhiShaderEffectMaterial *o = static_cast<const QSGRhiShaderEffectMaterial *>(other);
472 
473     if (int diff = m_cullMode - o->m_cullMode)
474         return diff;
475 
476     if (int diff = m_textureProviders.count() - o->m_textureProviders.count())
477         return diff;
478 
479     if (m_linker.m_constants != o->m_linker.m_constants)
480         return 1;
481 
482     if (hasAtlasTexture(m_textureProviders) && !m_geometryUsesTextureSubRect)
483         return -1;
484 
485     if (hasAtlasTexture(o->m_textureProviders) && !o->m_geometryUsesTextureSubRect)
486         return 1;
487 
488     for (int binding = 0, count = m_textureProviders.count(); binding != count; ++binding) {
489         QSGTextureProvider *tp1 = m_textureProviders.at(binding);
490         QSGTextureProvider *tp2 = o->m_textureProviders.at(binding);
491         if (tp1 && tp2) {
492             QSGTexture *t1 = tp1->texture();
493             QSGTexture *t2 = tp2->texture();
494             if (t1 && t2) {
495                 if (int diff = t1->comparisonKey() - t2->comparisonKey())
496                     return diff;
497             } else {
498                 if (!t1 && t2)
499                     return -1;
500                 if (t1 && !t2)
501                     return 1;
502             }
503         } else {
504             if (!tp1 && tp2)
505                 return -1;
506             if (tp1 && !tp2)
507                 return 1;
508         }
509     }
510 
511     return 0;
512 }
513 
type() const514 QSGMaterialType *QSGRhiShaderEffectMaterial::type() const
515 {
516     return m_materialType;
517 }
518 
createShader() const519 QSGMaterialShader *QSGRhiShaderEffectMaterial::createShader() const
520 {
521     Q_ASSERT(flags().testFlag(RhiShaderWanted));
522     return new QSGRhiShaderEffectMaterialShader(this);
523 }
524 
updateTextureProviders(bool layoutChange)525 void QSGRhiShaderEffectMaterial::updateTextureProviders(bool layoutChange)
526 {
527     if (layoutChange) {
528         for (QSGTextureProvider *tp : m_textureProviders) {
529             if (tp) {
530                 QObject::disconnect(tp, SIGNAL(textureChanged()), m_node,
531                                     SLOT(handleTextureChange()));
532                 QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), m_node,
533                                     SLOT(handleTextureProviderDestroyed(QObject*)));
534             }
535         }
536         m_textureProviders.fill(nullptr, MAX_BINDINGS);
537     }
538 
539     for (auto it = m_linker.m_samplers.constBegin(), itEnd = m_linker.m_samplers.constEnd(); it != itEnd; ++it) {
540         const int binding = it.key();
541         QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
542         QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
543         if (binding >= MAX_BINDINGS) {
544             qWarning("Sampler at binding %d exceeds the available ShaderEffect binding slots; ignored",
545                      binding);
546             continue;
547         }
548         QSGTextureProvider *&activeProvider(m_textureProviders[binding]);
549         if (newProvider != activeProvider) {
550             if (activeProvider) {
551                 QObject::disconnect(activeProvider, SIGNAL(textureChanged()), m_node,
552                                     SLOT(handleTextureChange()));
553                 QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), m_node,
554                                     SLOT(handleTextureProviderDestroyed(QObject*)));
555             }
556             if (newProvider) {
557                 Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
558                            "QSGRhiShaderEffectMaterial::updateTextureProviders",
559                            "Texture provider must belong to the rendering thread");
560                 QObject::connect(newProvider, SIGNAL(textureChanged()), m_node, SLOT(handleTextureChange()));
561                 QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), m_node,
562                                  SLOT(handleTextureProviderDestroyed(QObject*)));
563             } else {
564                 const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
565                 qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
566                          binding, typeName);
567             }
568             activeProvider = newProvider;
569         }
570     }
571 }
572 
QSGRhiShaderEffectNode(QSGDefaultRenderContext * rc,QSGRhiGuiThreadShaderEffectManager * mgr)573 QSGRhiShaderEffectNode::QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr)
574     : QSGShaderEffectNode(mgr),
575       m_rc(rc),
576       m_mgr(mgr),
577       m_material(this)
578 {
579     setFlag(UsePreprocess, true);
580     setMaterial(&m_material);
581 }
582 
updateNormalizedTextureSubRect(bool supportsAtlasTextures)583 QRectF QSGRhiShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtlasTextures)
584 {
585     QRectF srcRect(0, 0, 1, 1);
586     bool geometryUsesTextureSubRect = false;
587     if (supportsAtlasTextures) {
588         QSGTextureProvider *tp = nullptr;
589         for (int binding = 0, count = m_material.m_textureProviders.count(); binding != count; ++binding) {
590             if (QSGTextureProvider *candidate = m_material.m_textureProviders.at(binding)) {
591                 if (!tp) {
592                     tp = candidate;
593                 } else { // there can only be one...
594                     tp = nullptr;
595                     break;
596                 }
597             }
598         }
599         if (tp && tp->texture()) {
600             srcRect = tp->texture()->normalizedTextureSubRect();
601             geometryUsesTextureSubRect = true;
602         }
603     }
604 
605     if (m_material.m_geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
606         m_material.m_geometryUsesTextureSubRect = geometryUsesTextureSubRect;
607         markDirty(QSGNode::DirtyMaterial);
608     }
609 
610     return srcRect;
611 }
612 
loadShader(const QString & filename)613 static QShader loadShader(const QString &filename)
614 {
615     QFile f(filename);
616     if (!f.open(QIODevice::ReadOnly)) {
617         qWarning() << "Failed to find shader" << filename;
618         return QShader();
619     }
620     return QShader::fromSerialized(f.readAll());
621 }
622 
syncMaterial(SyncData * syncData)623 void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
624 {
625     static QShader defaultVertexShader;
626     static QShader defaultFragmentShader;
627 
628     if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
629         m_material.setFlag(QSGMaterial::Blending, syncData->blending);
630         markDirty(QSGNode::DirtyMaterial);
631     }
632 
633     if (m_material.m_cullMode != syncData->cullMode) {
634         m_material.m_cullMode = syncData->cullMode;
635         markDirty(QSGNode::DirtyMaterial);
636     }
637 
638     if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) {
639         m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
640         if (m_material.m_hasCustomVertexShader) {
641             m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
642         } else {
643             if (!defaultVertexShader.isValid())
644                 defaultVertexShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"));
645             m_material.m_vertexShader = defaultVertexShader;
646         }
647 
648         m_material.m_hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
649         if (m_material.m_hasCustomFragmentShader) {
650             m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
651         } else {
652             if (!defaultFragmentShader.isValid())
653                 defaultFragmentShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"));
654             m_material.m_fragmentShader = defaultFragmentShader;
655         }
656 
657         m_material.m_materialType = shaderMaterialTypeCache.get(m_material.m_vertexShader, m_material.m_fragmentShader);
658         m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
659 
660         if (m_material.m_hasCustomVertexShader) {
661             m_material.m_linker.feedConstants(*syncData->vertex.shader);
662             m_material.m_linker.feedSamplers(*syncData->vertex.shader);
663         } else {
664             QSGShaderEffectNode::ShaderData defaultSD;
665             defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect vertex shader");
666             defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
667             defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
668 
669             // { mat4 qt_Matrix; float qt_Opacity; } where only the matrix is used
670             QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
671             v.name = QByteArrayLiteral("qt_Matrix");
672             v.offset = 0;
673             v.size = 16 * sizeof(float);
674             defaultSD.shaderInfo.variables.append(v);
675             QSGShaderEffectNode::VariableData vd;
676             vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
677             defaultSD.varData.append(vd);
678             defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
679             m_material.m_linker.feedConstants(defaultSD);
680         }
681 
682         if (m_material.m_hasCustomFragmentShader) {
683             m_material.m_linker.feedConstants(*syncData->fragment.shader);
684             m_material.m_linker.feedSamplers(*syncData->fragment.shader);
685         } else {
686             QSGShaderEffectNode::ShaderData defaultSD;
687             defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect fragment shader");
688             defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
689             defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
690 
691             // { mat4 qt_Matrix; float qt_Opacity; } where only the opacity is used
692             QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
693             v.name = QByteArrayLiteral("qt_Opacity");
694             v.offset = 16 * sizeof(float);
695             v.size = sizeof(float);
696             defaultSD.shaderInfo.variables.append(v);
697             QSGShaderEffectNode::VariableData vd;
698             vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
699             defaultSD.varData.append(vd);
700 
701             v.name = QByteArrayLiteral("source");
702             v.bindPoint = 1;
703             v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
704             defaultSD.shaderInfo.variables.append(v);
705             for (const QSGShaderEffectNode::VariableData &extVarData : qAsConst(syncData->fragment.shader->varData)) {
706                 if (extVarData.specialType == QSGShaderEffectNode::VariableData::Source) {
707                     vd.value = extVarData.value;
708                     break;
709                 }
710             }
711             vd.specialType = QSGShaderEffectNode::VariableData::Source;
712             defaultSD.varData.append(vd);
713 
714             defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
715 
716             m_material.m_linker.feedConstants(defaultSD);
717             m_material.m_linker.feedSamplers(defaultSD);
718         }
719 
720         m_material.m_linker.linkTextureSubRects();
721         m_material.updateTextureProviders(true);
722         markDirty(QSGNode::DirtyMaterial);
723 
724     } else  {
725 
726         if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) {
727             if (!syncData->vertex.dirtyConstants->isEmpty())
728                 m_material.m_linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
729             if (!syncData->fragment.dirtyConstants->isEmpty())
730                 m_material.m_linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
731             markDirty(QSGNode::DirtyMaterial);
732         }
733 
734         if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) {
735             if (!syncData->vertex.dirtyTextures->isEmpty())
736                 m_material.m_linker.feedSamplers(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
737             if (!syncData->fragment.dirtyTextures->isEmpty())
738                 m_material.m_linker.feedSamplers(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
739             m_material.m_linker.linkTextureSubRects();
740             m_material.updateTextureProviders(false);
741             markDirty(QSGNode::DirtyMaterial);
742         }
743     }
744 
745     if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.m_hasCustomVertexShader) {
746         m_material.setFlag(QSGMaterial::RequiresFullMatrix, m_material.m_hasCustomVertexShader);
747         markDirty(QSGNode::DirtyMaterial);
748     }
749 }
750 
handleTextureChange()751 void QSGRhiShaderEffectNode::handleTextureChange()
752 {
753     markDirty(QSGNode::DirtyMaterial);
754     emit m_mgr->textureChanged();
755 }
756 
handleTextureProviderDestroyed(QObject * object)757 void QSGRhiShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
758 {
759     for (QSGTextureProvider *&tp : m_material.m_textureProviders) {
760         if (tp == object)
761             tp = nullptr;
762     }
763 }
764 
preprocess()765 void QSGRhiShaderEffectNode::preprocess()
766 {
767     for (QSGTextureProvider *tp : m_material.m_textureProviders) {
768         if (tp) {
769             if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
770                 texture->updateTexture();
771         }
772     }
773 }
774 
cleanupMaterialTypeCache()775 void QSGRhiShaderEffectNode::cleanupMaterialTypeCache()
776 {
777     shaderMaterialTypeCache.reset();
778 }
779 
hasSeparateSamplerAndTextureObjects() const780 bool QSGRhiGuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
781 {
782     return false; // because SPIR-V and QRhi make it look so, regardless of the underlying API
783 }
784 
log() const785 QString QSGRhiGuiThreadShaderEffectManager::log() const
786 {
787     return QString();
788 }
789 
status() const790 QSGGuiThreadShaderEffectManager::Status QSGRhiGuiThreadShaderEffectManager::status() const
791 {
792     return m_status;
793 }
794 
prepareShaderCode(ShaderInfo::Type typeHint,const QByteArray & src,ShaderInfo * result)795 void QSGRhiGuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result)
796 {
797     QUrl srcUrl(QString::fromUtf8(src));
798     if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
799         if (!m_fileSelector) {
800             m_fileSelector = new QFileSelector(this);
801             m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("qsb"));
802         }
803         const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
804         QFile f(fn);
805         if (!f.open(QIODevice::ReadOnly)) {
806             qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
807             m_status = Error;
808             emit shaderCodePrepared(false, typeHint, src, result);
809             emit logAndStatusChanged();
810             return;
811         }
812         const QShader s = QShader::fromSerialized(f.readAll());
813         f.close();
814         if (!s.isValid()) {
815             qWarning("ShaderEffect: Failed to deserialize QShader from %s", qPrintable(fn));
816             m_status = Error;
817             emit shaderCodePrepared(false, typeHint, src, result);
818             emit logAndStatusChanged();
819             return;
820         }
821         result->name = fn;
822         result->rhiShader = s;
823         const bool ok = reflect(result);
824         m_status = ok ? Compiled : Error;
825         emit shaderCodePrepared(ok, typeHint, src, result);
826         emit logAndStatusChanged();
827     } else {
828         qWarning("rhi shader effect only supports files (qrc or local) at the moment");
829         emit shaderCodePrepared(false, typeHint, src, result);
830     }
831 }
832 
reflect(ShaderInfo * result)833 bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
834 {
835     switch (result->rhiShader.stage()) {
836     case QShader::VertexStage:
837         result->type = ShaderInfo::TypeVertex;
838         break;
839     case QShader::FragmentStage:
840         result->type = ShaderInfo::TypeFragment;
841         break;
842     default:
843         result->type = ShaderInfo::TypeOther;
844         qWarning("Unsupported shader stage (%d)", result->rhiShader.stage());
845         return false;
846     }
847 
848     const QShaderDescription desc = result->rhiShader.description();
849     result->constantDataSize = 0;
850 
851     int ubufBinding = -1;
852     const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
853     const int ubufCount = ubufs.count();
854     for (int i = 0; i < ubufCount; ++i) {
855         const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
856         if (ubufBinding == -1 && ubuf.binding >= 0) {
857             ubufBinding = ubuf.binding;
858             result->constantDataSize = ubuf.size;
859             for (const QShaderDescription::BlockVariable &member : ubuf.members) {
860                 ShaderInfo::Variable v;
861                 v.type = ShaderInfo::Constant;
862                 v.name = member.name.toUtf8();
863                 v.offset = member.offset;
864                 v.size = member.size;
865                 result->variables.append(v);
866             }
867         } else {
868             qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
869         }
870     }
871 
872     const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = desc.combinedImageSamplers();
873     const int samplerCount = combinedImageSamplers.count();
874     for (int i = 0; i < samplerCount; ++i) {
875         const QShaderDescription::InOutVariable &combinedImageSampler(combinedImageSamplers[i]);
876         ShaderInfo::Variable v;
877         v.type = ShaderInfo::Sampler;
878         v.name = combinedImageSampler.name.toUtf8();
879         v.bindPoint = combinedImageSampler.binding;
880         result->variables.append(v);
881     }
882 
883     return true;
884 }
885 
886 QT_END_NAMESPACE
887