Qt 6.x
The Qt SDK
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
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 m_constantBufferSize = qMax(m_constantBufferSize, shader.shaderInfo.constantDataSize);
43 for (int i = 0; i < shader.shaderInfo.variables.size(); ++i) {
46 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
47 Constant c;
48 c.size = var.size;
49 c.specialType = vd.specialType;
51 c.value = vd.value;
52 if (shaderEffectDebug) {
53 if (c.specialType == QSGShaderEffectNode::VariableData::None) {
54 qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
55 << "offset" << var.offset << "value" << c.value;
56 } else {
57 qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
58 << "offset" << var.offset << "special" << c.specialType;
59 }
60 }
61 } else {
62 Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
63 c.value = var.name.mid(11);
64 }
65 m_constants[var.offset] = c;
66 }
67 }
68 } else {
69 for (int idx : *dirtyIndices) {
70 const int offset = shader.shaderInfo.variables.at(idx).offset;
71 const QVariant value = shader.varData.at(idx).value;
72 m_constants[offset].value = value;
73 if (shaderEffectDebug) {
74 qDebug() << "cbuf update" << shader.shaderInfo.name
75 << "offset" << offset << "value" << value;
76 }
77 }
78 }
79}
80
82{
83 if (!dirtyIndices) {
84 for (int i = 0; i < shader.shaderInfo.variables.size(); ++i) {
86 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
89
90#ifndef QT_NO_DEBUG
91 int existingBindPoint = m_samplerNameMap.value(var.name, -1);
92 Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
93#endif
94
95 m_samplers.insert(var.bindPoint, vd.value);
96 m_samplerNameMap.insert(var.name, var.bindPoint);
97 }
98 }
99 } else {
100 for (int idx : *dirtyIndices) {
101 const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
102 const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
103
104#ifndef QT_NO_DEBUG
105 int existingBindPoint = m_samplerNameMap.value(var.name, -1);
106 Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
107#endif
108
109 m_samplers.insert(var.bindPoint, vd.value);
110 m_samplerNameMap.insert(var.name, var.bindPoint);
111 }
112 }
113}
114
116{
117 // feedConstants stores <name> in Constant::value for subrect entries. Now
118 // that both constants and textures are known, replace the name with the
119 // texture binding point.
120 for (Constant &c : m_constants) {
122 if (c.value.userType() == QMetaType::QByteArray) {
123 const QByteArray name = c.value.toByteArray();
125 qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", name.constData());
126 const int binding = m_samplerNameMap[name];
127 c.value = binding;
128 m_subRectBindings.insert(binding);
129 }
130 }
131 }
132}
133
135{
136 if (m_error) {
137 qDebug() << "Failed to generate program data";
138 return;
139 }
140 qDebug() << "Combined shader data" << m_vs << m_fs << "cbuffer size" << m_constantBufferSize;
141 qDebug() << " - constants" << m_constants;
142 qDebug() << " - samplers" << m_samplers;
143}
144
146{
147 QDebugStateSaver saver(debug);
148 debug.space();
149 debug << "size" << c.size;
151 debug << "special" << c.specialType;
152 else
153 debug << "value" << c.value;
154 return debug;
155}
156
158{
159 QSGMaterialType *ref(const QShader &vs, const QShader &fs);
160 void unref(const QShader &vs, const QShader &fs);
161 void reset() {
162 for (auto it = m_types.begin(), end = m_types.end(); it != end; ++it)
163 delete it->type;
164 m_types.clear();
166 }
169 m_graveyard.clear();
170 }
171 struct Key {
174 size_t hash;
175 Key(const QShader &vs, const QShader &fs)
176 : vs(vs),
177 fs(fs)
178 {
180 hash = hashGen(hashGen(0, vs), fs);
181 }
182 bool operator==(const Key &other) const {
183 return vs == other.vs && fs == other.fs;
184 }
185 };
187 int ref;
189 };
192};
193
195{
196 return seed ^ key.hash;
197}
198
200
202{
204 const Key k(vs, fs);
205 auto it = m_types.find(k);
206 if (it != m_types.end()) {
207 it->ref += 1;
208 return it->type;
209 }
210
212 m_types.insert(k, { 1, t });
213 return t;
214}
215
217{
219 const Key k(vs, fs);
220 auto it = m_types.find(k);
221 if (it != m_types.end()) {
222 if (!--it->ref) {
223 m_graveyard.append(it->type);
224 m_types.erase(it);
225 }
226 }
227}
228
230
232{
234 shaderMaterialTypeCache[materialTypeCacheKey].reset();
235}
236
238{
240 shaderMaterialTypeCache[materialTypeCacheKey].clearGraveyard();
241}
242
244{
245public:
247
248 bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
249 void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
250 bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
251};
252
254{
258}
259
261{
262 float r, g, b, a;
263 c.getRgbF(&r, &g, &b, &a);
264 return QColor::fromRgbF(r * a, g * a, b * a, a);
265}
266
267template<typename T>
268static inline void fillUniformBlockMember(char *dst, const T *value, int valueCount, int fieldSizeBytes)
269{
270 const size_t valueBytes = sizeof(T) * valueCount;
271 const size_t fieldBytes = fieldSizeBytes;
272 if (valueBytes <= fieldBytes) {
273 memcpy(dst, value, valueBytes);
274 if (valueBytes < fieldBytes)
275 memset(dst + valueBytes, 0, fieldBytes - valueBytes);
276 } else {
277 memcpy(dst, value, fieldBytes);
278 }
279}
280
282{
283 Q_UNUSED(oldMaterial);
284 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
285
286 bool changed = false;
287 QByteArray *buf = state.uniformData();
288
289 for (auto it = mat->m_linker.m_constants.constBegin(), itEnd = mat->m_linker.m_constants.constEnd(); it != itEnd; ++it) {
290 const int offset = it.key();
291 char *dst = buf->data() + offset;
292 const QSGRhiShaderLinker::Constant &c(it.value());
294 if (state.isOpacityDirty()) {
295 const float f = state.opacity();
296 fillUniformBlockMember<float>(dst, &f, 1, c.size);
297 changed = true;
298 }
299 } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
300 if (state.isMatrixDirty()) {
301 const QMatrix4x4 m = state.combinedMatrix();
302 fillUniformBlockMember<float>(dst, m.constData(), 16, c.size);
303 changed = true;
304 }
305 } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
306 // vec4
307 QRectF subRect(0, 0, 1, 1);
308 const int binding = c.value.toInt(); // filled in by linkTextureSubRects
310 if (QSGTextureProvider *tp = mat->m_textureProviders.at(binding)) {
311 if (QSGTexture *t = tp->texture())
312 subRect = t->normalizedTextureSubRect();
313 }
314 }
315 const float f[4] = { float(subRect.x()), float(subRect.y()),
316 float(subRect.width()), float(subRect.height()) };
317 fillUniformBlockMember<float>(dst, f, 4, c.size);
318 } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
319 changed = true;
320 switch (int(c.value.userType())) {
321 case QMetaType::QColor: {
322 const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value)).toRgb();
323 const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
324 fillUniformBlockMember<float>(dst, f, 4, c.size);
325 break;
326 }
327 case QMetaType::Float: {
328 const float f = qvariant_cast<float>(c.value);
329 fillUniformBlockMember<float>(dst, &f, 1, c.size);
330 break;
331 }
332 case QMetaType::Double: {
333 const float f = float(qvariant_cast<double>(c.value));
334 fillUniformBlockMember<float>(dst, &f, 1, c.size);
335 break;
336 }
337 case QMetaType::Int: {
338 const qint32 i = c.value.toInt();
339 fillUniformBlockMember<qint32>(dst, &i, 1, c.size);
340 break;
341 }
342 case QMetaType::Bool: {
343 const qint32 b = c.value.toBool();
344 fillUniformBlockMember<qint32>(dst, &b, 1, c.size);
345 break;
346 }
347 case QMetaType::QTransform: { // mat3
348 const QTransform v = qvariant_cast<QTransform>(c.value);
349 const float m[3][3] = {
350 { float(v.m11()), float(v.m12()), float(v.m13()) },
351 { float(v.m21()), float(v.m22()), float(v.m23()) },
352 { float(v.m31()), float(v.m32()), float(v.m33()) }
353 };
354 // stored as 4 floats per column, 1 unused
355 memset(dst, 0, c.size);
356 const size_t bytesPerColumn = 4 * sizeof(float);
357 if (c.size >= bytesPerColumn)
358 fillUniformBlockMember<float>(dst, m[0], 3, 3 * sizeof(float));
359 if (c.size >= 2 * bytesPerColumn)
360 fillUniformBlockMember<float>(dst + bytesPerColumn, m[1], 3, 3 * sizeof(float));
361 if (c.size >= 3 * bytesPerColumn)
362 fillUniformBlockMember<float>(dst + 2 * bytesPerColumn, m[2], 3, 3 * sizeof(float));
363 break;
364 }
365 case QMetaType::QSize:
366 case QMetaType::QSizeF: { // vec2
367 const QSizeF v = c.value.toSizeF();
368 const float f[2] = { float(v.width()), float(v.height()) };
369 fillUniformBlockMember<float>(dst, f, 2, c.size);
370 break;
371 }
372 case QMetaType::QPoint:
373 case QMetaType::QPointF: { // vec2
374 const QPointF v = c.value.toPointF();
375 const float f[2] = { float(v.x()), float(v.y()) };
376 fillUniformBlockMember<float>(dst, f, 2, c.size);
377 break;
378 }
379 case QMetaType::QRect:
380 case QMetaType::QRectF: { // vec4
381 const QRectF v = c.value.toRectF();
382 const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
383 fillUniformBlockMember<float>(dst, f, 4, c.size);
384 break;
385 }
386 case QMetaType::QVector2D: { // vec2
387 const QVector2D v = qvariant_cast<QVector2D>(c.value);
388 const float f[2] = { float(v.x()), float(v.y()) };
389 fillUniformBlockMember<float>(dst, f, 2, c.size);
390 break;
391 }
392 case QMetaType::QVector3D: { // vec3
393 const QVector3D v = qvariant_cast<QVector3D>(c.value);
394 const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
395 fillUniformBlockMember<float>(dst, f, 3, c.size);
396 break;
397 }
398 case QMetaType::QVector4D: { // vec4
399 const QVector4D v = qvariant_cast<QVector4D>(c.value);
400 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
401 fillUniformBlockMember<float>(dst, f, 4, c.size);
402 break;
403 }
404 case QMetaType::QQuaternion: { // vec4
405 const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
406 const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
407 fillUniformBlockMember<float>(dst, f, 4, c.size);
408 break;
409 }
410 case QMetaType::QMatrix4x4: { // mat4
411 const QMatrix4x4 m = qvariant_cast<QMatrix4x4>(c.value);
412 fillUniformBlockMember<float>(dst, m.constData(), 16, c.size);
413 break;
414 }
415 default:
416 break;
417 }
418 }
419 }
420
421 return changed;
422}
423
425 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
426{
427 Q_UNUSED(oldMaterial);
428 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
429
431 return;
432
433 QSGTextureProvider *tp = mat->m_textureProviders.at(binding);
434 if (tp) {
435 if (QSGTexture *t = tp->texture()) {
436 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
437
438 if (t->isAtlasTexture() && !mat->m_geometryUsesTextureSubRect && !mat->usesSubRectUniform(binding)) {
439 // Why the hassle with the batch: while removedFromAtlas() is
440 // able to operate with its own resource update batch (which is
441 // then committed immediately), that approach is wrong when the
442 // atlas enqueued (in the updateRhiTexture() above) not yet
443 // committed operations to state.resourceUpdateBatch()... The
444 // only safe way then is to use the same batch the atlas'
445 // updateRhiTexture() used.
446 QSGTexture *newTexture = t->removedFromAtlas(state.resourceUpdateBatch());
447 if (newTexture) {
448 t = newTexture;
449 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
450 }
451 }
452 *texture = t;
453 return;
454 }
455 }
456
457 if (!mat->m_dummyTexture) {
463 img.fill(0);
465 mat->m_dummyTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
466 }
467 *texture = mat->m_dummyTexture;
468}
469
471 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
472{
474 Q_UNUSED(oldMaterial);
475 QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
476
477 switch (mat->m_cullMode) {
479 ps->cullMode = GraphicsPipelineState::CullFront;
480 return true;
482 ps->cullMode = GraphicsPipelineState::CullBack;
483 return true;
484 default:
485 return false;
486 }
487}
488
490 : m_node(node)
491{
492 setFlag(Blending | RequiresFullMatrix, true); // may be changed in syncMaterial()
493}
494
496{
499
500 delete m_dummyTexture;
501}
502
503static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
504{
505 for (QSGTextureProvider *tp : textureProviders) {
506 if (tp && tp->texture() && tp->texture()->isAtlasTexture())
507 return true;
508 }
509 return false;
510}
511
513{
514 Q_ASSERT(other && type() == other->type());
515 const QSGRhiShaderEffectMaterial *o = static_cast<const QSGRhiShaderEffectMaterial *>(other);
516
517 if (int diff = m_cullMode - o->m_cullMode)
518 return diff;
519
520 if (int diff = m_textureProviders.size() - o->m_textureProviders.size())
521 return diff;
522
523 if (m_linker.m_constants != o->m_linker.m_constants)
524 return 1;
525
527 return -1;
528
529 if (hasAtlasTexture(o->m_textureProviders) && !o->m_geometryUsesTextureSubRect)
530 return 1;
531
532 for (int binding = 0, count = m_textureProviders.size(); binding != count; ++binding) {
533 QSGTextureProvider *tp1 = m_textureProviders.at(binding);
534 QSGTextureProvider *tp2 = o->m_textureProviders.at(binding);
535 if (tp1 && tp2) {
536 QSGTexture *t1 = tp1->texture();
537 QSGTexture *t2 = tp2->texture();
538 if (t1 && t2) {
539 const qint64 diff = t1->comparisonKey() - t2->comparisonKey();
540 if (diff != 0)
541 return diff < 0 ? -1 : 1;
542 } else {
543 if (!t1 && t2)
544 return -1;
545 if (t1 && !t2)
546 return 1;
547 }
548 } else {
549 if (!tp1 && tp2)
550 return -1;
551 if (tp1 && !tp2)
552 return 1;
553 }
554 }
555
556 return 0;
557}
558
560{
561 return m_materialType;
562}
563
565{
566 Q_UNUSED(renderMode);
567 return new QSGRhiShaderEffectMaterialShader(this);
568}
569
571{
572 if (layoutChange) {
574 if (tp) {
575 QObject::disconnect(tp, SIGNAL(textureChanged()), m_node,
576 SLOT(handleTextureChange()));
577 QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), m_node,
578 SLOT(handleTextureProviderDestroyed(QObject*)));
579 }
580 }
581 m_textureProviders.fill(nullptr, MAX_BINDINGS);
582 }
583
584 for (auto it = m_linker.m_samplers.constBegin(), itEnd = m_linker.m_samplers.constEnd(); it != itEnd; ++it) {
585 const int binding = it.key();
586 QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
587 QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
588 if (binding >= MAX_BINDINGS) {
589 qWarning("Sampler at binding %d exceeds the available ShaderEffect binding slots; ignored",
590 binding);
591 continue;
592 }
593 QSGTextureProvider *&activeProvider(m_textureProviders[binding]);
594 if (newProvider != activeProvider) {
595 if (activeProvider) {
596 QObject::disconnect(activeProvider, SIGNAL(textureChanged()), m_node,
597 SLOT(handleTextureChange()));
598 QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), m_node,
599 SLOT(handleTextureProviderDestroyed(QObject*)));
600 }
601 if (newProvider) {
602 Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
603 "QSGRhiShaderEffectMaterial::updateTextureProviders",
604 "Texture provider must belong to the rendering thread");
605 QObject::connect(newProvider, SIGNAL(textureChanged()), m_node, SLOT(handleTextureChange()));
606 QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), m_node,
607 SLOT(handleTextureProviderDestroyed(QObject*)));
608 } else {
609 const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
610 qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
611 binding, typeName);
612 }
613 activeProvider = newProvider;
614 }
615 }
616}
617
619 : m_material(this)
620{
621 Q_UNUSED(rc);
622 setFlag(UsePreprocess, true);
623 setMaterial(&m_material);
624}
625
627{
628 QRectF srcRect(0, 0, 1, 1);
629 bool geometryUsesTextureSubRect = false;
630 if (supportsAtlasTextures) {
631 QSGTextureProvider *tp = nullptr;
632 for (int binding = 0, count = m_material.m_textureProviders.size(); binding != count; ++binding) {
633 if (QSGTextureProvider *candidate = m_material.m_textureProviders.at(binding)) {
634 if (!tp) {
635 tp = candidate;
636 } else { // there can only be one...
637 tp = nullptr;
638 break;
639 }
640 }
641 }
642 if (tp && tp->texture()) {
643 srcRect = tp->texture()->normalizedTextureSubRect();
644 geometryUsesTextureSubRect = true;
645 }
646 }
647
648 if (m_material.m_geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
649 m_material.m_geometryUsesTextureSubRect = geometryUsesTextureSubRect;
651 }
652
653 return srcRect;
654}
655
656static QShader loadShaderFromFile(const QString &filename)
657{
658 QFile f(filename);
659 if (!f.open(QIODevice::ReadOnly)) {
660 qWarning() << "Failed to find shader" << filename;
661 return QShader();
662 }
663 return QShader::fromSerialized(f.readAll());
664}
665
667{
668 static QShader defaultVertexShader;
669 static QShader defaultFragmentShader;
670
671 if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
672 m_material.setFlag(QSGMaterial::Blending, syncData->blending);
674 }
675
676 if (m_material.m_cullMode != syncData->cullMode) {
677 m_material.m_cullMode = syncData->cullMode;
679 }
680
682 if (m_material.m_materialType) {
684 m_material.m_fragmentShader);
685 }
686
687 m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
688 if (m_material.m_hasCustomVertexShader) {
689 m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
690 } else {
691 if (!defaultVertexShader.isValid())
692 defaultVertexShader = loadShaderFromFile(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"));
693 m_material.m_vertexShader = defaultVertexShader;
694 }
695
697 if (m_material.m_hasCustomFragmentShader) {
698 m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
699 } else {
700 if (!defaultFragmentShader.isValid())
701 defaultFragmentShader = loadShaderFromFile(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"));
702 m_material.m_fragmentShader = defaultFragmentShader;
703 }
704
705 m_material.m_materialType = shaderMaterialTypeCache[syncData->materialTypeCacheKey].ref(m_material.m_vertexShader,
706 m_material.m_fragmentShader);
707 m_material.m_materialTypeCacheKey = syncData->materialTypeCacheKey;
708
709 m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
710
711 if (m_material.m_hasCustomVertexShader) {
712 m_material.m_linker.feedConstants(*syncData->vertex.shader);
713 m_material.m_linker.feedSamplers(*syncData->vertex.shader);
714 } else {
716 defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect vertex shader");
717 defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
719
720 // { mat4 qt_Matrix; float qt_Opacity; } where only the matrix is used
722 v.name = QByteArrayLiteral("qt_Matrix");
723 v.offset = 0;
724 v.size = 16 * sizeof(float);
725 defaultSD.shaderInfo.variables.append(v);
728 defaultSD.varData.append(vd);
729 defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
730 m_material.m_linker.feedConstants(defaultSD);
731 }
732
733 if (m_material.m_hasCustomFragmentShader) {
734 m_material.m_linker.feedConstants(*syncData->fragment.shader);
735 m_material.m_linker.feedSamplers(*syncData->fragment.shader);
736 } else {
738 defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect fragment shader");
739 defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
741
742 // { mat4 qt_Matrix; float qt_Opacity; } where only the opacity is used
744 v.name = QByteArrayLiteral("qt_Opacity");
745 v.offset = 16 * sizeof(float);
746 v.size = sizeof(float);
747 defaultSD.shaderInfo.variables.append(v);
750 defaultSD.varData.append(vd);
751
752 v.name = QByteArrayLiteral("source");
753 v.bindPoint = 1;
755 defaultSD.shaderInfo.variables.append(v);
756 for (const QSGShaderEffectNode::VariableData &extVarData : std::as_const(syncData->fragment.shader->varData)) {
757 if (extVarData.specialType == QSGShaderEffectNode::VariableData::Source) {
758 vd.value = extVarData.value;
759 break;
760 }
761 }
763 defaultSD.varData.append(vd);
764
765 defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
766
767 m_material.m_linker.feedConstants(defaultSD);
768 m_material.m_linker.feedSamplers(defaultSD);
769 }
770
771 m_material.m_linker.linkTextureSubRects();
772 m_material.updateTextureProviders(true);
774
775 } else {
776
778 if (!syncData->vertex.dirtyConstants->isEmpty())
779 m_material.m_linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
780 if (!syncData->fragment.dirtyConstants->isEmpty())
781 m_material.m_linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
783 }
784
786 if (!syncData->vertex.dirtyTextures->isEmpty())
787 m_material.m_linker.feedSamplers(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
788 if (!syncData->fragment.dirtyTextures->isEmpty())
789 m_material.m_linker.feedSamplers(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
790 m_material.m_linker.linkTextureSubRects();
791 m_material.updateTextureProviders(false);
793 }
794 }
795
796 if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.m_hasCustomVertexShader) {
799 }
800}
801
802void QSGRhiShaderEffectNode::handleTextureChange()
803{
806}
807
808void QSGRhiShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
809{
810 for (QSGTextureProvider *&tp : m_material.m_textureProviders) {
811 if (tp == object)
812 tp = nullptr;
813 }
814}
815
817{
818 for (QSGTextureProvider *tp : m_material.m_textureProviders) {
819 if (tp) {
820 if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
821 texture->updateTexture();
822 }
823 }
824}
825
827{
828 return false; // because SPIR-V and QRhi make it look so, regardless of the underlying API
829}
830
832{
833 return QString();
834}
835
837{
838 return m_status;
839}
840
842{
843 if (!src.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || src.isLocalFile()) {
844 if (!m_fileSelector) {
845 m_fileSelector = new QFileSelector(this);
846 m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("qsb"));
847 }
848 const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(src));
849 const QShader s = loadShaderFromFile(fn);
850 if (!s.isValid()) {
851 qWarning("ShaderEffect: Failed to deserialize QShader from %s. "
852 "Either the filename is incorrect, or it is not a valid .qsb file. "
853 "In Qt 6 shaders must be preprocessed using the Qt Shader Tools infrastructure. "
854 "The vertexShader and fragmentShader properties are now URLs that are expected to point to .qsb files generated by the qsb tool. "
855 "See https://doc.qt.io/qt-6/qtshadertools-index.html for more information.",
856 qPrintable(fn));
857 m_status = Error;
858 emit shaderCodePrepared(false, typeHint, src, result);
860 return;
861 }
862 result->name = fn;
863 result->rhiShader = s;
864 const bool ok = reflect(result);
865 m_status = ok ? Compiled : Error;
866 emit shaderCodePrepared(ok, typeHint, src, result);
868 } else {
869 qWarning("rhi shader effect only supports files (qrc or local) at the moment");
870 emit shaderCodePrepared(false, typeHint, src, result);
871 }
872}
873
874bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
875{
876 switch (result->rhiShader.stage()) {
878 result->type = ShaderInfo::TypeVertex;
879 break;
881 result->type = ShaderInfo::TypeFragment;
882 break;
883 default:
884 result->type = ShaderInfo::TypeOther;
885 qWarning("Unsupported shader stage (%d)", result->rhiShader.stage());
886 return false;
887 }
888
889 const QShaderDescription desc = result->rhiShader.description();
890 result->constantDataSize = 0;
891
892 int ubufBinding = -1;
893 const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
894 const int ubufCount = ubufs.size();
895 for (int i = 0; i < ubufCount; ++i) {
896 const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
897 if (ubufBinding == -1 && ubuf.binding >= 0) {
898 ubufBinding = ubuf.binding;
899 result->constantDataSize = ubuf.size;
900 for (const QShaderDescription::BlockVariable &member : ubuf.members) {
901 ShaderInfo::Variable v;
902 v.type = ShaderInfo::Constant;
903 v.name = member.name;
904 v.offset = member.offset;
905 v.size = member.size;
906 result->variables.append(v);
907 }
908 } else {
909 qWarning("Uniform block %s (binding %d) ignored", ubuf.blockName.constData(),
910 ubuf.binding);
911 }
912 }
913
914 const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = desc.combinedImageSamplers();
915 const int samplerCount = combinedImageSamplers.size();
916 for (int i = 0; i < samplerCount; ++i) {
917 const QShaderDescription::InOutVariable &combinedImageSampler(combinedImageSamplers[i]);
918 ShaderInfo::Variable v;
919 v.type = ShaderInfo::Sampler;
920 v.name = combinedImageSampler.name;
921 v.bindPoint = combinedImageSampler.binding;
922 result->variables.append(v);
923 }
924
925 return true;
926}
927
929
930#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
QColor toRgb() const noexcept
Create and returns an RGB QColor based on this color.
Definition qcolor.cpp:2035
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
\inmodule QtCore
Definition qhash.h:818
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:1209
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:929
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:1205
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
T value(const Key &key) const noexcept
Definition qhash.h:1044
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:949
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
\inmodule QtCore\reentrant
Definition qpoint.h:214
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
The QQuaternion class represents a quaternion consisting of a vector and scalar.
Definition qquaternion.h:21
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:658
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:655
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:925
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:40
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:622
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
Definition qsgnode.cpp:584
void setImage(const QImage &image)
void commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override
Call this function to enqueue image upload operations to resourceUpdates, in case there are any pendi...
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)
bool usesSubRectUniform(int binding) const
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 QRectF normalizedTextureSubRect() const
Returns the rectangle inside textureSize() that this texture represents in normalized coordinates.
void setHorizontalWrapMode(WrapMode hwrap)
Sets the horizontal wrap mode to hwrap.
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.
virtual qint64 comparisonKey() const =0
Returns a key suitable for comparing textures.
void setVerticalWrapMode(WrapMode vwrap)
Sets the vertical wrap mode to vwrap.
Definition qset.h:18
bool isEmpty() const
Definition qset.h:52
void clear()
Definition qset.h:61
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtGui
Definition qshader.h:81
static QShader fromSerialized(const QByteArray &data)
Creates a new QShader instance from the given data.
Definition qshader.cpp:510
@ VertexStage
Definition qshader.h:84
@ FragmentStage
Definition qshader.h:88
bool isValid() const
Definition qshader.cpp:313
\inmodule QtCore
Definition qsize.h:207
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QThread * currentThread()
Definition qthread.cpp:966
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:64
T value() const &
Definition qvariant.h:511
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
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
const char * typeName
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
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
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]
GLdouble s
[6]
Definition qopenglext.h:235
QCborArray members(const QCborMap *classDef, QLatin1StringView key, QTypeRevision maxMajorVersion, Postprocess &&process)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
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:1391
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)
@ desc
int qint32
Definition qtypes.h:44
long long qint64
Definition qtypes.h:55
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]
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.
Key(const QShader &vs, const QShader &fs)
void unref(const QShader &vs, const QShader &fs)
QSGMaterialType * ref(const QShader &vs, const QShader &fs)
QVector< QSGMaterialType * > m_graveyard
QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo
\variable QShaderDescription::InOutVariable::name
\variable QShaderDescription::BlockVariable::name