Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsgrhishadereffectnode.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include "qsgrhisupport_p.h"
7#include <qsgmaterialshader.h>
9#include <private/qsgplaintexture_p.h>
11#include <QQmlFile>
12#include <QFile>
13#include <QFileSelector>
14#include <QMutexLocker>
15
17
18void QSGRhiShaderLinker::reset(const QShader &vs, const QShader &fs)
19{
20 Q_ASSERT(vs.isValid() && fs.isValid());
21 m_vs = vs;
22 m_fs = fs;
23
24 m_error = false;
25
26 //m_constantBufferSize = 0;
27 m_constants.clear();
31
32 m_constants.reserve(8);
35}
36
38{
39 Q_ASSERT(shader.shaderInfo.variables.size() == shader.varData.size());
40 static bool shaderEffectDebug = qEnvironmentVariableIntValue("QSG_RHI_SHADEREFFECT_DEBUG");
41 if (!dirtyIndices) {
42 for (int i = 0; i < shader.shaderInfo.variables.size(); ++i) {
45 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
46 Constant c;
47 c.size = var.size;
48 c.specialType = vd.specialType;
50 c.value = vd.value;
51 if (shaderEffectDebug) {
52 if (c.specialType == QSGShaderEffectNode::VariableData::None) {
53 qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
54 << "offset" << var.offset << "value" << c.value;
55 } else {
56 qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
57 << "offset" << var.offset << "special" << c.specialType;
58 }
59 }
60 } else {
61 Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
62 c.value = var.name.mid(11);
63 }
64 m_constants[var.offset] = c;
65 }
66 }
67 } else {
68 for (int idx : *dirtyIndices) {
69 const int offset = shader.shaderInfo.variables.at(idx).offset;
70 const QVariant value = shader.varData.at(idx).value;
71 m_constants[offset].value = value;
72 if (shaderEffectDebug) {
73 qDebug() << "cbuf update" << shader.shaderInfo.name
74 << "offset" << offset << "value" << value;
75 }
76 }
77 }
78}
79
81{
82 if (!dirtyIndices) {
83 for (int i = 0; i < shader.shaderInfo.variables.size(); ++i) {
85 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
88
89#ifndef QT_NO_DEBUG
90 int existingBindPoint = m_samplerNameMap.value(var.name, -1);
91 Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
92#endif
93
94 m_samplers.insert(var.bindPoint, vd.value);
95 m_samplerNameMap.insert(var.name, var.bindPoint);
96 }
97 }
98 } else {
99 for (int idx : *dirtyIndices) {
100 const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
101 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
102
103#ifndef QT_NO_DEBUG
104 int existingBindPoint = m_samplerNameMap.value(var.name, -1);
105 Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
106#endif
107
108 m_samplers.insert(var.bindPoint, vd.value);
109 m_samplerNameMap.insert(var.name, var.bindPoint);
110 }
111 }
112}
113
115{
116 // feedConstants stores <name> in Constant::value for subrect entries. Now
117 // that both constants and textures are known, replace the name with the
118 // texture binding point.
119 for (Constant &c : m_constants) {
121 if (c.value.userType() == QMetaType::QByteArray) {
122 const QByteArray name = c.value.toByteArray();
124 qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", name.constData());
125 const int binding = m_samplerNameMap[name];
126 c.value = binding;
127 m_subRectBindings.insert(binding);
128 }
129 }
130 }
131}
132
134{
135 if (m_error) {
136 qDebug() << "Failed to generate program data";
137 return;
138 }
139 qDebug() << "Combined shader data" << m_vs << m_fs;
140 qDebug() << " - constants" << m_constants;
141 qDebug() << " - samplers" << m_samplers;
142}
143
145{
146 QDebugStateSaver saver(debug);
147 debug.space();
148 debug << "size" << c.size;
150 debug << "special" << c.specialType;
151 else
152 debug << "value" << c.value;
153 return debug;
154}
155
157{
158 QSGMaterialType *ref(const QShader &vs, const QShader &fs);
159 void unref(const QShader &vs, const QShader &fs);
160 void reset() {
161 for (auto it = m_types.begin(), end = m_types.end(); it != end; ++it)
162 delete it->type;
163 m_types.clear();
165 }
168 m_graveyard.clear();
169 }
170 struct Key {
173 size_t hash;
174 Key(const QShader &vs, const QShader &fs)
175 : vs(vs),
176 fs(fs)
177 {
179 hash = hashGen(hashGen(0, vs), fs);
180 }
181 bool operator==(const Key &other) const {
182 return vs == other.vs && fs == other.fs;
183 }
184 };
189 QHash<Key, MaterialType> m_types;
190 QHash<Key, QSGMaterialType *> m_graveyard;
191};
192
194{
195 return seed ^ key.hash;
196}
197
199
201{
203 const Key k(vs, fs);
204 auto it = m_types.find(k);
205 if (it != m_types.end()) {
206 it->ref += 1;
207 return it->type;
208 }
209
210 auto reuseIt = m_graveyard.constFind(k);
211 if (reuseIt != m_graveyard.cend()) {
212 QSGMaterialType *t = reuseIt.value();
213 m_types.insert(k, { 1, t });
214 m_graveyard.erase(reuseIt);
215 return t;
216 }
217
219 m_types.insert(k, { 1, t });
220 return t;
221}
222
224{
226 const Key k(vs, fs);
227 auto it = m_types.find(k);
228 if (it != m_types.end()) {
229 if (!--it->ref) {
230 m_graveyard.insert(k, it->type);
231 m_types.erase(it);
232 }
233 }
234}
235
236static QHash<void *, QSGRhiShaderMaterialTypeCache> shaderMaterialTypeCache;
237
239{
241 shaderMaterialTypeCache[materialTypeCacheKey].reset();
242}
243
245{
247 shaderMaterialTypeCache[materialTypeCacheKey].clearGraveyard();
248}
249
251{
252public:
254
255 bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
256 void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
257 bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
258};
259
266
268{
269 float r, g, b, a;
270 c.getRgbF(&r, &g, &b, &a);
271 return QColor::fromRgbF(r * a, g * a, b * a, a);
272}
273
274template<typename T>
275static inline void fillUniformBlockMember(char *dst, const T *value, int valueCount, int fieldSizeBytes)
276{
277 const size_t valueBytes = sizeof(T) * valueCount;
278 const size_t fieldBytes = fieldSizeBytes;
279 if (valueBytes <= fieldBytes) {
280 memcpy(dst, value, valueBytes);
281 if (valueBytes < fieldBytes)
282 memset(dst + valueBytes, 0, fieldBytes - valueBytes);
283 } else {
284 memcpy(dst, value, fieldBytes);
285 }
286}
287
289{
290 Q_UNUSED(oldMaterial);
291 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
292
293 bool changed = false;
294 QByteArray *buf = state.uniformData();
295
296 for (auto it = mat->m_linker.m_constants.constBegin(), itEnd = mat->m_linker.m_constants.constEnd(); it != itEnd; ++it) {
297 const int offset = it.key();
298 char *dst = buf->data() + offset;
299 const QSGRhiShaderLinker::Constant &c(it.value());
301 if (state.isOpacityDirty()) {
302 const float f = state.opacity();
303 fillUniformBlockMember<float>(dst, &f, 1, c.size);
304 changed = true;
305 }
306 } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
307 if (state.isMatrixDirty()) {
308 Q_ASSERT(state.projectionMatrixCount() == mat->viewCount());
309 const int rendererViewCount = state.projectionMatrixCount();
310 const int shaderMatrixCount = c.size / 64;
311 if (shaderMatrixCount < mat->viewCount() && mat->viewCount() >= 2) {
312 qWarning("qt_Matrix uniform block member size is wrong: expected at least view_count * 64 bytes, "
313 "where view_count is %d, meaning %d bytes in total, but got only %d bytes. "
314 "This may be due to the ShaderEffect and its shaders not being multiview-capable, "
315 "or they are used with an unexpected render target. "
316 "Check if the shaders declare qt_Matrix as appropriate, "
317 "and if gl_ViewIndex is used correctly in the vertex shader.",
318 mat->viewCount(), mat->viewCount() * 64, c.size);
319 }
320 const int matrixCount = qMin(rendererViewCount, shaderMatrixCount);
321 size_t offset = 0;
322 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
323 const QMatrix4x4 m = state.combinedMatrix(viewIndex);
324 fillUniformBlockMember<float>(dst + offset, m.constData(), 16, 64);
325 offset += 64;
326 }
327 if (offset < c.size)
328 memset(dst + offset, 0, c.size - offset);
329 changed = true;
330 }
331 } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
332 // vec4
333 QRectF subRect(0, 0, 1, 1);
334 const int binding = c.value.toInt(); // filled in by linkTextureSubRects
336 if (QSGTextureProvider *tp = mat->m_textureProviders.at(binding)) {
337 if (QSGTexture *t = tp->texture())
338 subRect = t->normalizedTextureSubRect();
339 }
340 }
341 const float f[4] = { float(subRect.x()), float(subRect.y()),
342 float(subRect.width()), float(subRect.height()) };
343 fillUniformBlockMember<float>(dst, f, 4, c.size);
344 } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
345 changed = true;
346 switch (int(c.value.userType())) {
347 case QMetaType::QColor: {
348 const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value)).toRgb();
349 const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
350 fillUniformBlockMember<float>(dst, f, 4, c.size);
351 break;
352 }
353 case QMetaType::Float: {
354 const float f = qvariant_cast<float>(c.value);
355 fillUniformBlockMember<float>(dst, &f, 1, c.size);
356 break;
357 }
358 case QMetaType::Double: {
359 const float f = float(qvariant_cast<double>(c.value));
360 fillUniformBlockMember<float>(dst, &f, 1, c.size);
361 break;
362 }
363 case QMetaType::Int: {
364 const qint32 i = c.value.toInt();
365 fillUniformBlockMember<qint32>(dst, &i, 1, c.size);
366 break;
367 }
368 case QMetaType::Bool: {
369 const qint32 b = c.value.toBool();
370 fillUniformBlockMember<qint32>(dst, &b, 1, c.size);
371 break;
372 }
373 case QMetaType::QTransform: { // mat3
374 const QTransform v = qvariant_cast<QTransform>(c.value);
375 const float m[3][3] = {
376 { float(v.m11()), float(v.m12()), float(v.m13()) },
377 { float(v.m21()), float(v.m22()), float(v.m23()) },
378 { float(v.m31()), float(v.m32()), float(v.m33()) }
379 };
380 // stored as 4 floats per column, 1 unused
381 memset(dst, 0, c.size);
382 const size_t bytesPerColumn = 4 * sizeof(float);
383 if (c.size >= bytesPerColumn)
384 fillUniformBlockMember<float>(dst, m[0], 3, 3 * sizeof(float));
385 if (c.size >= 2 * bytesPerColumn)
386 fillUniformBlockMember<float>(dst + bytesPerColumn, m[1], 3, 3 * sizeof(float));
387 if (c.size >= 3 * bytesPerColumn)
388 fillUniformBlockMember<float>(dst + 2 * bytesPerColumn, m[2], 3, 3 * sizeof(float));
389 break;
390 }
391 case QMetaType::QSize:
392 case QMetaType::QSizeF: { // vec2
393 const QSizeF v = c.value.toSizeF();
394 const float f[2] = { float(v.width()), float(v.height()) };
395 fillUniformBlockMember<float>(dst, f, 2, c.size);
396 break;
397 }
398 case QMetaType::QPoint:
399 case QMetaType::QPointF: { // vec2
400 const QPointF v = c.value.toPointF();
401 const float f[2] = { float(v.x()), float(v.y()) };
402 fillUniformBlockMember<float>(dst, f, 2, c.size);
403 break;
404 }
405 case QMetaType::QRect:
406 case QMetaType::QRectF: { // vec4
407 const QRectF v = c.value.toRectF();
408 const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
409 fillUniformBlockMember<float>(dst, f, 4, c.size);
410 break;
411 }
412 case QMetaType::QVector2D: { // vec2
413 const QVector2D v = qvariant_cast<QVector2D>(c.value);
414 const float f[2] = { float(v.x()), float(v.y()) };
415 fillUniformBlockMember<float>(dst, f, 2, c.size);
416 break;
417 }
418 case QMetaType::QVector3D: { // vec3
419 const QVector3D v = qvariant_cast<QVector3D>(c.value);
420 const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
421 fillUniformBlockMember<float>(dst, f, 3, c.size);
422 break;
423 }
424 case QMetaType::QVector4D: { // vec4
425 const QVector4D v = qvariant_cast<QVector4D>(c.value);
426 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
427 fillUniformBlockMember<float>(dst, f, 4, c.size);
428 break;
429 }
430 case QMetaType::QQuaternion: { // vec4
431 const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
432 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
433 fillUniformBlockMember<float>(dst, f, 4, c.size);
434 break;
435 }
436 case QMetaType::QMatrix4x4: { // mat4
437 const QMatrix4x4 m = qvariant_cast<QMatrix4x4>(c.value);
438 fillUniformBlockMember<float>(dst, m.constData(), 16, c.size);
439 break;
440 }
441 default:
442 break;
443 }
444 }
445 }
446
447 return changed;
448}
449
451 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
452{
453 Q_UNUSED(oldMaterial);
454 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
455
457 return;
458
459 QSGTextureProvider *tp = mat->m_textureProviders.at(binding);
460 if (tp) {
461 if (QSGTexture *t = tp->texture()) {
462 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
463
464 if (t->isAtlasTexture() && !mat->m_geometryUsesTextureSubRect && !mat->usesSubRectUniform(binding)) {
465 // Why the hassle with the batch: while removedFromAtlas() is
466 // able to operate with its own resource update batch (which is
467 // then committed immediately), that approach is wrong when the
468 // atlas enqueued (in the updateRhiTexture() above) not yet
469 // committed operations to state.resourceUpdateBatch()... The
470 // only safe way then is to use the same batch the atlas'
471 // updateRhiTexture() used.
472 QSGTexture *newTexture = t->removedFromAtlas(state.resourceUpdateBatch());
473 if (newTexture) {
474 t = newTexture;
475 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
476 }
477 }
478 *texture = t;
479 return;
480 }
481 }
482
483 if (!mat->m_dummyTexture) {
484 mat->m_dummyTexture = new QSGPlainTexture;
485 mat->m_dummyTexture->setFiltering(QSGTexture::Nearest);
486 mat->m_dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
487 mat->m_dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
489 img.fill(0);
490 mat->m_dummyTexture->setImage(img);
491 mat->m_dummyTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
492 }
493 *texture = mat->m_dummyTexture;
494}
495
497 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
498{
500 Q_UNUSED(oldMaterial);
501 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
502
503 switch (mat->m_cullMode) {
505 ps->cullMode = GraphicsPipelineState::CullFront;
506 return true;
508 ps->cullMode = GraphicsPipelineState::CullBack;
509 return true;
510 default:
511 return false;
512 }
513}
514
516 : m_node(node)
517{
518 setFlag(Blending | RequiresFullMatrix, true); // may be changed in syncMaterial()
519}
520
528
529static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
530{
531 for (QSGTextureProvider *tp : textureProviders) {
532 if (tp && tp->texture() && tp->texture()->isAtlasTexture())
533 return true;
534 }
535 return false;
536}
537
539{
540 Q_ASSERT(other && type() == other->type());
541 const QSGRhiShaderEffectMaterial *o = static_cast<const QSGRhiShaderEffectMaterial *>(other);
542
543 if (int diff = m_cullMode - o->m_cullMode)
544 return diff;
545
546 if (int diff = m_textureProviders.size() - o->m_textureProviders.size())
547 return diff;
548
549 if (m_linker.m_constants != o->m_linker.m_constants)
550 return 1;
551
553 return -1;
554
555 if (hasAtlasTexture(o->m_textureProviders) && !o->m_geometryUsesTextureSubRect)
556 return 1;
557
558 for (int binding = 0, count = m_textureProviders.size(); binding != count; ++binding) {
559 QSGTextureProvider *tp1 = m_textureProviders.at(binding);
560 QSGTextureProvider *tp2 = o->m_textureProviders.at(binding);
561 if (tp1 && tp2) {
562 QSGTexture *t1 = tp1->texture();
563 QSGTexture *t2 = tp2->texture();
564 if (t1 && t2) {
565 const qint64 diff = t1->comparisonKey() - t2->comparisonKey();
566 if (diff != 0)
567 return diff < 0 ? -1 : 1;
568 } else {
569 if (!t1 && t2)
570 return -1;
571 if (t1 && !t2)
572 return 1;
573 }
574 } else {
575 if (!tp1 && tp2)
576 return -1;
577 if (tp1 && !tp2)
578 return 1;
579 }
580 }
581
582 return 0;
583}
584
589
595
597{
598 if (layoutChange) {
600 if (tp) {
601 QObject::disconnect(tp, SIGNAL(textureChanged()), m_node,
602 SLOT(handleTextureChange()));
603 QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), m_node,
604 SLOT(handleTextureProviderDestroyed(QObject*)));
605 }
606 }
607 m_textureProviders.fill(nullptr, MAX_BINDINGS);
608 }
609
610 for (auto it = m_linker.m_samplers.constBegin(), itEnd = m_linker.m_samplers.constEnd(); it != itEnd; ++it) {
611 const int binding = it.key();
612 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
613 QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
614 if (binding >= MAX_BINDINGS) {
615 qWarning("Sampler at binding %d exceeds the available ShaderEffect binding slots; ignored",
616 binding);
617 continue;
618 }
619 QSGTextureProvider *&activeProvider(m_textureProviders[binding]);
620 if (newProvider != activeProvider) {
621 if (activeProvider) {
622 QObject::disconnect(activeProvider, SIGNAL(textureChanged()), m_node,
623 SLOT(handleTextureChange()));
624 QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), m_node,
625 SLOT(handleTextureProviderDestroyed(QObject*)));
626 }
627 if (newProvider) {
628 Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
629 "QSGRhiShaderEffectMaterial::updateTextureProviders",
630 "Texture provider must belong to the rendering thread");
631 QObject::connect(newProvider, SIGNAL(textureChanged()), m_node, SLOT(handleTextureChange()));
632 QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), m_node,
633 SLOT(handleTextureProviderDestroyed(QObject*)));
634 } else {
635 const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
636 qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
637 binding, typeName);
638 }
639 activeProvider = newProvider;
640 }
641 }
642}
643
645 : m_material(this)
646{
647 Q_UNUSED(rc);
648 setFlag(UsePreprocess, true);
649 setMaterial(&m_material);
650}
651
653{
654 QRectF srcRect(0, 0, 1, 1);
655 bool geometryUsesTextureSubRect = false;
656 if (supportsAtlasTextures) {
657 QSGTextureProvider *tp = nullptr;
658 for (int binding = 0, count = m_material.m_textureProviders.size(); binding != count; ++binding) {
659 if (QSGTextureProvider *candidate = m_material.m_textureProviders.at(binding)) {
660 if (!tp) {
661 tp = candidate;
662 } else { // there can only be one...
663 tp = nullptr;
664 break;
665 }
666 }
667 }
668 if (tp && tp->texture()) {
669 srcRect = tp->texture()->normalizedTextureSubRect();
670 geometryUsesTextureSubRect = true;
671 }
672 }
673
674 if (m_material.m_geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
675 m_material.m_geometryUsesTextureSubRect = geometryUsesTextureSubRect;
677 }
678
679 return srcRect;
680}
681
682static QShader loadShaderFromFile(const QString &filename)
683{
684 QFile f(filename);
685 if (!f.open(QIODevice::ReadOnly)) {
686 qWarning() << "Failed to find shader" << filename;
687 return QShader();
688 }
689 return QShader::fromSerialized(f.readAll());
690}
691
700
702{
704 s.shader = loadShaderFromFile(filename);
705 const QList<QShaderDescription::BlockVariable> uboMembers = s.shader.description().uniformBlocks().constFirst().members;
706 for (const auto &member: uboMembers) {
707 if (member.name == QByteArrayLiteral("qt_Matrix"))
708 s.matrixArrayByteSize = member.size;
709 else if (member.name == QByteArrayLiteral("qt_Opacity"))
710 s.opacityOffset = member.offset;
711 }
712 s.viewCount = viewCount;
713 return s;
714}
715
717{
718 static const int defaultVertexShaderCount = 2;
719 static QSGRhiShaderEffectDefaultShader defaultVertexShaders[defaultVertexShaderCount] = {
720 QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"), 1),
721 QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb.mv2qsb"), 2)
722 };
723 static const int defaultFragmentShaderCount = 2;
724 static QSGRhiShaderEffectDefaultShader defaultFragmentShaders[defaultFragmentShaderCount] = {
725 QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"), 1),
726 QSGRhiShaderEffectDefaultShader::create(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb.mv2qsb"), 2)
727 };
728
729 if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
730 m_material.setFlag(QSGMaterial::Blending, syncData->blending);
732 }
733
734 if (m_material.m_cullMode != syncData->cullMode) {
735 m_material.m_cullMode = syncData->cullMode;
737 }
738
739 if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) {
740 if (m_material.m_materialType) {
742 m_material.m_fragmentShader);
743 }
744
745 m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
746 quint32 defaultMatrixArrayByteSize = 0;
747 if (m_material.m_hasCustomVertexShader) {
748 m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
749 } else {
750 bool found = false;
751 for (int i = 0; i < defaultVertexShaderCount; ++i) {
752 if (defaultVertexShaders[i].viewCount == syncData->viewCount) {
753 m_material.m_vertexShader = defaultVertexShaders[i].shader;
754 defaultMatrixArrayByteSize = defaultVertexShaders[i].matrixArrayByteSize;
755 found = true;
756 break;
757 }
758 }
759 if (!found) {
760 qWarning("No default vertex shader found for view count %d", syncData->viewCount);
761 m_material.m_vertexShader = defaultVertexShaders[0].shader;
762 defaultMatrixArrayByteSize = 64;
763 }
764 }
765
766 m_material.m_hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
767 quint32 defaultOpacityOffset = 0;
768 if (m_material.m_hasCustomFragmentShader) {
769 m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
770 } else {
771 bool found = false;
772 for (int i = 0; i < defaultFragmentShaderCount; ++i) {
773 if (defaultFragmentShaders[i].viewCount == syncData->viewCount) {
774 m_material.m_fragmentShader = defaultFragmentShaders[i].shader;
775 defaultOpacityOffset = defaultFragmentShaders[i].opacityOffset;
776 found = true;
777 break;
778 }
779 }
780 if (!found) {
781 qWarning("No default fragment shader found for view count %d", syncData->viewCount);
782 m_material.m_fragmentShader = defaultFragmentShaders[0].shader;
783 defaultOpacityOffset = 64;
784 }
785 }
786
787 m_material.m_materialType = shaderMaterialTypeCache[syncData->materialTypeCacheKey].ref(m_material.m_vertexShader,
788 m_material.m_fragmentShader);
789 m_material.m_materialTypeCacheKey = syncData->materialTypeCacheKey;
790
791 m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
792
793 if (m_material.m_hasCustomVertexShader) {
794 m_material.m_linker.feedConstants(*syncData->vertex.shader);
795 m_material.m_linker.feedSamplers(*syncData->vertex.shader);
796 } else {
798 defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect vertex shader");
799 defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
801
802 // { mat4 qt_Matrix[VIEW_COUNT]; float qt_Opacity; } where only the matrix is used
804 v.name = QByteArrayLiteral("qt_Matrix");
805 v.offset = 0;
806 v.size = defaultMatrixArrayByteSize;
807 defaultSD.shaderInfo.variables.append(v);
810 defaultSD.varData.append(vd);
811 m_material.m_linker.feedConstants(defaultSD);
812 }
813
814 if (m_material.m_hasCustomFragmentShader) {
815 m_material.m_linker.feedConstants(*syncData->fragment.shader);
816 m_material.m_linker.feedSamplers(*syncData->fragment.shader);
817 } else {
819 defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect fragment shader");
820 defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
822
823 // { mat4 qt_Matrix[VIEW_COUNT]; float qt_Opacity; } where only the opacity is used
825 v.name = QByteArrayLiteral("qt_Opacity");
826 v.offset = defaultOpacityOffset;
827 v.size = sizeof(float);
828 defaultSD.shaderInfo.variables.append(v);
831 defaultSD.varData.append(vd);
832
833 v.name = QByteArrayLiteral("source");
834 v.bindPoint = 1;
836 defaultSD.shaderInfo.variables.append(v);
837 for (const QSGShaderEffectNode::VariableData &extVarData : std::as_const(syncData->fragment.shader->varData)) {
838 if (extVarData.specialType == QSGShaderEffectNode::VariableData::Source) {
839 vd.value = extVarData.value;
840 break;
841 }
842 }
844 defaultSD.varData.append(vd);
845
846 m_material.m_linker.feedConstants(defaultSD);
847 m_material.m_linker.feedSamplers(defaultSD);
848 }
849
850 m_material.m_linker.linkTextureSubRects();
851 m_material.updateTextureProviders(true);
853
854 } else {
855
856 if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) {
857 if (!syncData->vertex.dirtyConstants->isEmpty())
858 m_material.m_linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
859 if (!syncData->fragment.dirtyConstants->isEmpty())
860 m_material.m_linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
862 }
863
864 if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) {
865 if (!syncData->vertex.dirtyTextures->isEmpty())
866 m_material.m_linker.feedSamplers(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
867 if (!syncData->fragment.dirtyTextures->isEmpty())
868 m_material.m_linker.feedSamplers(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
869 m_material.m_linker.linkTextureSubRects();
870 m_material.updateTextureProviders(false);
872 }
873 }
874
875 if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.m_hasCustomVertexShader) {
878 }
879}
880
881void QSGRhiShaderEffectNode::handleTextureChange()
882{
885}
886
887void QSGRhiShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
888{
889 for (QSGTextureProvider *&tp : m_material.m_textureProviders) {
890 if (tp == object)
891 tp = nullptr;
892 }
893}
894
896{
897 for (QSGTextureProvider *tp : m_material.m_textureProviders) {
898 if (tp) {
899 if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
900 texture->updateTexture();
901 }
902 }
903}
904
906{
907 return false; // because SPIR-V and QRhi make it look so, regardless of the underlying API
908}
909
914
919
921{
922 if (!src.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || src.isLocalFile()) {
923 if (!m_fileSelector) {
924 m_fileSelector = new QFileSelector(this);
925 m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("qsb"));
926 }
927 const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(src));
928 const QShader s = loadShaderFromFile(fn);
929 if (!s.isValid()) {
930 qWarning("ShaderEffect: Failed to deserialize QShader from %s. "
931 "Either the filename is incorrect, or it is not a valid .qsb file. "
932 "In Qt 6 shaders must be preprocessed using the Qt Shader Tools infrastructure. "
933 "The vertexShader and fragmentShader properties are now URLs that are expected to point to .qsb files generated by the qsb tool. "
934 "See https://doc.qt.io/qt-6/qtshadertools-index.html for more information.",
935 qPrintable(fn));
936 m_status = Error;
937 emit shaderCodePrepared(false, typeHint, src, result);
939 return;
940 }
941 result->name = fn;
942 result->rhiShader = s;
943 const bool ok = reflect(result);
944 m_status = ok ? Compiled : Error;
945 emit shaderCodePrepared(ok, typeHint, src, result);
947 } else {
948 qWarning("rhi shader effect only supports files (qrc or local) at the moment");
949 emit shaderCodePrepared(false, typeHint, src, result);
950 }
951}
952
953bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
954{
955 switch (result->rhiShader.stage()) {
957 result->type = ShaderInfo::TypeVertex;
958 break;
960 result->type = ShaderInfo::TypeFragment;
961 break;
962 default:
963 result->type = ShaderInfo::TypeOther;
964 qWarning("Unsupported shader stage (%d)", result->rhiShader.stage());
965 return false;
966 }
967
968 const QShaderDescription desc = result->rhiShader.description();
969
970 int ubufBinding = -1;
971 const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
972 const int ubufCount = ubufs.size();
973 for (int i = 0; i < ubufCount; ++i) {
974 const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
975 if (ubufBinding == -1 && ubuf.binding >= 0) {
976 ubufBinding = ubuf.binding;
977 for (const QShaderDescription::BlockVariable &member : ubuf.members) {
978 ShaderInfo::Variable v;
979 v.type = ShaderInfo::Constant;
980 v.name = member.name;
981 v.offset = member.offset;
982 v.size = member.size;
983 result->variables.append(v);
984 }
985 } else {
986 qWarning("Uniform block %s (binding %d) ignored", ubuf.blockName.constData(),
987 ubuf.binding);
988 }
989 }
990
991 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = desc.combinedImageSamplers();
992 const int samplerCount = combinedImageSamplers.size();
993 for (int i = 0; i < samplerCount; ++i) {
994 const QShaderDescription::InOutVariable &combinedImageSampler(combinedImageSamplers[i]);
995 ShaderInfo::Variable v;
996 v.type = ShaderInfo::Sampler;
997 v.name = combinedImageSampler.name;
998 v.bindPoint = combinedImageSampler.binding;
999 result->variables.append(v);
1000 }
1001
1002 return true;
1003}
1004
1006
1007#include "moc_qsgrhishadereffectnode_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromRgbF(float r, float g, float b, float a=1.0)
Static convenience function that returns a QColor constructed from the RGB color values,...
Definition qcolor.cpp:2427
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
void setExtraSelectors(const QStringList &list)
Sets the list of extra selectors which have been added programmatically to this instance.
QString select(const QString &filePath) const
This function returns the selected version of the path, based on the conditions at runtime.
\inmodule QtCore
Definition qfile.h:93
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
void reserve(qsizetype size)
Ensures that the QHash's internal hash table has space to store at least size items without having to...
Definition qhash.h:931
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1215
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
\inmodule QtCore\reentrant
Definition qpoint.h:217
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
The QQuaternion class represents a quaternion consisting of a vector and scalar.
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
\inmodule QtCore\reentrant
Definition qrect.h:484
The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,...
Definition qsgtexture.h:100
void setMaterial(QSGMaterial *material)
Sets the material of this geometry node to material.
Definition qsgnode.cpp:927
void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QUrl &src, ShaderInfo *result)
Encapsulates the current rendering state during a call to QSGMaterialShader::updateUniformData() and ...
The QSGMaterialShader class represents a graphics API independent shader program.
void setShader(Stage stage, const QShader &shader)
Sets the shader for the specified stage.
void setFlag(Flags flags, bool on=true)
Sets the flags on this material shader if on is true; otherwise clears the specified flags.
The QSGMaterial class encapsulates rendering state for a shader program.
Definition qsgmaterial.h:15
QSGMaterial::Flags flags() const
Returns the material's flags.
Definition qsgmaterial.h:44
void setFlag(Flags flags, bool on=true)
Sets the flags flags on this material if on is true; otherwise clears the attribute.
@ DirtyMaterial
Definition qsgnode.h:75
@ UsePreprocess
Definition qsgnode.h:52
void markDirty(DirtyState bits)
Notifies all connected renderers that the node has dirty bits.
Definition qsgnode.cpp:624
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
Definition qsgnode.cpp:586
RenderMode
\value RenderMode2D Normal 2D rendering \value RenderMode2DNoDepthBuffer Normal 2D rendering with dep...
void prepareShaderCode(ShaderInfo::Type typeHint, const QUrl &src, ShaderInfo *result) override
bool hasSeparateSamplerAndTextureObjects() const override
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to prepare use of sampled images in the shader,...
QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material)
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to enable the material to provide a custom set of graphics...
QVector< QSGTextureProvider * > m_textureProviders
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node)
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
QSGRhiShaderEffectNode * m_node
QSGShaderEffectNode::CullMode m_cullMode
void updateTextureProviders(bool layoutChange)
void preprocess() override
Override this function to do processing on the node before it is rendered.
static void garbageCollectMaterialTypeCache(void *materialTypeCacheKey)
QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) override
QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc)
void syncMaterial(SyncData *syncData) override
static void resetMaterialTypeCache(void *materialTypeCacheKey)
void feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet< int > *dirtyIndices=nullptr)
QHash< QByteArray, int > m_samplerNameMap
void reset(const QShader &vs, const QShader &fs)
QHash< uint, Constant > m_constants
QHash< int, QVariant > m_samplers
void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet< int > *dirtyIndices=nullptr)
The QSGTextureProvider class encapsulates texture based entities in QML.
virtual QSGTexture * texture() const =0
Returns a pointer to the texture object.
\inmodule QtQuick
Definition qsgtexture.h:20
virtual void commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
Call this function to enqueue image upload operations to resourceUpdates, in case there are any pendi...
virtual QSGTexture * removedFromAtlas(QRhiResourceUpdateBatch *resourceUpdates=nullptr) const
This function returns a copy of the current texture which is removed from its atlas.
void setFiltering(Filtering filter)
Sets the sampling mode to filter.
const_iterator constBegin() const noexcept
Definition qset.h:139
void clear()
Definition qset.h:61
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtGui
Definition qshader.h:81
QShaderCode shader(const QShaderKey &key) const
Definition qshader.cpp:395
static QShader fromSerialized(const QByteArray &data)
Creates a new QShader instance from the given data.
Definition qshader.cpp:540
@ VertexStage
Definition qshader.h:84
@ FragmentStage
Definition qshader.h:88
bool isValid() const
Definition qshader.cpp:343
\inmodule QtCore
Definition qsize.h:208
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QThread * currentThread()
Definition qthread.cpp:1039
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
\inmodule QtCore
Definition qurl.h:94
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
#define this
Definition dialogs.cpp:9
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
@ CaseInsensitive
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
QList< QString > QStringList
Constructs a string list that contains the given string, str.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
const char * typeName
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum src
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint texture
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLint ref
GLuint name
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint shader
Definition qopenglext.h:665
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
Members members(const Members &candidates, QTypeRevision maxMajorVersion, Postprocess &&process)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:492
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static void fillUniformBlockMember(char *dst, const T *value, int valueCount, int fieldSizeBytes)
static QShader loadShaderFromFile(const QString &filename)
size_t qHash(const QSGRhiShaderMaterialTypeCache::Key &key, size_t seed=0)
static QMutex shaderMaterialTypeCacheMutex
static QColor qsg_premultiply_color(const QColor &c)
static QHash< void *, QSGRhiShaderMaterialTypeCache > shaderMaterialTypeCache
static bool hasAtlasTexture(const QVector< QSGTextureProvider * > &textureProviders)
QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c)
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define t2
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define emit
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
int qint32
Definition qtypes.h:49
long long qint64
Definition qtypes.h:60
QT_BEGIN_NAMESPACE typedef signed char qint8
Definition qtypes.h:45
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]
view create()
Describes state changes that the material wants to apply to the currently active graphics pipeline st...
The QSGMaterialType class is used as a unique type token in combination with QSGMaterial.
static QSGRhiShaderEffectDefaultShader create(const QString &filename, int viewCount)
Key(const QShader &vs, const QShader &fs)
void unref(const QShader &vs, const QShader &fs)
QSGMaterialType * ref(const QShader &vs, const QShader &fs)
QHash< Key, QSGMaterialType * > m_graveyard
QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo
\variable QShaderDescription::InOutVariable::name
\variable QShaderDescription::BlockVariable::name