aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph')
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp12
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h7
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h10
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp11
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h11
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp3
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp4
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h6
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp179
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h23
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp39
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2356
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h253
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp1
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp572
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h98
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp565
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.h182
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h114
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp556
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h136
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h8
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialtype.h51
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp18
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp31
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h54
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp96
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h20
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp (renamed from src/quick/scenegraph/util/qsgtexture.cpp)343
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.h (renamed from src/quick/scenegraph/util/qsgtexture.h)21
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_p.h108
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp29
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h42
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode.cpp4
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp50
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h28
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp114
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h6
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp13
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp490
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.h5
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h23
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode.cpp61
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode_p.h5
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp52
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp194
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h63
-rw-r--r--src/quick/scenegraph/qsgdefaultspritenode.cpp77
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp16
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp415
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h3
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h6
-rw-r--r--src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp (renamed from src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp)44
-rw-r--r--src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h (renamed from src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h)13
-rw-r--r--src/quick/scenegraph/qsgopengllayer.cpp (renamed from src/quick/scenegraph/qsgdefaultlayer.cpp)58
-rw-r--r--src/quick/scenegraph/qsgopengllayer_p.h (renamed from src/quick/scenegraph/qsgdefaultlayer_p.h)22
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp390
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h13
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp568
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h125
-rw-r--r--src/quick/scenegraph/qsgrhilayer.cpp471
-rw-r--r--src/quick/scenegraph/qsgrhilayer_p.h143
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp886
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode_p.h163
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp567
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h165
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp269
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h98
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp474
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h5
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp26
-rw-r--r--src/quick/scenegraph/scenegraph.pri46
-rw-r--r--src/quick/scenegraph/scenegraph.qrc49
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsbbin0 -> 1873 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsbbin0 -> 1754 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsbbin0 -> 1753 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsbbin0 -> 1280 bytes
-rwxr-xr-xsrc/quick/scenegraph/shaders_ng/compile.bat84
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag25
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsbbin0 -> 2209 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert26
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsbbin0 -> 2209 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag25
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsbbin0 -> 2210 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsbbin0 -> 2319 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsbbin0 -> 2340 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsbbin0 -> 2334 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.frag20
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsbbin0 -> 1883 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.vert22
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsbbin0 -> 2048 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag20
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsbbin0 -> 1883 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.frag.qsbbin0 -> 1149 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.vert15
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.vert.qsbbin0 -> 1597 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag40
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsbbin0 -> 3314 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert44
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsbbin0 -> 3675 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag40
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsbbin0 -> 3304 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag29
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsbbin0 -> 2411 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert37
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsbbin0 -> 3028 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag29
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsbbin0 -> 2412 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsbbin0 -> 1173 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.vert18
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsbbin0 -> 1791 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag33
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsbbin0 -> 2549 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert32
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsbbin0 -> 2728 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag33
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsbbin0 -> 1859 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.frag16
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.frag.qsbbin0 -> 1622 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.vert.qsbbin0 -> 1860 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.frag9
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsbbin0 -> 838 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.vert51
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsbbin0 -> 3682 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsbbin0 -> 1299 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.vert61
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsbbin0 -> 4350 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.frag22
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.frag.qsbbin0 -> 1908 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.vert29
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.vert.qsbbin0 -> 2437 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.frag8
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.frag.qsbbin0 -> 853 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.vert14
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.vert.qsbbin0 -> 1510 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag.qsbbin0 -> 2210 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert.qsbbin0 -> 2460 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsbbin0 -> 1631 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag.qsbbin0 -> 1873 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert21
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert.qsbbin0 -> 2142 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.frag16
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.frag.qsbbin0 -> 1618 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.vert.qsbbin0 -> 1865 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.frag9
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsbbin0 -> 851 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsbbin0 -> 1837 bytes
-rw-r--r--src/quick/scenegraph/util/qsgdefaultimagenode.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgdefaultimagenode_p.h1
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode.cpp24
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h11
-rw-r--r--src/quick/scenegraph/util/qsgengine.cpp21
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp55
-rw-r--r--src/quick/scenegraph/util/qsgopenglatlastexture.cpp (renamed from src/quick/scenegraph/util/qsgatlastexture.cpp)21
-rw-r--r--src/quick/scenegraph/util/qsgopenglatlastexture_p.h (renamed from src/quick/scenegraph/util/qsgatlastexture_p.h)10
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp425
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture_p.h (renamed from src/quick/scenegraph/util/qsgtexture_p.h)56
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture.cpp490
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture_p.h217
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.cpp5
-rw-r--r--src/quick/scenegraph/util/qsgsimplerectnode.cpp8
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp8
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp119
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial_p.h23
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp54
191 files changed, 13066 insertions, 1867 deletions
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index 2e5fdbbe6b..ca620965a8 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -77,11 +77,6 @@ QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *
return m_nodes.value(node, nullptr);
}
-const QLinkedList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const
-{
- return m_renderableNodes;
-}
-
void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
{
m_nodes.insert(node, renderableNode);
@@ -238,12 +233,13 @@ void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
renderableNode(m_background)->markMaterialDirty();
}
-void QSGAbstractSoftwareRenderer::setBackgroundRect(const QRect &rect)
+void QSGAbstractSoftwareRenderer::setBackgroundRect(const QRect &rect, qreal devicePixelRatio)
{
- if (m_background->rect().toRect() == rect)
+ if (m_background->rect().toRect() == rect && m_devicePixelRatio == devicePixelRatio)
return;
m_background->setRect(rect);
- renderableNode(m_background)->markGeometryDirty();
+ m_devicePixelRatio = devicePixelRatio;
+ renderableNode(m_background)->markGeometryDirty();
// Invalidate the whole scene when the background is resized
markDirty();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index 99204ef25e..e1b477ab97 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -54,7 +54,6 @@
#include <private/qsgrenderer_p.h>
#include <QtCore/QHash>
-#include <QtCore/QLinkedList>
QT_BEGIN_NAMESPACE
@@ -83,12 +82,11 @@ protected:
QRegion optimizeRenderList();
void setBackgroundColor(const QColor &color);
- void setBackgroundRect(const QRect &rect);
+ void setBackgroundRect(const QRect &rect, qreal devicePixelRatio);
QColor backgroundColor();
QRect backgroundRect();
// only known after calling optimizeRenderList()
bool isOpaque() const { return m_isOpaque; }
- const QLinkedList<QSGSoftwareRenderableNode*> &renderableNodes() const;
private:
void nodeAdded(QSGNode *node);
@@ -99,12 +97,13 @@ private:
void nodeOpacityUpdated(QSGNode *node);
QHash<QSGNode*, QSGSoftwareRenderableNode*> m_nodes;
- QLinkedList<QSGSoftwareRenderableNode*> m_renderableNodes;
+ QVector<QSGSoftwareRenderableNode*> m_renderableNodes;
QSGSimpleRectNode *m_background;
QRegion m_dirtyRegion;
QRegion m_obscuredRegion;
+ qreal m_devicePixelRatio = 1;
bool m_isOpaque = false;
QSGSoftwareRenderableNodeUpdater *m_nodeUpdater;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 5b5bf005d8..7b5ee66df6 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -99,8 +99,9 @@ QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode()
return new QSGSoftwareInternalRectangleNode();
}
-QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode()
+QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode(QSGRenderContext *renderContext)
{
+ Q_UNUSED(renderContext);
return new QSGSoftwareInternalImageNode();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
index 1f14717416..84f468ce33 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -89,7 +89,7 @@ public:
QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
index 70378d2950..683ccb60c2 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
@@ -45,7 +45,8 @@
QT_BEGIN_NAMESPACE
QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext)
- : m_item(nullptr)
+ : QSGLayer(*(new QSGSoftwareLayerPrivate))
+ , m_item(nullptr)
, m_context(renderContext)
, m_renderer(nullptr)
, m_device_pixel_ratio(1)
@@ -69,6 +70,11 @@ int QSGSoftwareLayer::textureId() const
return 0;
}
+int QSGSoftwareLayerPrivate::comparisonKey() const
+{
+ return 0;
+}
+
QSize QSGSoftwareLayer::textureSize() const
{
return m_pixmap.size();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
index 9f5a22e66f..1859e14514 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
@@ -53,13 +53,16 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
QT_BEGIN_NAMESPACE
class QSGSoftwarePixmapRenderer;
+class QSGSoftwareLayerPrivate;
class QSGSoftwareLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGSoftwareLayer)
Q_OBJECT
public:
QSGSoftwareLayer(QSGRenderContext *renderContext);
@@ -117,6 +120,13 @@ private:
bool m_dirtyTexture;
};
+class QSGSoftwareLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGSoftwareLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGSOFTWARELAYER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
index bb4afc8301..4a6d73ec59 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -79,7 +79,7 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target)
QElapsedTimer renderTimer;
// Setup background item
- setBackgroundRect(m_projectionRect.normalized());
+ setBackgroundRect(m_projectionRect.normalized(), qreal(1));
setBackgroundColor(clearColor());
renderTimer.start();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
index 16e3a111ae..82a48d80ca 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
@@ -38,10 +38,12 @@
****************************************************************************/
#include "qsgsoftwarepixmaptexture_p.h"
+#include <private/qsgcontext_p.h>
QT_BEGIN_NAMESPACE
QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint flags)
+ : QSGTexture(*(new QSGSoftwarePixmapTexturePrivate))
{
// Prevent pixmap format conversion to reduce memory consumption
// and surprises in calling code. (See QTBUG-47328)
@@ -55,11 +57,11 @@ QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint fla
}
QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QPixmap &pixmap)
- : m_pixmap(pixmap)
+ : QSGTexture(*(new QSGSoftwarePixmapTexturePrivate)),
+ m_pixmap(pixmap)
{
}
-
int QSGSoftwarePixmapTexture::textureId() const
{
return 0;
@@ -85,6 +87,11 @@ void QSGSoftwarePixmapTexture::bind()
Q_UNREACHABLE();
}
+int QSGSoftwarePixmapTexturePrivate::comparisonKey() const
+{
+ return 0;
+}
+
QT_END_NAMESPACE
#include "moc_qsgsoftwarepixmaptexture_p.cpp"
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
index 034fa25da9..baa62d93de 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
@@ -52,12 +52,16 @@
//
#include <private/qsgtexture_p.h>
+#include <QtGui/QPixmap>
QT_BEGIN_NAMESPACE
+class QSGSoftwarePixmapTexturePrivate;
+
class QSGSoftwarePixmapTexture : public QSGTexture
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGSoftwarePixmapTexture)
public:
QSGSoftwarePixmapTexture(const QImage &image, uint flags);
QSGSoftwarePixmapTexture(const QPixmap &pixmap);
@@ -74,6 +78,13 @@ private:
QPixmap m_pixmap;
};
+class QSGSoftwarePixmapTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGSoftwarePixmapTexture)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGSOFTWAREPIXMAPTEXTURE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index 20286a03d5..141d8f3c6d 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -41,6 +41,7 @@
#include "qsgsoftwarelayer_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
#include "qsgsoftwareinternalimagenode_p.h"
+#include <private/qsgplaintexture_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index 7fb531cca3..95c7efd4cb 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -52,7 +52,7 @@
#include <qsgsimplerectnode.h>
#include <qsgsimpletexturenode.h>
#include <private/qsgrendernode_p.h>
-#include <private/qsgtexture_p.h>
+#include <private/qsgplaintexture_p.h>
#include <qmath.h>
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
index e9ed52d428..df129cbfee 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -114,7 +114,8 @@ void QSGSoftwareRenderer::render()
setBackgroundColor(clearColor());
setBackgroundRect(QRect(0, 0,
m_paintDevice->width() / m_paintDevice->devicePixelRatioF(),
- m_paintDevice->height() / m_paintDevice->devicePixelRatioF()));
+ m_paintDevice->height() / m_paintDevice->devicePixelRatioF()),
+ m_paintDevice->devicePixelRatioF());
// Build Renderlist
// The renderlist is created by visiting each node in the tree and when a
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
index 796bc870de..d3dc4978f2 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
@@ -65,7 +65,7 @@ namespace QSGCompressedAtlasTexture
{
Atlas::Atlas(const QSize &size, uint format)
- : QSGAtlasTexture::AtlasBase(size)
+ : QSGOpenGLAtlasTexture::AtlasBase(size)
, m_format(format)
{
}
@@ -117,7 +117,7 @@ void Atlas::uploadPendingTexture(int i)
}
Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size)
- : QSGAtlasTexture::TextureBase(atlas, textureRect)
+ : QSGOpenGLAtlasTexture::TextureBase(atlas, textureRect)
, m_nonatlas_texture(nullptr)
, m_data(data)
, m_size(size)
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
index 59e935b623..78051778f5 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
@@ -57,7 +57,7 @@
#include <QtQuick/QSGTexture>
#include <QtQuick/private/qsgareaallocator_p.h>
-#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgopenglatlastexture_p.h>
QT_BEGIN_NAMESPACE
@@ -67,7 +67,7 @@ namespace QSGCompressedAtlasTexture {
class Texture;
-class Atlas : public QSGAtlasTexture::AtlasBase
+class Atlas : public QSGOpenGLAtlasTexture::AtlasBase
{
public:
Atlas(const QSize &size, uint format);
@@ -84,7 +84,7 @@ private:
uint m_format;
};
-class Texture : public QSGAtlasTexture::TextureBase
+class Texture : public QSGOpenGLAtlasTexture::TextureBase
{
Q_OBJECT
public:
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index d3310bc11c..1a8bddaa6e 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -44,13 +44,15 @@
#include <QOpenGLFunctions>
#include <QDebug>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO, "qt.scenegraph.textureio");
QSGCompressedTexture::QSGCompressedTexture(const QTextureFileData &texData)
- : m_textureData(texData)
+ : QSGTexture(*(new QSGCompressedTexturePrivate)),
+ m_textureData(texData)
{
m_size = m_textureData.size();
m_hasAlpha = !formatIsOpaque(m_textureData.glInternalFormat());
@@ -68,6 +70,8 @@ QSGCompressedTexture::~QSGCompressedTexture()
funcs->glDeleteTextures(1, &m_textureId);
}
#endif
+
+ delete m_texture;
}
int QSGCompressedTexture::textureId() const
@@ -85,6 +89,20 @@ int QSGCompressedTexture::textureId() const
return m_textureId;
}
+int QSGCompressedTexturePrivate::comparisonKey() const
+{
+ Q_Q(const QSGCompressedTexture);
+ // not textureId() as that would create an id when not yet done - that's not wanted here
+ if (q->m_textureId)
+ return q->m_textureId;
+
+ if (q->m_texture)
+ return int(qintptr(q->m_texture));
+
+ // two textures (and so materials) with not-yet-created texture underneath are never equal
+ return int(qintptr(q));
+}
+
QSize QSGCompressedTexture::textureSize() const
{
return m_size;
@@ -145,6 +163,165 @@ void QSGCompressedTexture::bind()
#endif // QT_CONFIG(opengl)
}
+static QPair<QRhiTexture::Format, bool> toRhiCompressedFormat(uint glinternalformat)
+{
+ switch (glinternalformat) {
+ case QOpenGLTexture::RGB_DXT1:
+ return { QRhiTexture::BC1, false };
+ case QOpenGLTexture::SRGB_DXT1:
+ return { QRhiTexture::BC1, true };
+
+ case QOpenGLTexture::RGBA_DXT3:
+ return { QRhiTexture::BC3, false };
+ case QOpenGLTexture::SRGB_Alpha_DXT3:
+ return { QRhiTexture::BC3, true };
+
+ case QOpenGLTexture::RGBA_DXT5:
+ return { QRhiTexture::BC5, false };
+ case QOpenGLTexture::SRGB_Alpha_DXT5:
+ return { QRhiTexture::BC5, true };
+
+ case QOpenGLTexture::RGB8_ETC2:
+ return { QRhiTexture::ETC2_RGB8, false };
+ case QOpenGLTexture::SRGB8_ETC2:
+ return { QRhiTexture::ETC2_RGB8, true };
+
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ return { QRhiTexture::ETC2_RGB8A1, false };
+ case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return { QRhiTexture::ETC2_RGB8A1, true };
+
+ case QOpenGLTexture::RGBA8_ETC2_EAC:
+ return { QRhiTexture::ETC2_RGBA8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC:
+ return { QRhiTexture::ETC2_RGBA8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_4x4:
+ return { QRhiTexture::ASTC_4x4, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4:
+ return { QRhiTexture::ASTC_4x4, true };
+
+ case QOpenGLTexture::RGBA_ASTC_5x4:
+ return { QRhiTexture::ASTC_5x4, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4:
+ return { QRhiTexture::ASTC_5x4, true };
+
+ case QOpenGLTexture::RGBA_ASTC_5x5:
+ return { QRhiTexture::ASTC_5x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5:
+ return { QRhiTexture::ASTC_5x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_6x5:
+ return { QRhiTexture::ASTC_6x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5:
+ return { QRhiTexture::ASTC_6x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_6x6:
+ return { QRhiTexture::ASTC_6x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6:
+ return { QRhiTexture::ASTC_6x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x5:
+ return { QRhiTexture::ASTC_8x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5:
+ return { QRhiTexture::ASTC_8x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x6:
+ return { QRhiTexture::ASTC_8x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6:
+ return { QRhiTexture::ASTC_8x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x8:
+ return { QRhiTexture::ASTC_8x8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8:
+ return { QRhiTexture::ASTC_8x8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x5:
+ return { QRhiTexture::ASTC_10x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5:
+ return { QRhiTexture::ASTC_10x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x6:
+ return { QRhiTexture::ASTC_10x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6:
+ return { QRhiTexture::ASTC_10x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x8:
+ return { QRhiTexture::ASTC_10x8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8:
+ return { QRhiTexture::ASTC_10x8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x10:
+ return { QRhiTexture::ASTC_10x10, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10:
+ return { QRhiTexture::ASTC_10x10, true };
+
+ case QOpenGLTexture::RGBA_ASTC_12x10:
+ return { QRhiTexture::ASTC_12x10, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10:
+ return { QRhiTexture::ASTC_12x10, true };
+
+ case QOpenGLTexture::RGBA_ASTC_12x12:
+ return { QRhiTexture::ASTC_12x12, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12:
+ return { QRhiTexture::ASTC_12x12, true };
+
+ default:
+ return { QRhiTexture::UnknownFormat, false };
+ }
+}
+
+QRhiTexture *QSGCompressedTexturePrivate::rhiTexture() const
+{
+ Q_Q(const QSGCompressedTexture);
+ return q->m_texture;
+}
+
+void QSGCompressedTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGCompressedTexture);
+ if (q->m_uploaded)
+ return;
+
+ q->m_uploaded = true; // even if fails, no point in trying again
+
+ if (!q->m_textureData.isValid()) {
+ qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", q->m_textureData.logName().constData());
+ return;
+ }
+
+ const QPair<QRhiTexture::Format, bool> fmt = toRhiCompressedFormat(q->m_textureData.glInternalFormat());
+ if (fmt.first == QRhiTexture::UnknownFormat) {
+ qWarning("Unknown compressed format 0x%x", q->m_textureData.glInternalFormat());
+ return;
+ }
+
+ QRhiTexture::Flags texFlags = 0;
+ if (fmt.second)
+ texFlags |= QRhiTexture::sRGB;
+
+ if (!rhi->isTextureFormatSupported(fmt.first, texFlags)) {
+ qWarning("Unsupported compressed format 0x%x", q->m_textureData.glInternalFormat());
+ return;
+ }
+
+ if (!q->m_texture) {
+ q->m_texture = rhi->newTexture(fmt.first, q->m_size, 1, texFlags);
+ if (!q->m_texture->build()) {
+ qWarning("Failed to create QRhiTexture for compressed data");
+ delete q->m_texture;
+ q->m_texture = nullptr;
+ return;
+ }
+ }
+
+ // only upload mip level 0 since we never do mipmapping for compressed textures (for now?)
+ resourceUpdates->uploadTexture(q->m_texture, QRhiTextureUploadEntry(0, 0,
+ { q->m_textureData.data().constData() + q->m_textureData.dataOffset(), q->m_textureData.dataLength() }));
+
+ q->m_textureData = QTextureFileData(); // Release this memory, not needed anymore
+}
+
QTextureFileData QSGCompressedTexture::textureData() const
{
return m_textureData;
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
index c3b58a2389..b858d8ddee 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
@@ -52,25 +52,28 @@
//
#include <private/qtexturefiledata_p.h>
-#include <QSGTexture>
-#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include <QQuickTextureFactory>
#include <QOpenGLFunctions>
QT_BEGIN_NAMESPACE
+class QSGCompressedTexturePrivate;
+
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture
{
+ Q_DECLARE_PRIVATE(QSGCompressedTexture)
Q_OBJECT
public:
QSGCompressedTexture(const QTextureFileData& texData);
virtual ~QSGCompressedTexture();
- int textureId() const override;
QSize textureSize() const override;
bool hasAlphaChannel() const override;
bool hasMipmaps() const override;
+ int textureId() const override;
void bind() override;
QTextureFileData textureData() const;
@@ -81,14 +84,24 @@ protected:
QTextureFileData m_textureData;
QSize m_size;
mutable uint m_textureId = 0;
+ QRhiTexture *m_texture = nullptr;
bool m_hasAlpha = false;
bool m_uploaded = false;
};
-namespace QSGAtlasTexture {
+namespace QSGOpenGLAtlasTexture {
class Manager;
}
+class QSGCompressedTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGCompressedTexture)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory
{
public:
@@ -101,7 +114,7 @@ protected:
QTextureFileData m_textureData;
private:
- friend class QSGAtlasTexture::Manager;
+ friend class QSGOpenGLAtlasTexture::Manager;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
index fddac7ed71..679ad1d445 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -224,18 +224,33 @@ QRect QSGAbstractRenderer::viewportRect() const
Convenience method that calls setProjectionMatrix() with an
orthographic matrix generated from \a rect.
+ \a flipY must be \c true when the graphics API uses Y down in its
+ normalized device coordinate system (for example, Vulkan), \c false
+ otherwise.
+
\sa setProjectionMatrix(), projectionMatrix()
*/
-void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
+void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect, bool flipY)
{
QMatrix4x4 matrix;
matrix.ortho(rect.x(),
rect.x() + rect.width(),
- rect.y() + rect.height(),
- rect.y(),
+ flipY ? rect.y() : rect.y() + rect.height(),
+ flipY ? rect.y() + rect.height() : rect.y(),
1,
-1);
setProjectionMatrix(matrix);
+
+ if (flipY) {
+ matrix.setToIdentity();
+ matrix.ortho(rect.x(),
+ rect.x() + rect.width(),
+ rect.y() + rect.height(),
+ rect.y(),
+ 1,
+ -1);
+ }
+ setProjectionMatrixWithNativeNDC(matrix);
}
/*!
@@ -250,6 +265,15 @@ void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
}
/*!
+ \internal
+ */
+void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix)
+{
+ Q_D(QSGAbstractRenderer);
+ d->m_projection_matrix_native_ndc = matrix;
+}
+
+/*!
Returns the projection matrix
\sa setProjectionMatrix(), setProjectionMatrixToRect()
@@ -261,6 +285,15 @@ QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const
}
/*!
+ \internal
+ */
+QMatrix4x4 QSGAbstractRenderer::projectionMatrixWithNativeNDC() const
+{
+ Q_D(const QSGAbstractRenderer);
+ return d->m_projection_matrix_native_ndc;
+}
+
+/*!
Use \a color to clear the framebuffer when clearMode() is
set to QSGAbstractRenderer::ClearColorBuffer.
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
index b9805f9db6..08e600e0b8 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -75,9 +75,11 @@ public:
inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); }
QRect viewportRect() const;
- void setProjectionMatrixToRect(const QRectF &rect);
+ void setProjectionMatrixToRect(const QRectF &rect, bool flipY = false);
void setProjectionMatrix(const QMatrix4x4 &matrix);
+ void setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix);
QMatrix4x4 projectionMatrix() const;
+ QMatrix4x4 projectionMatrixWithNativeNDC() const;
void setClearColor(const QColor &color);
QColor clearColor() const;
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
index e6b17f4448..bbc4289b2c 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
@@ -78,6 +78,7 @@ public:
QRect m_viewport_rect;
QMatrix4x4 m_projection_matrix;
+ QMatrix4x4 m_projection_matrix_native_ndc;
uint m_mirrored : 1;
};
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index afe3380494..b5437ffb7e 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
** Contact: https://www.qt.io/licensing/
@@ -57,7 +57,7 @@
#include <private/qnumeric_p.h>
#include <private/qquickprofiler_p.h>
-#include "qsgmaterialshader_p.h"
+#include "qsgmaterialrhishader_p.h"
#include <algorithm>
@@ -67,6 +67,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
+#endif
+
extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
int qt_sg_envInt(const char *name, int defaultValue);
@@ -108,8 +112,8 @@ static inline int size_of_type(GLenum type)
4,
sizeof(double)
};
- Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
- return sizes[type - GL_BYTE];
+ Q_ASSERT(type >= QSGGeometry::ByteType && type <= QSGGeometry::DoubleType);
+ return sizes[type - QSGGeometry::ByteType];
}
bool qsg_sort_element_increasing_order(Element *a, Element *b) { return a->order < b->order; }
@@ -132,44 +136,180 @@ struct QMatrix4x4_Accessor
const float OPAQUE_LIMIT = 0.999f;
-ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
+const uint DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD = 4;
+const int VERTEX_BUFFER_BINDING = 0;
+const int ZORDER_BUFFER_BINDING = VERTEX_BUFFER_BINDING + 1;
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static inline QRhiVertexInputAttribute::Format vertexInputFormat(const QSGGeometry::Attribute &a)
+{
+ switch (a.type) {
+ case QSGGeometry::FloatType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::Float4;
+ if (a.tupleSize == 3)
+ return QRhiVertexInputAttribute::Float3;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::Float2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::Float;
+ break;
+ case QSGGeometry::UnsignedByteType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::UNormByte4;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::UNormByte2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::UNormByte;
+ break;
+ default:
+ break;
+ }
+ qWarning("Unsupported attribute type 0x%x with %d components", a.type, a.tupleSize);
+ Q_UNREACHABLE();
+ return QRhiVertexInputAttribute::Float;
+}
+
+static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShader *s, const QSGGeometry *geometry, bool batchable)
+{
+ Q_ASSERT(geometry);
+ const QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ if (!sd->vertexShader) {
+ qWarning("No vertex shader in QSGMaterialRhiShader %p", s);
+ return QRhiVertexInputLayout();
+ }
+
+ const int attrCount = geometry->attributeCount();
+ QVector<QRhiVertexInputAttribute> inputAttributes;
+ inputAttributes.reserve(attrCount + 1);
+ int offset = 0;
+ for (int i = 0; i < attrCount; ++i) {
+ const QSGGeometry::Attribute &a = geometry->attributes()[i];
+ if (!sd->vertexShader->vertexInputLocations.contains(a.position)) {
+ qWarning("Vertex input %d is present in material but not in shader. This is wrong.",
+ a.position);
+ }
+ inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, vertexInputFormat(a), offset));
+ offset += a.tupleSize * size_of_type(a.type);
+ }
+ if (batchable) {
+ inputAttributes.append(QRhiVertexInputAttribute(ZORDER_BUFFER_BINDING, sd->vertexShader->qt_order_attrib_location,
+ QRhiVertexInputAttribute::Float, 0));
+ }
+
+ Q_ASSERT(VERTEX_BUFFER_BINDING == 0 && ZORDER_BUFFER_BINDING == 1); // not very flexible
+ QVector<QRhiVertexInputBinding> inputBindings;
+ inputBindings.reserve(2);
+ inputBindings.append(QRhiVertexInputBinding(geometry->sizeOfVertex()));
+ if (batchable)
+ inputBindings.append(QRhiVertexInputBinding(sizeof(float)));
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings(inputBindings);
+ inputLayout.setAttributes(inputAttributes);
+
+ return inputLayout;
+}
+
+static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geometry)
+{
+ switch (geometry->indexType()) {
+ case QSGGeometry::UnsignedShortType:
+ return QRhiCommandBuffer::IndexUInt16;
+ break;
+ case QSGGeometry::UnsignedIntType:
+ return QRhiCommandBuffer::IndexUInt32;
+ break;
+ default:
+ Q_UNREACHABLE();
+ return QRhiCommandBuffer::IndexUInt16;
+ }
+}
+
+static inline QRhiGraphicsPipeline::Topology gpTopology(int geomDrawMode)
+{
+ QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
+ switch (geomDrawMode) {
+ case QSGGeometry::DrawPoints:
+ topology = QRhiGraphicsPipeline::Points;
+ break;
+ case QSGGeometry::DrawLines:
+ topology = QRhiGraphicsPipeline::Lines;
+ break;
+ case QSGGeometry::DrawLineStrip:
+ topology = QRhiGraphicsPipeline::LineStrip;
+ break;
+ case QSGGeometry::DrawTriangles:
+ topology = QRhiGraphicsPipeline::Triangles;
+ break;
+ case QSGGeometry::DrawTriangleStrip:
+ topology = QRhiGraphicsPipeline::TriangleStrip;
+ break;
+ default:
+ qWarning("Primitive topology 0x%x not supported", geomDrawMode);
+ break;
+ }
+ return topology;
+}
+
+ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = rewrittenShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = material->createShader();
- QOpenGLContext *ctx = context->openglContext();
- QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
-
- QOpenGLShaderProgram *p = s->program();
- char const *const *attr = s->attributeNames();
- int i;
- for (i = 0; attr[i]; ++i) {
- if (*attr[i])
- p->bindAttributeLocation(attr[i], i);
- }
- p->bindAttributeLocation("_qt_order", i);
- context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
- context->initializeShader(s);
- if (!p->isLinked())
- return nullptr;
-
shader = new Shader;
- shader->program = s;
- shader->pos_order = i;
- shader->id_zRange = p->uniformLocation("_qt_zRange");
- shader->lastOpacity = 0;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::BatchableVertexShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, true);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ QOpenGLContext *ctx = context->openglContext();
+ QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
+ QOpenGLShaderProgram *p = s->program();
+ char const *const *attr = s->attributeNames();
+ int i;
+ for (i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ p->bindAttributeLocation(attr[i], i);
+ }
+ p->bindAttributeLocation("_qt_order", i);
+ context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
+ context->initializeShader(s);
+ if (!p->isLinked()) {
+ delete shader;
+ return nullptr;
+ }
+ shader->programGL.program = s;
+ shader->programGL.pos_order = i;
+ }
- Q_ASSERT(shader->pos_order >= 0);
- Q_ASSERT(shader->id_zRange >= 0);
+ shader->lastOpacity = 0;
- qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms", (int) qsg_renderer_timer.elapsed());
+ qCDebug(QSG_LOG_TIME_COMPILATION, "material shaders prepared in %dms", (int) qsg_renderer_timer.elapsed());
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
QQuickProfiler::SceneGraphContextMaterialCompile);
@@ -178,25 +318,43 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
return shader;
}
-ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material)
+ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = stockShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader());
- context->compileShader(s, material);
- context->initializeShader(s);
+ shader = new Shader;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::StandardShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, false);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ context->compileShader(s, material);
+ context->initializeShader(s);
+ shader->programGL.program = s;
+ shader->programGL.pos_order = -1;
+ }
- shader = new Shader();
- shader->program = s;
- shader->id_zRange = -1;
- shader->pos_order = -1;
shader->lastOpacity = 0;
stockShaders[type] = shader;
@@ -216,6 +374,45 @@ void ShaderManager::invalidated()
rewrittenShaders.clear();
delete blitProgram;
blitProgram = nullptr;
+
+ qDeleteAll(srbCache);
+ srbCache.clear();
+}
+
+void ShaderManager::clearCachedRendererData()
+{
+ for (ShaderManager::Shader *sms : stockShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+ for (ShaderManager::Shader *sms : rewrittenShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+}
+
+QRhiShaderResourceBindings *ShaderManager::srb(const QVector<QRhiShaderResourceBinding> &bindings)
+{
+ auto it = srbCache.constFind(bindings);
+ if (it != srbCache.constEnd())
+ return *it;
+
+ QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
+ srb->setBindings(bindings);
+ if (srb->build()) {
+ srbCache.insert(bindings, srb);
+ } else {
+ qWarning("Failed to build srb");
+ delete srb;
+ srb = nullptr;
+ }
+ return srb;
}
void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
@@ -248,13 +445,13 @@ void qsg_dumpShadowRoots(Node *n)
QByteArray ind(indent, ' ');
if (n->type() == QSGNode::ClipNodeType || n->isBatchRoot) {
- qDebug() << ind.constData() << "[X]" << n->sgNode << hex << uint(n->sgNode->flags());
+ qDebug() << ind.constData() << "[X]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
qsg_dumpShadowRoots(n->rootInfo(), indent);
} else {
QDebug d = qDebug();
- d << ind.constData() << "[ ]" << n->sgNode << hex << uint(n->sgNode->flags());
+ d << ind.constData() << "[ ]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
if (n->type() == QSGNode::GeometryNodeType)
- d << "order" << dec << n->element()->order;
+ d << "order" << Qt::dec << n->element()->order;
}
SHADOWNODE_TRAVERSE(n)
@@ -531,11 +728,12 @@ void Updater::updateRootTransforms(Node *node, Node *root, const QMatrix4x4 &com
}
}
-int qsg_positionAttribute(QSGGeometry *g) {
+int qsg_positionAttribute(QSGGeometry *g)
+{
int vaOffset = 0;
for (int a=0; a<g->attributeCount(); ++a) {
const QSGGeometry::Attribute &attr = g->attributes()[a];
- if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == GL_FLOAT) {
+ if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == QSGGeometry::FloatType) {
return vaOffset;
}
vaOffset += attr.tupleSize * size_of_type(attr.type);
@@ -773,15 +971,28 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
, m_currentStencilValue(0)
, m_clipMatrixId(0)
, m_currentClip(nullptr)
- , m_currentClipType(NoClip)
+ , m_currentClipType(ClipState::NoClip)
, m_vertexUploadPool(256)
, m_indexUploadPool(64)
, m_vao(nullptr)
, m_visualizeMode(VisualizeNothing)
{
- initializeOpenGLFunctions();
+ m_rhi = m_context->rhi();
+ if (m_rhi) {
+ m_ubufAlignment = m_rhi->ubufAlignment();
+ m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
+ if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
+ m_uint32IndexForRhi = true;
+ } else {
+ initializeOpenGLFunctions();
+ m_uint32IndexForRhi = false;
+ }
+
setNodeUpdater(new Updater(this));
+ // The shader manager is shared between renderers (think for example Item
+ // layers that create a new Renderer each) with the same rendercontext
+ // (i.e. QRhi or QOpenGLContext).
m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
@@ -810,20 +1021,29 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream")));
}
- // If rendering with an OpenGL Core profile context, we need to create a VAO
- // to hold our vertex specification state.
- if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
- m_vao = new QOpenGLVertexArrayObject(this);
- m_vao->create();
- }
+ if (!m_rhi) {
+ // If rendering with an OpenGL Core profile context, we need to create a VAO
+ // to hold our vertex specification state.
+ if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ m_vao = new QOpenGLVertexArrayObject(this);
+ m_vao->create();
+ }
- bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ }
}
static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
{
- funcs->glDeleteBuffers(1, &buffer->id);
+ if (buffer->buf) {
+ //qDebug("releasing rhibuf %p", buffer->buf);
+ delete buffer->buf;
+ }
+
+ if (buffer->id)
+ funcs->glDeleteBuffers(1, &buffer->id);
+
// The free here is ok because we're in one of two situations.
// 1. We're using the upload pool in which case unmap will have set the
// data pointer to 0 and calling free on 0 is ok.
@@ -837,12 +1057,14 @@ static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIn
qsg_wipeBuffer(&batch->vbo, funcs);
if (separateIndexBuffer)
qsg_wipeBuffer(&batch->ibo, funcs);
+ delete batch->ubuf;
+ batch->stencilClipState.reset();
delete batch;
}
Renderer::~Renderer()
{
- if (QOpenGLContext::currentContext()) {
+ if (m_rhi || QOpenGLContext::currentContext()) {
// Clean up batches and buffers
const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
@@ -864,6 +1086,35 @@ Renderer::~Renderer()
else
m_elementAllocator.release(e);
}
+
+ destroyGraphicsResources();
+}
+
+void Renderer::destroyGraphicsResources()
+{
+ // If this is from the dtor, then the shader manager and its already
+ // prepared shaders will stay around for other renderers -> the cached data
+ // in the rhi shaders have to be purged as it may refer to samplers we
+ // are going to destroy.
+ m_shaderManager->clearCachedRendererData();
+
+ qDeleteAll(m_pipelines);
+ qDeleteAll(m_samplers);
+
+ m_stencilClipCommon.reset();
+
+ delete m_dummyTexture;
+}
+
+void Renderer::releaseCachedResources()
+{
+ m_shaderManager->invalidated();
+
+ destroyGraphicsResources();
+
+ m_pipelines.clear();
+ m_samplers.clear();
+ m_dummyTexture = nullptr;
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
@@ -905,14 +1156,54 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
{
- if (buffer->id == 0)
- glGenBuffers(1, &buffer->id);
- GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
- glBindBuffer(target, buffer->id);
- glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
-
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
- buffer->data = nullptr;
+ if (m_rhi) {
+ // Batches are pooled and reused which means the QRhiBuffer will be
+ // still valid in a recycled Batch. We only hit the newBuffer() path
+ // for brand new Batches.
+ if (!buffer->buf) {
+ buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
+ isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
+ buffer->size);
+ if (!buffer->buf->build())
+ qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
+// else
+// qDebug("created rhibuf %p size %d", buffer->buf, buffer->size);
+ } else {
+ bool needsRebuild = false;
+ if (buffer->buf->size() < buffer->size) {
+ buffer->buf->setSize(buffer->size);
+ needsRebuild = true;
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic
+ && buffer->nonDynamicChangeCount > DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD)
+ {
+ buffer->buf->setType(QRhiBuffer::Dynamic);
+ buffer->nonDynamicChangeCount = 0;
+ needsRebuild = true;
+ }
+ if (needsRebuild) {
+ //qDebug("rebuilding rhibuf %p size %d type Dynamic", buffer->buf, buffer->size);
+ buffer->buf->build();
+ }
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic) {
+ m_resourceUpdates->uploadStaticBuffer(buffer->buf,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ buffer->nonDynamicChangeCount += 1;
+ } else {
+ m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ }
+ if (m_visualizeMode == VisualizeNothing)
+ buffer->data = nullptr;
+ } else {
+ if (buffer->id == 0)
+ glGenBuffers(1, &buffer->id);
+ GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
+ glBindBuffer(target, buffer->id);
+ glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing)
+ buffer->data = nullptr;
}
}
@@ -1098,8 +1389,12 @@ void Renderer::nodeWasRemoved(Node *node)
m_elementsToDelete.add(e);
if (m_renderNodeElements.isEmpty()) {
- static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ if (m_rhi) {
+ m_useDepthBuffer = true;
+ } else {
+ static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ }
}
}
}
@@ -1539,7 +1834,7 @@ void Renderer::prepareOpaqueBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1638,7 +1933,7 @@ void Renderer::prepareAlphaBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1666,19 +1961,20 @@ void Renderer::prepareAlphaBatches()
}
-static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
+static inline int qsg_fixIndexCount(int iCount, int drawMode)
+{
switch (drawMode) {
- case GL_TRIANGLE_STRIP:
+ case QSGGeometry::DrawTriangleStrip:
// Merged triangle strips need to contain degenerate triangles at the beginning and end.
// One could save 2 uploaded ushorts here by ditching the padding for the front of the
// first and the end of the last, but for simplicity, we simply don't care.
// Those extra triangles will be skipped while drawing to preserve the strip's parity
// anyhow.
return iCount + 2;
- case GL_LINES:
+ case QSGGeometry::DrawLines:
// For lines we drop the last vertex if the number of vertices is uneven.
return iCount - (iCount % 2);
- case GL_TRIANGLES:
+ case QSGGeometry::DrawTriangles:
// For triangles we drop trailing vertices until the result is divisible by 3.
return iCount - (iCount % 3);
default:
@@ -1700,7 +1996,7 @@ static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
* iBase: The starting index for this element in the batch
*/
-void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount)
+void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount)
{
if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
QSGGeometry *g = e->node->geometry();
@@ -1736,35 +2032,67 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
}
int iCount = g->indexCount();
- quint16 *indices = (quint16 *) *indexData;
+ if (m_uint32IndexForRhi) {
+ // can only happen when using the rhi
+ quint32 *iBase = (quint32 *) iBasePtr;
+ quint32 *indices = (quint32 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- if (iCount == 0) {
- iCount = vCount;
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase;
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ // source index data in QSGGeometry is always ushort (we would not merge otherwise)
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + i;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
} else {
- const quint16 *srcIndices = g->indexDataAsUShort();
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase + srcIndices[0];
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ // normally batching is only done for ushort index data
+ quint16 *iBase = (quint16 *) iBasePtr;
+ quint16 *indices = (quint16 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + srcIndices[i];
- }
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- indices[iCount] = indices[iCount - 1];
- iCount += 2;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
}
*vertexData += vCount * vSize;
- *indexData += iCount * sizeof(quint16);
- *iBase += vCount;
+ *indexData += iCount * mergedIndexElemSize();
*indexCount += iCount;
}
@@ -1779,66 +2107,67 @@ static QMatrix4x4 qsg_matrixForRoot(Node *node)
void Renderer::uploadBatch(Batch *b)
{
- // Early out if nothing has changed in this batch..
- if (!b->needsUpload) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
- return;
- }
-
- if (!b->first) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
- return;
- }
-
- if (b->isRenderNode) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
- return;
- }
+ // Early out if nothing has changed in this batch..
+ if (!b->needsUpload) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
+ return;
+ }
- // Figure out if we can merge or not, if not, then just render the batch as is..
- Q_ASSERT(b->first);
- Q_ASSERT(b->first->node);
+ if (!b->first) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
+ return;
+ }
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- QSGMaterial::Flags flags = gn->activeMaterial()->flags();
- bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP ||
- g->drawingMode() == GL_LINES || g->drawingMode() == GL_POINTS)
- && b->positionAttribute >= 0
- && g->indexType() == GL_UNSIGNED_SHORT
- && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
- && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
- && b->isSafeToBatch();
+ if (b->isRenderNode) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
+ return;
+ }
- b->merged = canMerge;
+ // Figure out if we can merge or not, if not, then just render the batch as is..
+ Q_ASSERT(b->first);
+ Q_ASSERT(b->first->node);
- // Figure out how much memory we need...
- b->vertexCount = 0;
- b->indexCount = 0;
- int unmergedIndexSize = 0;
- Element *e = b->first;
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial::Flags flags = gn->activeMaterial()->flags();
+ bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
+ g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
+ && b->positionAttribute >= 0
+ && g->indexType() == QSGGeometry::UnsignedShortType
+ && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
+ && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
+ && b->isSafeToBatch();
+
+ b->merged = canMerge;
+
+ // Figure out how much memory we need...
+ b->vertexCount = 0;
+ b->indexCount = 0;
+ int unmergedIndexSize = 0;
+ Element *e = b->first;
- while (e) {
- QSGGeometry *eg = e->node->geometry();
- b->vertexCount += eg->vertexCount();
- int iCount = eg->indexCount();
- if (b->merged) {
- if (iCount == 0)
- iCount = eg->vertexCount();
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- } else {
- unmergedIndexSize += iCount * eg->sizeOfIndex();
- }
- b->indexCount += iCount;
- e = e->nextInBatch;
+ while (e) {
+ QSGGeometry *eg = e->node->geometry();
+ b->vertexCount += eg->vertexCount();
+ int iCount = eg->indexCount();
+ if (b->merged) {
+ if (iCount == 0)
+ iCount = eg->vertexCount();
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : eg->sizeOfIndex();
+ unmergedIndexSize += iCount * effectiveIndexSize;
}
+ b->indexCount += iCount;
+ e = e->nextInBatch;
+ }
- // Abort if there are no vertices in this batch.. We abort this late as
- // this is a broken usecase which we do not care to optimize for...
- if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
- return;
+ // Abort if there are no vertices in this batch.. We abort this late as
+ // this is a broken usecase which we do not care to optimize for...
+ if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
+ return;
- /* Allocate memory for this batch. Merged batches are divided into three separate blocks
+ /* Allocate memory for this batch. Merged batches are divided into three separate blocks
1. Vertex data for all elements, as they were in the QSGGeometry object, but
with the tranform relative to this batch's root applied. The vertex data
is otherwise unmodified.
@@ -1850,119 +2179,161 @@ void Renderer::uploadBatch(Batch *b)
primitive. These are unsigned shorts for merged and arbitrary for
non-merged.
*/
- int bufferSize = b->vertexCount * g->sizeOfVertex();
- int ibufferSize = 0;
- if (b->merged) {
- ibufferSize = b->indexCount * sizeof(quint16);
- if (m_useDepthBuffer)
- bufferSize += b->vertexCount * sizeof(float);
- } else {
- ibufferSize = unmergedIndexSize;
- }
+ int bufferSize = b->vertexCount * g->sizeOfVertex();
+ int ibufferSize = 0;
+ if (b->merged) {
+ ibufferSize = b->indexCount * mergedIndexElemSize();
+ if (m_useDepthBuffer)
+ bufferSize += b->vertexCount * sizeof(float);
+ } else {
+ ibufferSize = unmergedIndexSize;
+ }
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
- map(&b->vbo, bufferSize);
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
+ if (separateIndexBuffer)
+ map(&b->ibo, ibufferSize, true);
+ else
+ bufferSize += ibufferSize;
+ map(&b->vbo, bufferSize);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
- << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
- << " vbo:" << b->vbo.id << ":" << b->vbo.size;
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
+ << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
+ << " vbo:" << b->vbo.id << ":" << b->vbo.size;
- if (b->merged) {
- char *vertexData = b->vbo.data;
- char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
-
- quint16 iOffset = 0;
- e = b->first;
- int verticesInSet = 0;
- int indicesInSet = 0;
- b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const auto indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
- b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
- while (e) {
- verticesInSet += e->node->geometry()->vertexCount();
- if (verticesInSet > 0xffff) {
- b->drawSets.last().indexCount = indicesInSet;
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
- }
- drawSetIndices = indexData - indexBase;
- b->drawSets << DrawSet(vertexData - b->vbo.data,
- zData - b->vbo.data,
- drawSetIndices);
- iOffset = 0;
- verticesInSet = e->node->geometry()->vertexCount();
- indicesInSet = 0;
+ if (b->merged) {
+ char *vertexData = b->vbo.data;
+ char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
+ char *indexData = separateIndexBuffer
+ ? b->ibo.data
+ : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
+
+ quint16 iOffset16 = 0;
+ quint32 iOffset32 = 0;
+ e = b->first;
+ uint verticesInSet = 0;
+ // Start a new set already after 65534 vertices because 0xFFFF may be
+ // used for an always-on primitive restart with some apis (adapt for
+ // uint32 indices as appropriate).
+ const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
+ int indicesInSet = 0;
+ b->drawSets.reset();
+ int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
+ const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
+ while (e) {
+ verticesInSet += e->node->geometry()->vertexCount();
+ if (verticesInSet > verticesInSetLimit) {
+ b->drawSets.last().indexCount = indicesInSet;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
}
- uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, &iOffset, &indicesInSet);
- e = e->nextInBatch;
+ drawSetIndices = indexData - indexBase;
+ b->drawSets << DrawSet(vertexData - b->vbo.data,
+ zData - b->vbo.data,
+ drawSetIndices);
+ iOffset16 = 0;
+ iOffset32 = 0;
+ verticesInSet = e->node->geometry()->vertexCount();
+ indicesInSet = 0;
}
- b->drawSets.last().indexCount = indicesInSet;
- // We skip the very first and very last degenerate triangles since they aren't needed
- // and the first one would reverse the vertex ordering of the merged strips.
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
- }
- } else {
- char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
- Element *e = b->first;
- while (e) {
- QSGGeometry *g = e->node->geometry();
- int vbs = g->vertexCount() * g->sizeOfVertex();
- memcpy(vboData, g->vertexData(), vbs);
- vboData = vboData + vbs;
- if (g->indexCount()) {
+ void *iBasePtr = &iOffset16;
+ if (m_uint32IndexForRhi)
+ iBasePtr = &iOffset32;
+ uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, iBasePtr, &indicesInSet);
+ e = e->nextInBatch;
+ }
+ b->drawSets.last().indexCount = indicesInSet;
+ // We skip the very first and very last degenerate triangles since they aren't needed
+ // and the first one would reverse the vertex ordering of the merged strips.
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
+ }
+ } else {
+ char *vboData = b->vbo.data;
+ char *iboData = separateIndexBuffer ? b->ibo.data
+ : vboData + b->vertexCount * g->sizeOfVertex();
+ Element *e = b->first;
+ while (e) {
+ QSGGeometry *g = e->node->geometry();
+ int vbs = g->vertexCount() * g->sizeOfVertex();
+ memcpy(vboData, g->vertexData(), vbs);
+ vboData = vboData + vbs;
+ const int indexCount = g->indexCount();
+ if (indexCount) {
+ if (!m_rhi) {
int ibs = g->indexCount() * g->sizeOfIndex();
memcpy(iboData, g->indexData(), ibs);
iboData += ibs;
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+ const int ibs = indexCount * effectiveIndexSize;
+ if (g->sizeOfIndex() == effectiveIndexSize) {
+ memcpy(iboData, g->indexData(), ibs);
+ } else {
+ if (g->sizeOfIndex() == sizeof(quint16) && effectiveIndexSize == sizeof(quint32)) {
+ quint16 *src = g->indexDataAsUShort();
+ quint32 *dst = (quint32 *) iboData;
+ for (int i = 0; i < indexCount; ++i)
+ dst[i] = src[i];
+ } else {
+ Q_ASSERT_X(false, "uploadBatch (unmerged)", "uint index with ushort effective index - cannot happen");
+ }
+ }
+ iboData += ibs;
}
- e = e->nextInBatch;
}
+ e = e->nextInBatch;
}
+ }
#ifndef QT_NO_DEBUG_OUTPUT
- if (Q_UNLIKELY(debug_upload())) {
- const char *vd = b->vbo.data;
- qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
- for (int i=0; i<b->vertexCount; ++i) {
- QDebug dump = qDebug().nospace();
- dump << " --- " << i << ": ";
- int offset = 0;
- for (int a=0; a<g->attributeCount(); ++a) {
- const QSGGeometry::Attribute &attr = g->attributes()[a];
- dump << attr.position << ":(" << attr.tupleSize << ",";
- if (attr.type == GL_FLOAT) {
- dump << "float ";
- if (attr.isVertexCoordinate)
- dump << "* ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
- } else if (attr.type == GL_UNSIGNED_BYTE) {
- dump << "ubyte ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
- }
- dump << ") ";
- offset += attr.tupleSize * size_of_type(attr.type);
+ if (Q_UNLIKELY(debug_upload())) {
+ const char *vd = b->vbo.data;
+ qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
+ for (int i=0; i<b->vertexCount; ++i) {
+ QDebug dump = qDebug().nospace();
+ dump << " --- " << i << ": ";
+ int offset = 0;
+ for (int a=0; a<g->attributeCount(); ++a) {
+ const QSGGeometry::Attribute &attr = g->attributes()[a];
+ dump << attr.position << ":(" << attr.tupleSize << ",";
+ if (attr.type == QSGGeometry::FloatType) {
+ dump << "float ";
+ if (attr.isVertexCoordinate)
+ dump << "* ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
+ } else if (attr.type == QSGGeometry::UnsignedByteType) {
+ dump << "ubyte ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
}
- if (b->merged && m_useDepthBuffer) {
- float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
- dump << " Z:(" << zorder << ")";
- }
- vd += g->sizeOfVertex();
+ dump << ") ";
+ offset += attr.tupleSize * size_of_type(attr.type);
+ }
+ if (b->merged && m_useDepthBuffer) {
+ float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
+ dump << " Z:(" << zorder << ")";
}
+ vd += g->sizeOfVertex();
+ }
- if (!b->drawSets.isEmpty()) {
+ if (!b->drawSets.isEmpty()) {
+ if (m_uint32IndexForRhi) {
+ const quint32 *id = (const quint32 *)(separateIndexBuffer
+ ? b->ibo.data
+ : b->vbo.data + b->drawSets.at(0).indices);
+ {
+ QDebug iDump = qDebug();
+ iDump << " -- Index Data, count:" << b->indexCount;
+ for (int i=0; i<b->indexCount; ++i) {
+ if ((i % 24) == 0)
+ iDump << Qt::endl << " --- ";
+ iDump << id[i];
+ }
+ }
+ } else {
const quint16 *id = (const quint16 *)(separateIndexBuffer
? b->ibo.data
: b->vbo.data + b->drawSets.at(0).indices);
@@ -1971,29 +2342,30 @@ void Renderer::uploadBatch(Batch *b)
iDump << " -- Index Data, count:" << b->indexCount;
for (int i=0; i<b->indexCount; ++i) {
if ((i % 24) == 0)
- iDump << endl << " --- ";
+ iDump << Qt::endl << " --- ";
iDump << id[i];
}
}
+ }
- for (int i=0; i<b->drawSets.size(); ++i) {
- const DrawSet &s = b->drawSets.at(i);
- qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
- }
+ for (int i=0; i<b->drawSets.size(); ++i) {
+ const DrawSet &s = b->drawSets.at(i);
+ qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
}
}
+ }
#endif // QT_NO_DEBUG_OUTPUT
- unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->vbo);
+ if (separateIndexBuffer)
+ unmap(&b->ibo, true);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
- b->needsUpload = false;
+ b->needsUpload = false;
- if (Q_UNLIKELY(debug_render()))
- b->uploadedThisFrame = true;
+ if (Q_UNLIKELY(debug_render()))
+ b->uploadedThisFrame = true;
}
/*!
@@ -2002,15 +2374,15 @@ void Renderer::uploadBatch(Batch *b)
* If the clip is a pixel aligned rectangle, this function will use glScissor instead
* of stencil.
*/
-Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
+ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
{
if (!clip) {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
- return NoClip;
+ return ClipState::NoClip;
}
- ClipType clipType = NoClip;
+ ClipState::ClipType clipType = ClipState::NoClip;
GLuint vbo = 0;
int vboSize = 0;
@@ -2067,17 +2439,17 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
- if (!(clipType & ScissorClip)) {
+ if (!(clipType & ClipState::ScissorClip)) {
m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
glEnable(GL_SCISSOR_TEST);
- clipType |= ScissorClip;
+ clipType |= ClipState::ScissorClip;
} else {
m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
}
glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(),
m_currentScissorRect.width(), m_currentScissorRect.height());
} else {
- if (!(clipType & StencilClip)) {
+ if (!(clipType & ClipState::StencilClip)) {
if (!m_clipProgram.isLinked()) {
QSGShaderSourceBuilder::initializeProgramFromFiles(
&m_clipProgram,
@@ -2097,7 +2469,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
m_clipProgram.bind();
m_clipProgram.enableAttributeArray(0);
- clipType |= StencilClip;
+ clipType |= ClipState::StencilClip;
}
glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
@@ -2148,7 +2520,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
if (vbo)
glDeleteBuffers(1, &vbo);
- if (clipType & StencilClip) {
+ if (clipType & ClipState::StencilClip) {
m_clipProgram.disableAttributeArray(0);
glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
@@ -2160,7 +2532,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
return clipType;
}
-void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
+void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) // legacy (GL-only)
{
if (clipList != m_currentClip && Q_LIKELY(!debug_noclip())) {
m_currentClip = clipList;
@@ -2174,7 +2546,7 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
m_currentClipType = updateStencilClip(m_currentClip);
if (batch->isOpaque) {
glEnable(GL_DEPTH_TEST);
- if (m_currentClipType & StencilClip)
+ if (m_currentClipType & ClipState::StencilClip)
glDepthMask(true);
}
}
@@ -2185,13 +2557,14 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
* which vertex attribute arrays need to be enabled and not. Then update the current
* Shader and current QSGMaterialShader.
*/
-void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader)
+void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader) // legacy (GL-only)
{
+ Q_ASSERT(!m_rhi);
const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : nullptr;
const char * const *n = program ? program->attributeNames() : nullptr;
- int cza = m_currentShader ? m_currentShader->pos_order : -1;
- int nza = shader ? shader->pos_order : -1;
+ int cza = m_currentShader ? m_currentShader->programGL.pos_order : -1;
+ int nza = shader ? shader->programGL.pos_order : -1;
int i = 0;
while (c || n) {
@@ -2233,7 +2606,372 @@ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader
}
}
-void Renderer::renderMergedBatch(const Batch *batch)
+void Renderer::applyClipStateToGraphicsState() // RHI only
+{
+ m_gstate.usesScissor = (m_currentClipState.type & ClipState::ScissorClip);
+ m_gstate.stencilTest = (m_currentClipState.type & ClipState::StencilClip);
+}
+
+QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch)
+{
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setFlags(QRhiGraphicsPipeline::UsesStencilRef);
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = 0;
+ ps->setTargetBlends({ blend });
+ ps->setSampleCount(renderTarget()->sampleCount());
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ if (firstStencilClipInBatch) {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Always;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Replace;
+ } else {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::IncrementAndClamp;
+ }
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+
+ ps->setTopology(m_stencilClipCommon.topology);
+
+ ps->setShaderStages({ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Vertex, m_stencilClipCommon.vs),
+ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Fragment, m_stencilClipCommon.fs) });
+ ps->setVertexInputLayout(m_stencilClipCommon.inputLayout);
+ ps->setShaderResourceBindings(batch->stencilClipState.srb); // use something, it just needs to be layout-compatible
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ if (!ps->build()) {
+ qWarning("Failed to build stencil clip pipeline");
+ delete ps;
+ return nullptr;
+ }
+
+ return ps;
+}
+
+void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI only
+{
+ // Note: No use of the clip-related speparate m_current* vars is allowed
+ // here. All stored in batch->clipState instead. To collect state during
+ // renderBatches(), m_currentClipState is used.
+
+ // The stenciling logic is slightly different from the legacy GL path as we
+ // cannot just randomly clear the stencil buffer. We now put all clip
+ // shapes into the stencil buffer for all batches in the frame. This means
+ // that the number of total clips in a scene is reduced (since the stencil
+ // value cannot exceed 255) but we do not need any clears inbetween.
+
+ Q_ASSERT(m_rhi);
+ batch->stencilClipState.updateStencilBuffer = false;
+ if (clipList == m_currentClipState.clipList || Q_UNLIKELY(debug_noclip())) {
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+ return;
+ }
+
+ ClipState::ClipType clipType = ClipState::NoClip;
+ QRect scissorRect;
+ QVarLengthArray<const QSGClipNode *, 4> stencilClipNodes;
+ const QSGClipNode *clip = clipList;
+
+ batch->stencilClipState.drawCalls.reset();
+ int totalVSize = 0;
+ int totalISize = 0;
+ int totalUSize = 0;
+ const int StencilClipUbufSize = 64;
+
+ while (clip) {
+ QMatrix4x4 m = m_current_projection_matrix_native_ndc;
+ if (clip->matrix())
+ m *= *clip->matrix();
+
+ bool isRectangleWithNoPerspective = clip->isRectangular()
+ && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
+ bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
+ bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
+
+ if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
+ QRectF bbox = clip->clipRect();
+ qreal invW = 1 / m(3, 3);
+ qreal fx1, fy1, fx2, fy2;
+ if (noRotate) {
+ fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
+ fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
+ fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
+ fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
+ } else {
+ Q_ASSERT(isRotate90);
+ fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
+ fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
+ fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
+ fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
+ }
+
+ if (fx1 > fx2)
+ qSwap(fx1, fx2);
+ if (fy1 > fy2)
+ qSwap(fy1, fy2);
+
+ QRect deviceRect = this->deviceRect();
+
+ GLint ix1 = qRound((fx1 + 1) * deviceRect.width() * qreal(0.5));
+ GLint iy1 = qRound((fy1 + 1) * deviceRect.height() * qreal(0.5));
+ GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
+ GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
+
+ if (!(clipType & ClipState::ScissorClip)) {
+ clipType |= ClipState::ScissorClip;
+ scissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ } else {
+ scissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ }
+ } else {
+ clipType |= ClipState::StencilClip;
+
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() > 0);
+
+ const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
+ // the 4 byte alignment may not actually be needed here
+ totalVSize = aligned(totalVSize, 4) + vertexByteSize;
+ if (g->indexCount()) {
+ const int indexByteSize = g->sizeOfIndex() * g->indexCount();
+ // so no need to worry about NonFourAlignedEffectiveIndexBufferOffset
+ totalISize = aligned(totalISize, 4) + indexByteSize;
+ }
+ // ubuf start offsets must be aligned (typically to 256 bytes)
+ totalUSize = aligned(totalUSize, m_ubufAlignment) + StencilClipUbufSize;
+
+ stencilClipNodes.append(clip);
+ }
+
+ clip = clip->clipList();
+ }
+
+ if (clipType & ClipState::StencilClip) {
+ bool rebuildVBuf = false;
+ if (!batch->stencilClipState.vbuf) {
+ batch->stencilClipState.vbuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, totalVSize);
+ rebuildVBuf = true;
+ } else if (batch->stencilClipState.vbuf->size() < totalVSize) {
+ batch->stencilClipState.vbuf->setSize(totalVSize);
+ rebuildVBuf = true;
+ }
+ if (rebuildVBuf) {
+ if (!batch->stencilClipState.vbuf->build()) {
+ qWarning("Failed to build stencil clip vertex buffer");
+ delete batch->stencilClipState.vbuf;
+ batch->stencilClipState.vbuf = nullptr;
+ return;
+ }
+ }
+
+ if (totalISize) {
+ bool rebuildIBuf = false;
+ if (!batch->stencilClipState.ibuf) {
+ batch->stencilClipState.ibuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::IndexBuffer, totalISize);
+ rebuildIBuf = true;
+ } else if (batch->stencilClipState.ibuf->size() < totalISize) {
+ batch->stencilClipState.ibuf->setSize(totalISize);
+ rebuildIBuf = true;
+ }
+ if (rebuildIBuf) {
+ if (!batch->stencilClipState.ibuf->build()) {
+ qWarning("Failed to build stencil clip index buffer");
+ delete batch->stencilClipState.ibuf;
+ batch->stencilClipState.ibuf = nullptr;
+ return;
+ }
+ }
+ }
+
+ bool rebuildUBuf = false;
+ if (!batch->stencilClipState.ubuf) {
+ batch->stencilClipState.ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUSize);
+ rebuildUBuf = true;
+ } else if (batch->stencilClipState.ubuf->size() < totalUSize) {
+ batch->stencilClipState.ubuf->setSize(totalUSize);
+ rebuildUBuf = true;
+ }
+ if (rebuildUBuf) {
+ if (!batch->stencilClipState.ubuf->build()) {
+ qWarning("Failed to build stencil clip uniform buffer");
+ delete batch->stencilClipState.ubuf;
+ batch->stencilClipState.ubuf = nullptr;
+ return;
+ }
+ }
+
+ if (!batch->stencilClipState.srb) {
+ batch->stencilClipState.srb = m_rhi->newShaderResourceBindings();
+ const QRhiShaderResourceBinding ubufBinding = QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
+ 0, QRhiShaderResourceBinding::VertexStage, batch->stencilClipState.ubuf, StencilClipUbufSize);
+ batch->stencilClipState.srb->setBindings({ ubufBinding });
+ if (!batch->stencilClipState.srb->build()) {
+ qWarning("Failed to build stencil clip srb");
+ delete batch->stencilClipState.srb;
+ batch->stencilClipState.srb = nullptr;
+ return;
+ }
+ }
+
+ int vOffset = 0;
+ int iOffset = 0;
+ int uOffset = 0;
+ for (const QSGClipNode *clip : stencilClipNodes) {
+ const QSGGeometry *g = clip->geometry();
+ const QSGGeometry::Attribute *a = g->attributes();
+ StencilClipState::StencilDrawCall drawCall;
+ const bool firstStencilClipInBatch = batch->stencilClipState.drawCalls.isEmpty();
+
+ if (firstStencilClipInBatch) {
+ m_stencilClipCommon.inputLayout.setBindings({ QRhiVertexInputBinding(g->sizeOfVertex()) });
+ m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, vertexInputFormat(*a), 0) });
+ m_stencilClipCommon.topology = gpTopology(g->drawingMode());
+ }
+#ifndef QT_NO_DEBUG
+ else {
+ if (gpTopology(g->drawingMode()) != m_stencilClipCommon.topology)
+ qWarning("updateClipState: Clip list entries have different primitive topologies, this is not currently supported.");
+ if (vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format())
+ qWarning("updateClipState: Clip list entries have different vertex input layouts, this is must not happen.");
+ }
+#endif
+
+ drawCall.vbufOffset = aligned(vOffset, 4);
+ const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
+ vOffset += vertexByteSize;
+
+ int indexByteSize = 0;
+ if (g->indexCount()) {
+ drawCall.ibufOffset = aligned(iOffset, 4);
+ indexByteSize = g->sizeOfIndex() * g->indexCount();
+ iOffset += indexByteSize;
+ }
+
+ drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
+ uOffset += StencilClipUbufSize;
+
+ QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
+ if (clip->matrix())
+ matrixYUpNDC *= *clip->matrix();
+
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ubuf, drawCall.ubufOffset, 64, matrixYUpNDC.constData());
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.vbuf, drawCall.vbufOffset, vertexByteSize, g->vertexData());
+ if (indexByteSize)
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ibuf, drawCall.ibufOffset, indexByteSize, g->indexData());
+
+ // stencil ref goes 1, 1, 2, 3, 4, ..., N for the clips in the first batch,
+ // then N+1, N+1, N+2, N+3, ... for the next batch,
+ // and so on.
+ // Note the different stencilOp for the first and the subsequent clips.
+ drawCall.stencilRef = firstStencilClipInBatch ? m_currentClipState.stencilRef + 1 : m_currentClipState.stencilRef;
+ m_currentClipState.stencilRef += 1;
+
+ drawCall.vertexCount = g->vertexCount();
+ drawCall.indexCount = g->indexCount();
+ drawCall.indexFormat = indexFormat(g);
+ batch->stencilClipState.drawCalls.add(drawCall);
+ }
+
+ if (!m_stencilClipCommon.vs.isValid())
+ m_stencilClipCommon.vs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.vert.qsb"));
+
+ if (!m_stencilClipCommon.fs.isValid())
+ m_stencilClipCommon.fs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.frag.qsb"));
+
+ if (!m_stencilClipCommon.replacePs)
+ m_stencilClipCommon.replacePs = buildStencilPipeline(batch, true);
+
+ if (!m_stencilClipCommon.incrPs)
+ m_stencilClipCommon.incrPs = buildStencilPipeline(batch, false);
+
+ batch->stencilClipState.updateStencilBuffer = true;
+ }
+
+ m_currentClipState.clipList = clipList;
+ m_currentClipState.type = clipType;
+ m_currentClipState.scissor = QRhiScissor(scissorRect.x(), scissorRect.y(),
+ scissorRect.width(), scissorRect.height());
+
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+}
+
+void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only
+{
+ // cliptype stencil + updateStencilBuffer==false means the batch uses
+ // stenciling but relies on the stencil data generated by a previous batch
+ // (due to the having the same clip node). Do not enqueue draw calls for
+ // stencil in this case as the stencil buffer is already up-to-date.
+ if (!batch->stencilClipState.updateStencilBuffer)
+ return;
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ const int count = batch->stencilClipState.drawCalls.size();
+ for (int i = 0; i < count; ++i) {
+ const StencilClipState::StencilDrawCall &drawCall(batch->stencilClipState.drawCalls.at(i));
+ QRhiShaderResourceBindings *srb = batch->stencilClipState.srb;
+ QRhiCommandBuffer::DynamicOffset ubufOffset(0, drawCall.ubufOffset);
+ if (i == 0) {
+ cb->setGraphicsPipeline(m_stencilClipCommon.replacePs);
+ cb->setShaderResources(srb, 1, &ubufOffset);
+ cb->setViewport(m_pstate.viewport);
+ } else if (i == 1) {
+ cb->setGraphicsPipeline(m_stencilClipCommon.incrPs);
+ cb->setShaderResources(srb, 1, &ubufOffset);
+ cb->setViewport(m_pstate.viewport);
+ }
+ cb->setStencilRef(drawCall.stencilRef);
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset);
+ if (drawCall.indexCount) {
+ cb->setVertexInput(0, 1, &vbufBinding,
+ batch->stencilClipState.ibuf, drawCall.ibufOffset, drawCall.indexFormat);
+ cb->drawIndexed(drawCall.indexCount);
+ } else {
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(drawCall.vertexCount);
+ }
+ }
+}
+
+void Renderer::setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader) // RHI only
+{
+ Q_ASSERT(m_rhi);
+ m_currentRhiProgram = program;
+ m_currentShader = shader;
+ m_currentMaterial = nullptr;
+}
+
+void Renderer::updateLineWidth(QSGGeometry *g) // legacy (GL-only)
+{
+ if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
+ glLineWidth(g->lineWidth());
+#if !defined(QT_OPENGL_ES_2)
+ else {
+ QOpenGLContext *ctx = m_context->openglContext();
+ if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
+ QOpenGLFunctions_1_0 *gl1funcs = nullptr;
+ QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
+ if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
+ gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
+ else
+ gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
+ Q_ASSERT(gl1funcs || gl3funcs);
+ if (gl1funcs)
+ gl1funcs->glPointSize(g->lineWidth());
+ else
+ gl3funcs->glPointSize(g->lineWidth());
+ }
+ }
+#endif
+}
+
+void Renderer::renderMergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0 || batch->indexCount == 0)
return;
@@ -2289,13 +3027,14 @@ void Renderer::renderMergedBatch(const Batch *batch)
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material) : m_shaderManager->prepareMaterialNoRewrite(material);
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material)
+ : m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
+ Q_ASSERT(sms->programGL.program);
if (m_currentShader != sms)
- setActiveShader(program, sms);
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
@@ -2303,7 +3042,7 @@ void Renderer::renderMergedBatch(const Batch *batch)
sms->lastOpacity = m_current_opacity;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2320,9 +3059,9 @@ void Renderer::renderMergedBatch(const Batch *batch)
m_currentMaterial = material;
- QSGGeometry* g = gn->geometry();
+ QSGGeometry *g = gn->geometry();
updateLineWidth(g);
- char const *const *attrNames = program->attributeNames();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
for (int i=0; i<batch->drawSets.size(); ++i) {
const DrawSet &draw = batch->drawSets.at(i);
int offset = 0;
@@ -2335,13 +3074,13 @@ void Renderer::renderMergedBatch(const Batch *batch)
offset += a.tupleSize * size_of_type(a.type);
}
if (m_useDepthBuffer)
- glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
+ glVertexAttribPointer(sms->programGL.pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
}
}
-void Renderer::renderUnmergedBatch(const Batch *batch)
+void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0)
return;
@@ -2371,7 +3110,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
char *indexBase = nullptr;
- const auto separateIndexBuffer = m_context->separateIndexBuffer();
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo;
if (batch->indexCount) {
if (m_context->hasBrokenIndexBufferObjects()) {
@@ -2389,10 +3128,10 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
- if (sms != m_currentShader)
- setActiveShader(program, sms);
+ Q_ASSERT(sms->programGL.program);
+ if (m_currentShader != sms)
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (sms->lastOpacity != m_current_opacity) {
@@ -2419,7 +3158,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2435,8 +3174,8 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
// are all identical (compare==0) since they are in the same batch.
m_currentMaterial = material;
- QSGGeometry* g = gn->geometry();
- char const *const *attrNames = program->attributeNames();
+ QSGGeometry *g = gn->geometry();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
int offset = 0;
for (int j = 0; attrNames[j]; ++j) {
if (!*attrNames[j])
@@ -2463,101 +3202,926 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
}
}
-void Renderer::updateLineWidth(QSGGeometry *g)
+static inline bool needsBlendConstant(QRhiGraphicsPipeline::BlendFactor f)
{
- if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
- glLineWidth(g->lineWidth());
-#if !defined(QT_OPENGL_ES_2)
- else {
- QOpenGLContext *ctx = m_context->openglContext();
- if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
- QOpenGLFunctions_1_0 *gl1funcs = nullptr;
- QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
- if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
- gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
- else
- gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
- Q_ASSERT(gl1funcs || gl3funcs);
- if (gl1funcs)
- gl1funcs->glPointSize(g->lineWidth());
- else
- gl3funcs->glPointSize(g->lineWidth());
+ return f == QRhiGraphicsPipeline::ConstantColor
+ || f == QRhiGraphicsPipeline::OneMinusConstantColor
+ || f == QRhiGraphicsPipeline::ConstantAlpha
+ || f == QRhiGraphicsPipeline::OneMinusConstantAlpha;
+}
+
+// With QRhi renderBatches() is split to two steps: prepare and render.
+//
+// Prepare goes through the batches and elements, and set up a graphics
+// pipeline, srb, uniform buffer, calculates clipping, based on m_gstate, the
+// material (shaders), and the batches. This step does not touch the command
+// buffer or renderpass-related state (m_pstate).
+//
+// The render step then starts a renderpass, and goes through all
+// batches/elements again and records setGraphicsPipeline, drawIndexed, etc. on
+// the command buffer. The prepare step's accumulated global state like
+// m_gstate must not be used here. Rather, all data needed for rendering is
+// available from Batch/Element at this stage. Bookkeeping of state in the
+// renderpass is done via m_pstate.
+
+bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms) // RHI only, [prepare step]
+{
+ // In unmerged batches the srbs in the elements are all compatible layout-wise.
+ const GraphicsPipelineStateKey k { m_gstate, sms, renderPassDescriptor(), e->srb };
+
+ // Note: dynamic state (viewport rect, scissor rect, stencil ref, blend
+ // constant) is never a part of GraphicsState/QRhiGraphicsPipeline.
+
+ // See if there is an existing, matching pipeline state object.
+ auto it = m_pipelines.constFind(k);
+ if (it != m_pipelines.constEnd()) {
+ e->ps = *it;
+ return true;
+ }
+
+ // Build a new one. This is potentially expensive.
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setShaderStages(sms->programRhi.shaderStages);
+ ps->setVertexInputLayout(sms->programRhi.inputLayout);
+ ps->setShaderResourceBindings(e->srb);
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ QRhiGraphicsPipeline::Flags flags = 0;
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ flags |= QRhiGraphicsPipeline::UsesBlendConstants;
+ if (m_gstate.usesScissor)
+ flags |= QRhiGraphicsPipeline::UsesScissor;
+ if (m_gstate.stencilTest)
+ flags |= QRhiGraphicsPipeline::UsesStencilRef;
+
+ ps->setFlags(flags);
+ ps->setTopology(gpTopology(m_gstate.drawMode));
+ ps->setCullMode(m_gstate.cullMode);
+
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = m_gstate.colorWrite;
+ blend.enable = m_gstate.blending;
+ blend.srcColor = m_gstate.srcColor;
+ blend.dstColor = m_gstate.dstColor;
+ ps->setTargetBlends({ blend });
+
+ ps->setDepthTest(m_gstate.depthTest);
+ ps->setDepthWrite(m_gstate.depthWrite);
+ ps->setDepthOp(m_gstate.depthFunc);
+
+ if (m_gstate.stencilTest) {
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Keep;
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+ }
+
+ ps->setSampleCount(m_gstate.sampleCount);
+
+ ps->setLineWidth(m_gstate.lineWidth);
+
+ //qDebug("building new ps %p", ps);
+ if (!ps->build()) {
+ qWarning("Failed to build graphics pipeline state");
+ delete ps;
+ return false;
+ }
+
+ m_pipelines.insert(k, ps);
+ e->ps = ps;
+ return true;
+}
+
+static QRhiSampler *newSampler(QRhi *rhi, const QSGSamplerDescription &desc)
+{
+ QRhiSampler::Filter magFilter;
+ QRhiSampler::Filter minFilter;
+ QRhiSampler::Filter mipmapMode;
+ QRhiSampler::AddressMode u;
+ QRhiSampler::AddressMode v;
+
+ switch (desc.filtering) {
+ case QSGTexture::None:
+ Q_FALLTHROUGH();
+ case QSGTexture::Nearest:
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ magFilter = minFilter = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ }
+
+ switch (desc.mipmapFiltering) {
+ case QSGTexture::None:
+ mipmapMode = QRhiSampler::None;
+ break;
+ case QSGTexture::Nearest:
+ mipmapMode = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ mipmapMode = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ mipmapMode = QRhiSampler::None;
+ break;
+ }
+
+ switch (desc.horizontalWrap) {
+ case QSGTexture::Repeat:
+ u = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ u = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ u = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ u = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ switch (desc.verticalWrap) {
+ case QSGTexture::Repeat:
+ v = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ v = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ v = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ v = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ return rhi->newSampler(magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTexture *Renderer::dummyTexture()
+{
+ if (!m_dummyTexture) {
+ m_dummyTexture = m_rhi->newTexture(QRhiTexture::RGBA8, QSize(64, 64));
+ if (m_dummyTexture->build()) {
+ if (m_resourceUpdates) {
+ QImage img(m_dummyTexture->pixelSize(), QImage::Format_RGBA8888_Premultiplied);
+ img.fill(0);
+ m_resourceUpdates->uploadTexture(m_dummyTexture, img);
+ }
+ }
+ }
+ return m_dummyTexture;
+}
+
+static void rendererToMaterialGraphicsState(QSGMaterialRhiShader::GraphicsPipelineState *dst,
+ GraphicsState *src)
+{
+ dst->blendEnable = src->blending;
+
+ // the enum values should match, sanity check it
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
+
+ dst->srcColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->srcColor);
+ dst->dstColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->dstColor);
+
+ dst->colorWrite = QSGMaterialRhiShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
+
+ dst->cullMode = QSGMaterialRhiShader::GraphicsPipelineState::CullMode(src->cullMode);
+}
+
+static void materialToRendererGraphicsState(GraphicsState *dst,
+ QSGMaterialRhiShader::GraphicsPipelineState *src)
+{
+ dst->blending = src->blendEnable;
+ dst->srcColor = QRhiGraphicsPipeline::BlendFactor(src->srcColor);
+ dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
+ dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
+ dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+}
+
+void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
+ const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ QVector<QRhiShaderResourceBinding> *bindings,
+ const Batch *batch,
+ int ubufOffset,
+ int ubufRegionSize) // RHI only, [prepare step]
+{
+ m_current_resource_update_batch = m_resourceUpdates;
+
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(shader);
+ if (pd->ubufBinding >= 0) {
+ m_current_uniform_data = &pd->masterUniformData;
+ const bool changed = shader->updateUniformData(renderState, material, m_currentMaterial);
+
+ if (changed || !batch->ubufDataValid)
+ m_resourceUpdates->updateDynamicBuffer(batch->ubuf, ubufOffset, ubufRegionSize, pd->masterUniformData.constData());
+
+ bindings->append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
+ pd->ubufStages,
+ batch->ubuf,
+ ubufOffset,
+ ubufRegionSize));
+ }
+
+ for (int binding = 0; binding < QSGMaterialRhiShaderPrivate::MAX_SHADER_RESOURCE_BINDINGS; ++binding) {
+ const QRhiShaderResourceBinding::StageFlags stages = pd->combinedImageSamplerBindings[binding];
+ if (!stages)
+ continue;
+
+ QSGTexture *prevTex = pd->textureBindingTable[binding];
+ QSGTexture *t = prevTex;
+
+ shader->updateSampledImage(renderState, binding, &t, material, m_currentMaterial);
+ if (!t) {
+ qWarning("No QSGTexture provided from updateSampledImage(). This is wrong.");
+ continue;
+ }
+
+ QSGTexturePrivate *td = QSGTexturePrivate::get(t);
+ // prevTex may be invalid at this point, avoid dereferencing it
+ if (t != prevTex || td->hasDirtySamplerOptions()) {
+ // The QSGTexture, and so the sampler parameters, may have changed.
+ // The rhiTexture is not relevant here.
+ td->resetDirtySamplerOptions();
+ pd->textureBindingTable[binding] = t; // does not own
+ pd->samplerBindingTable[binding] = nullptr;
+ if (t->anisotropyLevel() != QSGTexture::AnisotropyNone) // ###
+ qWarning("QSGTexture anisotropy levels are not currently supported");
+
+ const QSGSamplerDescription samplerDesc = QSGSamplerDescription::fromTexture(t);
+ QRhiSampler *sampler = nullptr;
+ auto it = m_samplers.constFind(samplerDesc);
+ if (it != m_samplers.constEnd()) {
+ sampler = *it;
+ Q_ASSERT(sampler);
+ } else {
+ sampler = newSampler(m_rhi, samplerDesc);
+ if (!sampler->build()) {
+ qWarning("Failed to build sampler");
+ delete sampler;
+ continue;
+ }
+ m_samplers.insert(samplerDesc, sampler);
+ }
+ pd->samplerBindingTable[binding] = sampler; // does not own
+ }
+
+ if (pd->textureBindingTable[binding] && pd->samplerBindingTable[binding]) {
+ QRhiTexture *texture = pd->textureBindingTable[binding]->rhiTexture();
+ // texture may be null if the update above failed for any reason,
+ // or if the QSGTexture chose to return null intentionally. This is
+ // valid and we still need to provide something to the shader.
+ if (!texture)
+ texture = dummyTexture();
+ QRhiSampler *sampler = pd->samplerBindingTable[binding];
+ bindings->append(QRhiShaderResourceBinding::sampledTexture(binding,
+ stages,
+ texture,
+ sampler));
}
}
+
+#ifndef QT_NO_DEBUG
+ if (bindings->isEmpty())
+ qWarning("No shader resources for material %p, this is odd.", material);
#endif
}
-void Renderer::renderBatches()
+void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
+ const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ Batch *batch,
+ bool *gstateChanged) // RHI only, [prepare step]
{
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ *gstateChanged = false;
+ if (shader->flags().testFlag(QSGMaterialRhiShader::UpdatesGraphicsPipelineState)) {
+ // generate the public mini-state from m_gstate, invoke the material,
+ // write the changes, if any, back to m_gstate, together with a way to
+ // roll those back.
+ QSGMaterialRhiShader::GraphicsPipelineState shaderPs;
+ rendererToMaterialGraphicsState(&shaderPs, &m_gstate);
+ const bool changed = shader->updateGraphicsPipelineState(renderState, &shaderPs, material, m_currentMaterial);
+ if (changed) {
+ m_gstateStack.push(m_gstate);
+ materialToRendererGraphicsState(&m_gstate, &shaderPs);
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ batch->blendConstant = shaderPs.blendConstant;
+ *gstateChanged = true;
+ }
+ }
+}
+
+bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ if (batch->vertexCount == 0 || batch->indexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+#ifndef QT_NO_DEBUG_OUTPUT
if (Q_UNLIKELY(debug_render())) {
- qDebug().nospace() << "Rendering:" << endl
- << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
- << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ QDebug debug = qDebug();
+ debug << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[ merged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+ if (batch->drawSets.size() > 1)
+ debug << "sets:" << batch->drawSets.size();
+ if (!batch->isOpaque)
+ debug << "opacity:" << e->node->inheritedOpacity();
+ batch->uploadedThisFrame = false;
}
+#endif
- QRect r = viewportRect();
- glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
- glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+ QSGGeometryNode *gn = e->node;
- if (m_useDepthBuffer) {
- glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glDepthMask(true);
- glDisable(GL_BLEND);
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+ if (batch->root)
+ m_current_model_view_matrix = qsg_matrixForRoot(batch->root);
+ else
+ m_current_model_view_matrix.setToIdentity();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGMaterial *material = gn->activeMaterial();
+ updateClipState(gn->clipList(), batch);
+
+ const QSGGeometry *g = gn->geometry();
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material, true, g)
+ : m_shaderManager->prepareMaterialNoRewrite(material, true, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < ubufSize) {
+ batch->ubuf->setSize(ubufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", ubufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ const QSGMaterialRhiShader::RenderState renderState = rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty)));
+
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
+
+ QVector<QRhiShaderResourceBinding> bindings;
+ updateMaterialDynamicData(sms, renderState, material, &bindings, batch, 0, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
+ Element *ee = e;
+ while (ee) {
+ qDebug() << " -" << ee->node;
+ ee = ee->nextInBatch;
+ }
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ const bool hasPipeline = ensurePipelineState(e, sms);
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ if (!hasPipeline)
+ return false;
+
+ batch->ubufDataValid = true;
+
+ m_currentMaterial = material;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::checkLineWidth(QSGGeometry *g)
+{
+ if (g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawLineLoop
+ || g->drawingMode() == QSGGeometry::DrawLineStrip)
+ {
+ if (g->lineWidth() != 1.0f) {
+ static bool checkedWideLineSupport = false;
+ if (!checkedWideLineSupport) {
+ checkedWideLineSupport = true;
+ if (!m_rhi->isFeatureSupported(QRhi::WideLines))
+ qWarning("Line widths other than 1 are not supported by the graphics API");
+ }
+ }
+ } else if (g->drawingMode() == QSGGeometry::DrawPoints) {
+ if (g->lineWidth() != 1.0f) {
+ static bool warnedPointSize = false;
+ if (!warnedPointSize) {
+ warnedPointSize = true;
+ qWarning("Point size is not controllable by QSGGeometry. "
+ "Set gl_PointSize from the vertex shader instead.");
+ }
+ }
+ }
+}
+
+void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ setGraphicsPipeline(cb, batch, e);
+
+ for (int i = 0, ie = batch->drawSets.size(); i != ie; ++i) {
+ const DrawSet &draw = batch->drawSets.at(i);
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { batch->vbo.buf, quint32(draw.vertices) },
+ { batch->vbo.buf, quint32(draw.zorders) }
+ };
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 2, vbufBindings,
+ batch->ibo.buf, draw.indices,
+ m_uint32IndexForRhi ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(draw.indexCount);
+ }
+}
+
+bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ if (batch->vertexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug() << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[unmerged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+
+ batch->uploadedThisFrame = false;
+ }
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGGeometryNode *gn = e->node;
+ updateClipState(gn->clipList(), batch);
+
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+
+ // The vertex attributes are assumed to be the same for all elements in the
+ // unmerged batch since the material (and so the shaders) is the same.
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial *material = gn->activeMaterial();
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, m_rhi, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (sms->lastOpacity != m_current_opacity) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ int totalUBufSize = 0;
+ while (e) {
+ totalUBufSize += aligned(ubufSize, m_ubufAlignment);
+ e = e->nextInBatch;
+ }
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUBufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < totalUBufSize) {
+ batch->ubuf->setSize(totalUBufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", totalUBufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty))),
+ material, batch, &pendingGStatePop);
+
+ int ubufOffset = 0;
+ QRhiGraphicsPipeline *ps = nullptr;
+ e = batch->first;
+ while (e) {
+ gn = e->node;
+
+ m_current_model_view_matrix = rootMatrix * *gn->matrix();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ if (m_useDepthBuffer) {
+ m_current_projection_matrix(2, 2) = m_zRange;
+ m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
+ }
+
+ QVector<QRhiShaderResourceBinding> bindings;
+ updateMaterialDynamicData(sms, rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty))),
+ material, &bindings, batch, ubufOffset, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
+ qDebug() << " - offending node is" << e->node;
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ return false;
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ ubufOffset += aligned(ubufSize, m_ubufAlignment);
+
+ const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
+ const float prevLineWidth = m_gstate.lineWidth;
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ // Do not bother even looking up the ps if the topology has not changed
+ // since everything else is the same for all elements in the batch.
+ // (except if the material modified blend state)
+ if (!ps || m_gstate.drawMode != prevDrawMode || m_gstate.lineWidth != prevLineWidth || pendingGStatePop) {
+ if (!ensurePipelineState(e, sms)) {
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+ return false;
+ }
+ ps = e->ps;
+ } else {
+ e->ps = ps;
+ }
+
+ // We don't need to bother with asking each node for its material as they
+ // are all identical (compare==0) since they are in the same batch.
+ m_currentMaterial = material;
+
+ // We only need to push this on the very first iteration...
+ dirty &= ~QSGMaterialShader::RenderState::DirtyOpacity;
+
+ e = e->nextInBatch;
+ }
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ batch->ubufDataValid = true;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ int vOffset = 0;
+ int iOffset = 0;
+ QRhiCommandBuffer *cb = commandBuffer();
+
+ while (e) {
+ gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+
+ setGraphicsPipeline(cb, batch, e);
+
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->vbo.buf, vOffset);
+ if (g->indexCount()) {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
+ batch->ibo.buf, iOffset,
+ effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
+ : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(g->indexCount());
+ } else {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding);
+ cb->draw(g->vertexCount());
+ }
+
+ vOffset += g->sizeOfVertex() * g->vertexCount();
+ iOffset += g->indexCount() * effectiveIndexSize;
+
+ e = e->nextInBatch;
+ }
+}
+
+void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e) // RHI only, [render step]
+{
+ cb->setGraphicsPipeline(e->ps);
+
+ if (!m_pstate.viewportSet) {
+ m_pstate.viewportSet = true;
+ cb->setViewport(m_pstate.viewport);
+ }
+ if (batch->clipState.type & ClipState::ScissorClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ m_pstate.scissorSet = true;
+ cb->setScissor(batch->clipState.scissor);
} else {
- glDisable(GL_DEPTH_TEST);
- glDepthMask(false);
+ Q_ASSERT(!e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ // Regardless of the ps not using scissor, the scissor may need to be
+ // reset, depending on the backend. So set the viewport again, which in
+ // turn also sets the scissor on backends where a scissor rect is
+ // always-on (Vulkan).
+ if (m_pstate.scissorSet) {
+ m_pstate.scissorSet = false;
+ cb->setViewport(m_pstate.viewport);
+ }
}
- glDisable(GL_CULL_FACE);
- glColorMask(true, true, true, true);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_STENCIL_TEST);
+ if (batch->clipState.type & ClipState::StencilClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesStencilRef));
+ cb->setStencilRef(batch->clipState.stencilRef);
+ }
+ if (e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
+ cb->setBlendConstants(batch->blendConstant);
+
+ cb->setShaderResources(e->srb);
+}
- bindable()->clear(clearMode());
+void Renderer::renderBatches()
+{
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug().nospace() << "Rendering:" << Qt::endl
+ << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << Qt::endl
+ << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ }
m_current_opacity = 1;
m_currentMaterial = nullptr;
m_currentShader = nullptr;
m_currentProgram = nullptr;
+ m_currentRhiProgram = nullptr;
m_currentClip = nullptr;
+ m_currentClipState.reset();
+
+ const QRect viewport = viewportRect();
bool renderOpaque = !debug_noopaque();
bool renderAlpha = !debug_noalpha();
- if (Q_LIKELY(renderOpaque)) {
- for (int i=0; i<m_opaqueBatches.size(); ++i) {
- Batch *b = m_opaqueBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else
- renderUnmergedBatch(b);
+ if (!m_rhi) {
+ // legacy, GL-only path
+
+ glViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+
+ if (m_useDepthBuffer) {
+ glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(true);
+ glDisable(GL_BLEND);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(false);
}
- }
+ glDisable(GL_CULL_FACE);
+ glColorMask(true, true, true, true);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_STENCIL_TEST);
- glEnable(GL_BLEND);
- if (m_useDepthBuffer)
- glDepthMask(false);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ bindable()->clear(clearMode());
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i=0; i<m_opaqueBatches.size(); ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
- if (Q_LIKELY(renderAlpha)) {
- for (int i=0; i<m_alphaBatches.size(); ++i) {
- Batch *b = m_alphaBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else if (b->isRenderNode)
- renderRenderNode(b);
+ glEnable(GL_BLEND);
+ if (m_useDepthBuffer)
+ glDepthMask(false);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i=0; i<m_alphaBatches.size(); ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else if (b->isRenderNode)
+ renderRenderNode(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
+
+ if (m_currentShader)
+ setActiveShader(nullptr, nullptr);
+
+ updateStencilClip(nullptr);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glDepthMask(true);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ } else {
+ // RHI path
+
+ m_pstate.viewport = QRhiViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ m_pstate.clearColor = clearColor();
+ m_pstate.dsClear = QRhiDepthStencilClearValue(1.0f, 0);
+ m_pstate.viewportSet = false;
+ m_pstate.scissorSet = false;
+
+ m_gstate.depthTest = true;
+ m_gstate.depthWrite = true;
+ m_gstate.depthFunc = QRhiGraphicsPipeline::Less;
+ m_gstate.blending = false;
+
+ m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.colorWrite = QRhiGraphicsPipeline::R
+ | QRhiGraphicsPipeline::G
+ | QRhiGraphicsPipeline::B
+ | QRhiGraphicsPipeline::A;
+ m_gstate.usesScissor = false;
+ m_gstate.stencilTest = false;
+
+ m_gstate.sampleCount = renderTarget()->sampleCount();
+
+ QVarLengthArray<PreparedRenderBatch, 64> opaqueRenderBatches;
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i = 0, ie = m_opaqueBatches.size(); i != ie; ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ opaqueRenderBatches.append(renderBatch);
+ }
+ }
+
+ m_gstate.blending = true;
+ // factors never change, always set for premultiplied alpha based blending
+
+ // depth test stays enabled but no need to write out depth from the
+ // transparent (back-to-front) pass
+ m_gstate.depthWrite = false;
+
+ QVarLengthArray<PreparedRenderBatch, 64> alphaRenderBatches;
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i = 0, ie = m_alphaBatches.size(); i != ie; ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else if (b->isRenderNode)
+ ok = false; // ###
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ alphaRenderBatches.append(renderBatch);
+ }
+ }
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates);
+ m_resourceUpdates = nullptr;
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ for (int i = 0, ie = opaqueRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &opaqueRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
else
- renderUnmergedBatch(b);
+ renderUnmergedBatch(renderBatch);
}
- }
- if (m_currentShader)
- setActiveShader(nullptr, nullptr);
- updateStencilClip(nullptr);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDepthMask(true);
+ for (int i = 0, ie = alphaRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &alphaRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
+ else if (renderBatch->batch->isRenderNode)
+ Q_UNREACHABLE(); // ###
+ else
+ renderUnmergedBatch(renderBatch);
+ }
+
+ if (m_currentShader)
+ setActiveRhiShader(nullptr, nullptr);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ cb->endPass();
+ }
}
void Renderer::deleteRemovedElements()
@@ -2588,8 +4152,6 @@ void Renderer::deleteRemovedElements()
void Renderer::render()
{
- Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
-
if (Q_UNLIKELY(debug_dump())) {
qDebug("\n");
QSGNodeDumper::dump(rootNode());
@@ -2622,8 +4184,13 @@ void Renderer::render()
timer.start();
}
- if (m_vao)
- m_vao->bind();
+ if (!m_rhi) {
+ Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
+ if (m_vao)
+ m_vao->bind();
+ } else {
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ }
if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
bool complete = (m_rebuild & BuildRenderLists) != 0;
@@ -2747,11 +4314,18 @@ void Renderer::render()
m_renderOrderRebuildLower = -1;
m_renderOrderRebuildUpper = -1;
- if (m_visualizeMode != VisualizeNothing)
- visualize();
+ if (!m_rhi) {
+ if (m_visualizeMode != VisualizeNothing)
+ visualize();
- if (m_vao)
- m_vao->release();
+ if (m_vao)
+ m_vao->release();
+ } else {
+ if (m_resourceUpdates) {
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+ }
}
struct RenderNodeState : public QSGRenderNode::RenderState
@@ -2801,8 +4375,8 @@ void Renderer::renderRenderNode(Batch *batch)
RenderNodeState state;
state.m_projectionMatrix = &pm;
- state.m_scissorEnabled = m_currentClipType & ScissorClip;
- state.m_stencilEnabled = m_currentClipType & StencilClip;
+ state.m_scissorEnabled = m_currentClipType & ClipState::ScissorClip;
+ state.m_stencilEnabled = m_currentClipType & ClipState::StencilClip;
state.m_scissorRect = m_currentScissorRect;
state.m_stencilValue = m_currentStencilValue;
@@ -2863,7 +4437,7 @@ void Renderer::renderRenderNode(Batch *batch)
if (changes & (QSGRenderNode::StencilState | QSGRenderNode::ScissorState)) {
glDisable(GL_SCISSOR_TEST);
m_currentClip = nullptr;
- m_currentClipType = NoClip;
+ m_currentClipType = ClipState::NoClip;
}
if (changes & QSGRenderNode::DepthState)
@@ -2886,11 +4460,6 @@ void Renderer::renderRenderNode(Batch *batch)
glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
-void Renderer::releaseCachedResources()
-{
- m_shaderManager->invalidated();
-}
-
class VisualizeShader : public QOpenGLShaderProgram
{
public:
@@ -2941,7 +4510,7 @@ void Renderer::visualizeBatch(Batch *b)
if (b->merged) {
shader->setUniformValue(shader->matrix, matrix);
- const auto &dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
+ const char *dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
for (int ds=0; ds<b->drawSets.size(); ++ds) {
const DrawSet &set = b->drawSets.at(ds);
glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices));
@@ -3198,6 +4767,61 @@ void Renderer::visualize()
shader->release();
}
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
+{
+ return a.depthTest == b.depthTest
+ && a.depthWrite == b.depthWrite
+ && a.depthFunc == b.depthFunc
+ && a.blending == b.blending
+ && a.srcColor == b.srcColor
+ && a.dstColor == b.dstColor
+ && a.colorWrite == b.colorWrite
+ && a.cullMode == b.cullMode
+ && a.usesScissor == b.usesScissor
+ && a.stencilTest == b.stencilTest
+ && a.sampleCount == b.sampleCount
+ && a.drawMode == b.drawMode
+ && a.lineWidth == b.lineWidth;
+}
+
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+uint qHash(const GraphicsState &s, uint seed) Q_DECL_NOTHROW
+{
+ // do not bother with all fields
+ return seed
+ + s.depthTest * 1000
+ + s.depthWrite * 100
+ + s.depthFunc
+ + s.blending * 10
+ + s.srcColor
+ + s.cullMode
+ + s.usesScissor
+ + s.stencilTest
+ + s.sampleCount;
+}
+
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return a.state == b.state
+ && a.sms->programRhi.program == b.sms->programRhi.program
+ && a.rpDesc == b.rpDesc
+ && a.layoutCompatibleSrb->isLayoutCompatible(b.layoutCompatibleSrb);
+}
+
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+uint qHash(const GraphicsPipelineStateKey &k, uint seed) Q_DECL_NOTHROW
+{
+ return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed) + qHash(k.rpDesc, seed);
+}
+
QT_END_NAMESPACE
}
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 12b48c1451..9dec203e73 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -58,11 +58,14 @@
#include <private/qsgnodeupdater_p.h>
#include <private/qsgrendernode_p.h>
#include <private/qdatabuffer_p.h>
+#include <private/qsgtexture_p.h>
#include <QtCore/QBitArray>
-
+#include <QtCore/QStack>
#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qrhi_p.h>
+
QT_BEGIN_NAMESPACE
class QOpenGLVertexArrayObject;
@@ -300,10 +303,11 @@ struct Buffer {
// Data is only valid while preparing the upload. Exception is if we are using the
// broken IBO workaround or we are using a visualization mode.
char *data;
+ QRhiBuffer *buf;
+ uint nonDynamicChangeCount;
};
struct Element {
-
Element()
: boundsComputed(false)
, boundsOutsideFloatRange(false)
@@ -334,6 +338,8 @@ struct Element {
Rect bounds; // in device coordinates
int order = 0;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
uint boundsComputed : 1;
uint boundsOutsideFloatRange : 1;
@@ -390,6 +396,48 @@ enum BatchCompatibility
BatchIsCompatible
};
+struct ClipState
+{
+ enum ClipTypeBit
+ {
+ NoClip = 0x00,
+ ScissorClip = 0x01,
+ StencilClip = 0x02
+ };
+ Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
+
+ const QSGClipNode *clipList;
+ ClipType type;
+ QRhiScissor scissor;
+ int stencilRef;
+
+ inline void reset();
+};
+
+struct StencilClipState
+{
+ StencilClipState() : drawCalls(1) { }
+
+ bool updateStencilBuffer = false;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+
+ struct StencilDrawCall {
+ int stencilRef;
+ int vertexCount;
+ int indexCount;
+ QRhiCommandBuffer::IndexFormat indexFormat;
+ quint32 vbufOffset;
+ quint32 ibufOffset;
+ quint32 ubufOffset;
+ };
+ QDataBuffer<StencilDrawCall> drawCalls;
+
+ inline void reset();
+};
+
struct Batch
{
Batch() : drawSets(1) {}
@@ -403,6 +451,7 @@ struct Batch
// pseudo-constructor...
void init() {
+ // Only non-reusable members are reset here. See Renderer::newBatch().
first = nullptr;
root = nullptr;
vertexCount = 0;
@@ -413,6 +462,9 @@ struct Batch
positionAttribute = -1;
uploadedThisFrame = false;
isRenderNode = false;
+ ubufDataValid = false;
+ clipState.reset();
+ blendConstant = QColor();
}
Element *first;
@@ -429,16 +481,21 @@ struct Batch
uint needsUpload : 1;
uint merged : 1;
uint isRenderNode : 1;
+ uint ubufDataValid : 1;
mutable uint uploadedThisFrame : 1; // solely for debugging purposes
Buffer vbo;
Buffer ibo;
+ QRhiBuffer *ubuf;
+ ClipState clipState;
+ StencilClipState stencilClipState;
+ QColor blendConstant;
QDataBuffer<DrawSet> drawSets;
};
-// NOTE: Node is zero-allocated by the Allocator.
+// NOTE: Node is zero-initialized by the Allocator.
struct Node
{
QSGNode *sgNode;
@@ -574,10 +631,19 @@ class ShaderManager : public QObject
Q_OBJECT
public:
struct Shader {
- ~Shader() { delete program; }
- int id_zRange;
- int pos_order;
- QSGMaterialShader *program;
+ ~Shader() {
+ delete programRhi.program;
+ delete programGL.program;
+ }
+ struct {
+ QSGMaterialShader *program = nullptr;
+ int pos_order;
+ } programGL;
+ struct {
+ QSGMaterialRhiShader *program = nullptr;
+ QRhiVertexInputLayout inputLayout;
+ QVector<QRhiGraphicsShaderStage> shaderStages;
+ } programRhi;
float lastOpacity;
};
@@ -588,12 +654,16 @@ public:
qDeleteAll(stockShaders);
}
+ void clearCachedRendererData();
+
+ QRhiShaderResourceBindings *srb(const QVector<QRhiShaderResourceBinding> &bindings);
+
public Q_SLOTS:
void invalidated();
public:
- Shader *prepareMaterial(QSGMaterial *material);
- Shader *prepareMaterialNoRewrite(QSGMaterial *material);
+ Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
+ Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
QOpenGLShaderProgram *visualizeProgram;
@@ -603,6 +673,50 @@ private:
QOpenGLShaderProgram *blitProgram;
QSGDefaultRenderContext *context;
+
+ QHash<QVector<QRhiShaderResourceBinding>, QRhiShaderResourceBindings *> srbCache;
+};
+
+struct GraphicsState
+{
+ bool depthTest = false;
+ bool depthWrite = false;
+ QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less;
+ bool blending = false;
+ QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One;
+ QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
+ QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF);
+ QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None;
+ bool usesScissor = false;
+ bool stencilTest = false;
+ int sampleCount = 1;
+ QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
+ float lineWidth = 1.0f;
+};
+
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW;
+
+struct GraphicsPipelineStateKey
+{
+ GraphicsState state;
+ const ShaderManager::Shader *sms;
+ const QRhiRenderPassDescriptor *rpDesc;
+ const QRhiShaderResourceBindings *layoutCompatibleSrb;
+};
+
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW;
+
+struct RenderPassState
+{
+ QRhiViewport viewport;
+ QColor clearColor;
+ QRhiDepthStencilClearValue dsClear;
+ bool viewportSet;
+ bool scissorSet;
};
class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
@@ -625,14 +739,6 @@ protected:
void releaseCachedResources() override;
private:
- enum ClipTypeBit
- {
- NoClip = 0x00,
- ScissorClip = 0x01,
- StencilClip = 0x02
- };
- Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
-
enum RebuildFlag {
BuildRenderListsForTaggedRoots = 0x0001,
BuildRenderLists = 0x0002,
@@ -642,6 +748,7 @@ private:
friend class Updater;
+ void destroyGraphicsResources();
void map(Buffer *buffer, int size, bool isIndexBuf = false);
void unmap(Buffer *buffer, bool isIndexBuf = false);
@@ -658,16 +765,39 @@ private:
void invalidateBatchAndOverlappingRenderOrders(Batch *batch);
void uploadBatch(Batch *b);
- void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount);
+ void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount);
+
+ struct PreparedRenderBatch {
+ const Batch *batch;
+ ShaderManager::Shader *sms;
+ };
void renderBatches();
- void renderMergedBatch(const Batch *batch);
- void renderUnmergedBatch(const Batch *batch);
- ClipType updateStencilClip(const QSGClipNode *clip);
+ bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms);
+ QRhiTexture *dummyTexture();
+ void updateMaterialDynamicData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, QVector<QRhiShaderResourceBinding> *bindings,
+ const Batch *batch, int ubufOffset, int ubufRegionSize);
+ void updateMaterialStaticData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, Batch *batch, bool *gstateChanged);
+ void checkLineWidth(QSGGeometry *g);
+ bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderMergedBatch(PreparedRenderBatch *renderBatch);
+ bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderUnmergedBatch(PreparedRenderBatch *renderBatch);
+ void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e);
+ void renderMergedBatch(const Batch *batch); // GL
+ void renderUnmergedBatch(const Batch *batch); // GL
+ ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
void updateClip(const QSGClipNode *clipList, const Batch *batch);
+ void applyClipStateToGraphicsState();
+ QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch);
+ void updateClipState(const QSGClipNode *clipList, Batch *batch);
+ void enqueueStencilDraw(const Batch *batch);
const QMatrix4x4 &matrixForRoot(Node *node);
void renderRenderNode(Batch *batch);
void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader);
+ void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader);
bool changeBatchRoot(Node *node, Node *newRoot);
void registerBatchRoot(Node *childRoot, Node *parentRoot);
@@ -723,17 +853,21 @@ private:
int m_batchVertexThreshold;
// Stuff used during rendering only...
- ShaderManager *m_shaderManager;
+ ShaderManager *m_shaderManager; // per rendercontext, shared
QSGMaterial *m_currentMaterial;
QSGMaterialShader *m_currentProgram;
+ QSGMaterialRhiShader *m_currentRhiProgram;
ShaderManager::Shader *m_currentShader;
+ ClipState m_currentClipState;
+ // *** legacy (GL) only
QRect m_currentScissorRect;
int m_currentStencilValue;
QOpenGLShaderProgram m_clipProgram;
int m_clipMatrixId;
const QSGClipNode *m_currentClip;
- ClipType m_currentClipType;
+ ClipState::ClipType m_currentClipType;
+ // ***
QDataBuffer<char> m_vertexUploadPool;
QDataBuffer<char> m_indexUploadPool;
@@ -745,6 +879,28 @@ private:
Allocator<Node, 256> m_nodeAllocator;
Allocator<Element, 64> m_elementAllocator;
+
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ uint m_ubufAlignment;
+ bool m_uint32IndexForRhi;
+ GraphicsState m_gstate;
+ RenderPassState m_pstate;
+ QStack<GraphicsState> m_gstateStack;
+ QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
+ QHash<QSGSamplerDescription, QRhiSampler *> m_samplers;
+ QRhiTexture *m_dummyTexture = nullptr;
+
+ struct StencilClipCommonData {
+ QRhiGraphicsPipeline *replacePs = nullptr;
+ QRhiGraphicsPipeline *incrPs = nullptr;
+ QShader vs;
+ QShader fs;
+ QRhiVertexInputLayout inputLayout;
+ QRhiGraphicsPipeline::Topology topology;
+ inline void reset();
+ } m_stencilClipCommon;
+
+ inline int mergedIndexElemSize() const;
};
Batch *Renderer::newBatch()
@@ -753,18 +909,69 @@ Batch *Renderer::newBatch()
int size = m_batchPool.size();
if (size) {
b = m_batchPool.at(size - 1);
+ // vbo, ibo, ubuf, stencil-related buffers are reused
m_batchPool.resize(size - 1);
} else {
b = new Batch();
Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo));
memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO
+ b->ubuf = nullptr;
+ b->stencilClipState.reset();
}
+ // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields
b->init();
return b;
}
+int Renderer::mergedIndexElemSize() const
+{
+ return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16);
}
+void Renderer::StencilClipCommonData::reset()
+{
+ delete replacePs;
+ replacePs = nullptr;
+
+ delete incrPs;
+ incrPs = nullptr;
+
+ vs = QShader();
+ fs = QShader();
+}
+
+void ClipState::reset()
+{
+ clipList = nullptr;
+ type = NoClip;
+ stencilRef = 0;
+}
+
+void StencilClipState::reset()
+{
+ updateStencilBuffer = false;
+
+ delete srb;
+ srb = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ drawCalls.reset();
+}
+
+}
+
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE);
+
QT_END_NAMESPACE
#endif // QSGBATCHRENDERER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index dd701fba5f..5218b6b1d5 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -443,6 +443,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
Q_ASSERT(m_attributes.stride > 0);
#if QT_CONFIG(opengl)
Q_ASSERT_X(indexType != GL_UNSIGNED_INT
+ || !QOpenGLContext::currentContext() // rhi, support for uint cannot be checked here
|| static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions())
->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint),
"QSGGeometry::QSGGeometry",
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 7a916610e3..d17915a842 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -88,7 +88,11 @@ public:
UnsignedShortType = 0x1403,
IntType = 0x1404,
UnsignedIntType = 0x1405,
- FloatType = 0x1406
+ FloatType = 0x1406,
+ Bytes2Type = 0x1407,
+ Bytes3Type = 0x1408,
+ Bytes4Type = 0x1409,
+ DoubleType = 0x140A
};
struct Q_QUICK_EXPORT Attribute
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 8557de1b1f..c8ae26311b 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -39,14 +39,6 @@
#include "qsgmaterial.h"
#include "qsgrenderer_p.h"
-#include "qsgmaterialshader_p.h"
-#if QT_CONFIG(opengl)
-# include <private/qsgshadersourcebuilder_p.h>
-# include <private/qsgdefaultcontext_p.h>
-# include <private/qsgdefaultrendercontext_p.h>
-# include <QtGui/QOpenGLFunctions>
-# include <QtGui/QOpenGLContext>
-#endif
QT_BEGIN_NAMESPACE
@@ -64,17 +56,6 @@ void qsg_set_material_failure()
qsg_material_failure = true;
}
#endif
-#if QT_CONFIG(opengl)
-const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
-{
- const QStringList files = m_sourceFiles[type];
- QSGShaderSourceBuilder builder;
- for (const QString &file : files)
- builder.appendSourceFile(file);
- m_sources[type] = builder.source();
- return m_sources[type].constData();
-}
-#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -89,494 +70,6 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"
\l {scene graph}{Qt Quick Scene Graph}.
*/
-/*!
- \class QSGMaterialShader
- \brief The QSGMaterialShader class represents an OpenGL shader program
- in the renderer.
- \inmodule QtQuick
- \ingroup qtquick-scenegraph-materials
-
- The QSGMaterialShader API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader form a tight relationship. For one
- scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color used to render the geometry.
-
- An instance of QSGMaterialShader is never created explicitly by the user,
- it will be created on demand by the scene graph through
- QSGMaterial::createShader(). The scene graph will make sure that there
- is only one instance of each shader implementation through a scene graph.
-
- The source code returned from vertexShader() is used to control what the
- material does with the vertiex data that comes in from the geometry.
- The source code returned from the fragmentShader() is used to control
- what how the material should fill each individual pixel in the geometry.
- The vertex and fragment source code is queried once during initialization,
- changing what is returned from these functions later will not have
- any effect.
-
- The activate() function is called by the scene graph when a shader is
- is starting to be used. The deactivate function is called by the scene
- graph when the shader is no longer going to be used. While active,
- the scene graph may make one or more calls to updateState() which
- will update the state of the shader for each individual geometry to
- render.
-
- The attributeNames() returns the name of the attributes used in the
- vertexShader(). These are used in the default implementation of
- activate() and deactivate() to decide whice vertex registers are enabled.
-
- The initialize() function is called during program creation to allow
- subclasses to prepare for use, such as resolve uniform names in the
- vertexShader() and fragmentShader().
-
- A minimal example:
- \code
- class Shader : public QSGMaterialShader
- {
- public:
- const char *vertexShader() const {
- return
- "attribute highp vec4 vertex; \n"
- "uniform highp mat4 matrix; \n"
- "void main() { \n"
- " gl_Position = matrix * vertex; \n"
- "}";
- }
-
- const char *fragmentShader() const {
- return
- "uniform lowp float opacity; \n"
- "void main() { \n"
- " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
- "}";
- }
-
- char const *const *attributeNames() const
- {
- static char const *const names[] = { "vertex", 0 };
- return names;
- }
-
- void initialize()
- {
- QSGMaterialShader::initialize();
- m_id_matrix = program()->uniformLocation("matrix");
- m_id_opacity = program()->uniformLocation("opacity");
- }
-
- void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
- {
- Q_ASSERT(program()->isLinked());
- if (state.isMatrixDirty())
- program()->setUniformValue(m_id_matrix, state.combinedMatrix());
- if (state.isOpacityDirty())
- program()->setUniformValue(m_id_opacity, state.opacity());
- }
-
- private:
- int m_id_matrix;
- int m_id_opacity;
- };
- \endcode
-
- \note All classes with QSG prefix should be used solely on the scene graph's
- rendering thread. See \l {Scene Graph and Rendering} for more information.
-
- */
-
-
-
-/*!
- Creates a new QSGMaterialShader.
- */
-QSGMaterialShader::QSGMaterialShader()
- : d_ptr(new QSGMaterialShaderPrivate)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
- : d_ptr(&dd)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::~QSGMaterialShader()
-{
-}
-
-/*!
- \fn char const *const *QSGMaterialShader::attributeNames() const
-
- Returns a zero-terminated array describing the names of the
- attributes used in the vertex shader.
-
- This function is called when the shader is compiled to specify
- which attributes exist. The order of the attribute names
- defines the attribute register position in the vertex shader.
- */
-
-#if QT_CONFIG(opengl)
-/*!
- \fn const char *QSGMaterialShader::vertexShader() const
-
- Called when the shader is being initialized to get the vertex
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::vertexShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Vertex);
-}
-
-
-/*!
- \fn const char *QSGMaterialShader::fragmentShader() const
-
- Called when the shader is being initialized to get the fragment
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::fragmentShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Fragment);
-}
-
-
-/*!
- \fn QOpenGLShaderProgram *QSGMaterialShader::program()
-
- Returns the shader program used by this QSGMaterialShader.
- */
-#endif
-
-/*!
- \fn void QSGMaterialShader::initialize()
-
- Reimplement this function to do one-time initialization when the
- shader program is compiled. The OpenGL shader program is compiled
- and linked, but not bound, when this function is called.
- */
-
-
-/*!
- This function is called by the scene graph to indicate that geometry is
- about to be rendered using this shader.
-
- State that is global for all uses of the shader, independent of the geometry
- that is being drawn, can be setup in this function.
- */
-
-void QSGMaterialShader::activate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph to indicate that geometry will
- no longer to be rendered using this shader.
- */
-
-void QSGMaterialShader::deactivate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph before geometry is rendered
- to make sure the shader is in the right state.
-
- The current rendering \a state is passed from the scene graph. If the state
- indicates that any state is dirty, the updateState implementation must
- update accordingly for the geometry to render correctly.
-
- The subclass specific state, such as the color of a flat color material, should
- be extracted from \a newMaterial to update the color uniforms accordingly.
-
- The \a oldMaterial can be used to minimze state changes when updating
- material states. The \a oldMaterial is 0 if this shader was just activated.
-
- \sa activate(), deactivate()
- */
-
-void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
-{
-}
-
-#if QT_CONFIG(opengl)
-/*!
- Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function.
-
- This function is useful when you have a single source file for a given shader
- stage. If your shader consists of multiple source files then use
- setShaderSourceFiles()
-
- \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = (QStringList() << sourceFile);
-}
-
-/*!
- Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function in the order given.
-
- \sa setShaderSourceFile(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = sourceFiles;
-}
-
-/*!
- This function is called when the shader is initialized to compile the
- actual QOpenGLShaderProgram. Do not call it explicitly.
-
- The default implementation will extract the vertexShader() and
- fragmentShader() and bind the names returned from attributeNames()
- to consecutive vertex attribute registers starting at 0.
- */
-
-void QSGMaterialShader::compile()
-{
- Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
-
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
-
- char const *const *attr = attributeNames();
-#ifndef QT_NO_DEBUG
- int maxVertexAttribs = 0;
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
- for (int i = 0; attr[i]; ++i) {
- if (i >= maxVertexAttribs) {
- qFatal("List of attribute names is either too long or not null-terminated.\n"
- "Maximum number of attributes on this hardware is %i.\n"
- "Vertex shader:\n%s\n"
- "Fragment shader:\n%s\n",
- maxVertexAttribs, vertexShader(), fragmentShader());
- }
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#else
- for (int i = 0; attr[i]; ++i) {
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#endif
-
- if (!program()->link()) {
- qWarning("QSGMaterialShader: Shader compilation failed:");
- qWarning() << program()->log();
- }
-}
-
-#endif
-
-/*!
- \class QSGMaterialShader::RenderState
- \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
- during a call to QSGMaterialShader::updateState().
- \inmodule QtQuick
-
- The render state contains a number of accessors that the shader needs to respect
- in order to conform to the current state of the scene graph.
-
- The instance is only valid inside a call to QSGMaterialShader::updateState() and
- should not be used outisde this function.
- */
-
-
-
-/*!
- \enum QSGMaterialShader::RenderState::DirtyState
-
- \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
-
- \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
-
- \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
-
- \value DirtyAll Used to indicate that everything needs to be updated.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
-
- Returns \c true if the dirtyStates() contain the dirty matrix state,
- otherwise returns \c false.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty opacity state,
- otherwise returns \c false.
- */
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty cached material state,
- otherwise returns \c false.
- */
-
-/*!
- \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
-
- Returns which rendering states that have changed and needs to be updated
- for geometry rendered with this material to conform to the current
- rendering state.
- */
-
-
-
-/*!
- Returns the accumulated opacity to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::opacity() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
-}
-
-/*!
- Returns the modelview determinant to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::determinant() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->determinant();
-}
-
-/*!
- Returns the matrix combined of modelview matrix and project matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
-}
-/*!
- Returns the ratio between physical pixels and device-independent pixels
- to be used for rendering.
-*/
-float QSGMaterialShader::RenderState::devicePixelRatio() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
-}
-
-
-
-/*!
- Returns the model view matrix.
-
- If the material has the RequiresFullMatrix flag
- set, this is guaranteed to be the complete transform
- matrix calculated from the scenegraph.
-
- However, if this flag is not set, the renderer may
- choose to alter this matrix. For example, it may
- pre-transform vertices on the CPU and set this matrix
- to identity.
-
- In a situation such as the above, it is still possible
- to retrieve the actual matrix determinant by setting
- the RequiresDeterminant flag in the material and
- calling the determinant() accessor.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
-}
-
-/*!
- Returns the projection matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
-}
-
-
-
-/*!
- Returns the viewport rect of the surface being rendered to.
- */
-
-QRect QSGMaterialShader::RenderState::viewportRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->viewportRect();
-}
-
-
-
-/*!
- Returns the device rect of the surface being rendered to
- */
-
-QRect QSGMaterialShader::RenderState::deviceRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->deviceRect();
-}
-
-#if QT_CONFIG(opengl)
-
-/*!
- Returns the QOpenGLContext that is being used for rendering
- */
-
-QOpenGLContext *QSGMaterialShader::RenderState::context() const
-{
- // Only the QSGDefaultRenderContext will have an OpenGL Context to query
- auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
- if (openGLRenderContext != nullptr)
- return openGLRenderContext->openglContext();
- else
- return nullptr;
-}
-
-#endif
-
#ifndef QT_NO_DEBUG
static int qt_material_count = 0;
@@ -605,22 +98,20 @@ static void qt_print_material_count()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- The QSGMaterial API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For
- one scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color to used to render the geometry.
-
- The QSGMaterial has two virtual functions that both need to be implemented.
- The function type() should return a unique instance for all instances of a
+ The QSGMaterial, QSGMaterialShader and QSGMaterialRhiShader subclasses
+ form a tight relationship. For one scene graph (including nested graphs),
+ there is one unique QSGMaterialShader or QSGMaterialRhiShader instance
+ which encapsulates the shaders the scene graph uses to render that
+ material, such as a shader to flat coloring of geometry. Each
+ QSGGeometryNode can have a unique QSGMaterial containing the how the shader
+ should be configured when drawing that node, such as the actual color to
+ used to render the geometry.
+
+ QSGMaterial has two virtual functions that both need to be implemented. The
+ function type() should return a unique instance for all instances of a
specific subclass. The createShader() function should return a new instance
- of QSGMaterialShader, specific to the subclass of QSGMaterial.
+ of QSGMaterialShader or QSGMaterialRhiShader, specific to that subclass of
+ QSGMaterial.
A minimal QSGMaterial implementation could look like this:
\code
@@ -632,6 +123,27 @@ static void qt_print_material_count()
};
\endcode
+ This is suitable only for the OpenGL-based, traditional renderer of the
+ scene graph. When using the new, graphics API abstracted renderer,
+ materials must create QSGMaterialRhiShader instances instead, or in
+ addition:
+ \code
+ class Material : public QSGMaterial
+ {
+ public:
+ Material() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+ QSGMaterialShader *createShader() {
+ if (flags().testFlag(RhiShaderWanted)) {
+ return new RhiShader;
+ } else {
+ // this is optional, relevant for materials that intend to be usable with the legacy OpenGL renderer as well
+ return new Shader;
+ }
+ }
+ };
+ \endcode
+
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
*/
@@ -693,6 +205,16 @@ QSGMaterial::~QSGMaterial()
QSGMaterialShader::compile() when its shader program is compiled and linked.
Set this flag to enforce that the function is called.
+ \value SupportsRhiShader Starting with Qt 5.14, the scene graph supports
+ QSGMaterialRhiShader as an alternative to the OpenGL-specific
+ QSGMaterialShader. Set this flag to indicate createShader() is capable of
+ returning QSGMaterialRhiShader instances when the RhiShaderWanted flag is
+ set.
+
+ \value RhiShaderWanted This flag is set by the scene graph, not by the
+ QSGMaterial. When set, and that can only happen when SupportsRhiShader was
+ set by the material, it indicates that createShader() must return a
+ QSGMaterialRhiShader instance instead of QSGMaterialShader.
*/
/*!
@@ -757,7 +279,11 @@ int QSGMaterial::compare(const QSGMaterial *other) const
The function will be called only once for each material type that
exists in the scene graph and will be cached internally.
-*/
+ When the QSGMaterial reports SupportsRhiShader in flags(), the scene graph
+ may request a QSGMaterialRhiShader instead of QSGMaterialShader. This is
+ indicated by having the RhiShaderWanted flag set. In this case the return
+ value must be a QSGRhiMaterialShader subclass.
+*/
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index c002cd5d5e..cb6e9a456a 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -41,95 +41,12 @@
#define QSGMATERIAL_H
#include <QtQuick/qtquickglobal.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/qopenglshaderprogram.h>
-#endif
-#include <QtGui/QMatrix4x4>
-#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialshader.h>
+#include <QtQuick/qsgmaterialrhishader.h>
+#include <QtQuick/qsgmaterialtype.h>
QT_BEGIN_NAMESPACE
-class QSGMaterial;
-class QSGMaterialShaderPrivate;
-
-namespace QSGBatchRenderer {
- class ShaderManager;
-}
-
-class Q_QUICK_EXPORT QSGMaterialShader
-{
-public:
- class Q_QUICK_EXPORT RenderState {
- public:
- enum DirtyState
- {
- DirtyMatrix = 0x0001,
- DirtyOpacity = 0x0002,
- DirtyCachedMaterialData = 0x0004,
- DirtyAll = 0xFFFF
- };
- Q_DECLARE_FLAGS(DirtyStates, DirtyState)
-
- inline DirtyStates dirtyStates() const { return m_dirty; }
-
- inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
- inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
- bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
-
- float opacity() const;
- QMatrix4x4 combinedMatrix() const;
- QMatrix4x4 modelViewMatrix() const;
- QMatrix4x4 projectionMatrix() const;
- QRect viewportRect() const;
- QRect deviceRect() const;
- float determinant() const;
- float devicePixelRatio() const;
-#if QT_CONFIG(opengl)
- QOpenGLContext *context() const;
-#endif
- private:
- friend class QSGRenderer;
- DirtyStates m_dirty;
- const void *m_data;
- };
-
- QSGMaterialShader();
- virtual ~QSGMaterialShader();
-
- virtual void activate();
- virtual void deactivate();
- // First time a material is used, oldMaterial is null.
- virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
- virtual char const *const *attributeNames() const = 0; // Array must end with null.
-#if QT_CONFIG(opengl)
- inline QOpenGLShaderProgram *program() { return &m_program; }
-#endif
-protected:
- Q_DECLARE_PRIVATE(QSGMaterialShader)
- QSGMaterialShader(QSGMaterialShaderPrivate &dd);
-
- friend class QSGDefaultRenderContext;
- friend class QSGBatchRenderer::ShaderManager;
-#if QT_CONFIG(opengl)
- void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
- void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
-
- virtual void compile();
-#endif
- virtual void initialize() { }
-#if QT_CONFIG(opengl)
- virtual const char *vertexShader() const;
- virtual const char *fragmentShader() const;
-#endif
-private:
-#if QT_CONFIG(opengl)
- QOpenGLShaderProgram m_program;
-#endif
- QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
-};
-
-struct QSGMaterialType { };
-
class Q_QUICK_EXPORT QSGMaterial
{
public:
@@ -139,7 +56,11 @@ public:
RequiresFullMatrixExceptTranslate = 0x0004 | RequiresDeterminant, // Allow precalculated translation
RequiresFullMatrix = 0x0008 | RequiresFullMatrixExceptTranslate,
- CustomCompileStep = 0x0010
+ CustomCompileStep = 0x0010,
+
+ SupportsRhiShader = 0x0020,
+
+ RhiShaderWanted = 0x1000 // // ### Qt 6: remove
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -160,7 +81,6 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterial::Flags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
new file mode 100644
index 0000000000..03f74df6b7
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialrhishader_p.h"
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGMaterialRhiShader
+ \brief The QSGMaterialRhiShader class represents a graphics API independent shader program.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+ \since 5.14
+
+ QSGMaterialRhiShader is a modern, cross-platform alternative to
+ QSGMaterialShader. The latter is tied to OpenGL and GLSL by design, whereas
+ QSGMaterialRhiShader is based on QShader, a container for multiple
+ versions of a graphics shader together with reflection information.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::Flag
+ Flag values to indicate special material properties.
+
+ \value UpdatesGraphicsPipelineState Setting this flag enables calling
+ updateGraphicsPipelineState().
+ */
+
+QShader QSGMaterialRhiShaderPrivate::loadShader(const QString &filename)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to find shader" << filename;
+ return QShader();
+ }
+ return QShader::fromSerialized(f.readAll());
+}
+
+void QSGMaterialRhiShaderPrivate::clearCachedRendererData()
+{
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ textureBindingTable[i] = nullptr;
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ samplerBindingTable[i] = nullptr;
+}
+
+static inline QRhiShaderResourceBinding::StageFlags toSrbStage(QShader::Stage stage)
+{
+ switch (stage) {
+ case QShader::VertexStage:
+ return QRhiShaderResourceBinding::VertexStage;
+ case QShader::FragmentStage:
+ return QRhiShaderResourceBinding::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+void QSGMaterialRhiShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
+{
+ ubufBinding = -1;
+ ubufSize = 0;
+ ubufStages = 0;
+ memset(combinedImageSamplerBindings, 0, sizeof(combinedImageSamplerBindings));
+ vertexShader = fragmentShader = nullptr;
+ masterUniformData.clear();
+
+ clearCachedRendererData();
+
+ for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) {
+ auto it = shaderFileNames.find(stage);
+ if (it != shaderFileNames.end()) {
+ QString fn = *it;
+ const QShader s = loadShader(*it);
+ if (!s.isValid())
+ continue;
+ shaders[stage] = ShaderStageData(s);
+ // load only once, subsequent prepare() calls will have it all in shaders already
+ shaderFileNames.erase(it);
+ }
+ }
+
+ auto vsIt = shaders.find(QShader::VertexStage);
+ if (vsIt != shaders.end()) {
+ vsIt->shaderVariant = vertexShaderVariant;
+ vsIt->vertexInputLocations.clear();
+ vsIt->qt_order_attrib_location = -1;
+
+ const QShaderDescription desc = vsIt->shader.description();
+ const QVector<QShaderDescription::InOutVariable> vertexInputs = desc.inputVariables();
+ for (const QShaderDescription::InOutVariable &v : vertexInputs) {
+ const QByteArray name = v.name.toUtf8();
+ if (vertexShaderVariant == QShader::BatchableVertexShader
+ && name == QByteArrayLiteral("_qt_order"))
+ {
+ vsIt->qt_order_attrib_location = v.location;
+ } else {
+ vsIt->vertexInputLocations.append(v.location);
+ }
+ }
+
+ if (vsIt->vertexInputLocations.contains(vsIt->qt_order_attrib_location)) {
+ qWarning("Vertex input clash in rewritten (batchable) vertex shader at input location %d. "
+ "Vertex shaders must avoid using this location.", vsIt->qt_order_attrib_location);
+ }
+ }
+
+ for (auto it = shaders.begin(); it != shaders.end(); ++it) {
+ const QShaderDescription desc = it->shader.description();
+
+ const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
+ const int ubufCount = ubufs.count();
+ if (ubufCount > 1) {
+ qWarning("Multiple uniform blocks found in shader. "
+ "This should be avoided as Qt Quick supports only one.");
+ }
+ for (int i = 0; i < ubufCount; ++i) {
+ const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
+ if (ubufBinding == -1 && ubuf.binding >= 0) {
+ ubufBinding = ubuf.binding;
+ ubufSize = ubuf.size;
+ ubufStages |= toSrbStage(it->shader.stage());
+ masterUniformData.fill('\0', ubufSize);
+ } else if (ubufBinding == ubuf.binding && ubuf.binding >= 0) {
+ if (ubuf.size > ubufSize) {
+ ubufSize = ubuf.size;
+ masterUniformData.fill('\0', ubufSize);
+ }
+ ubufStages |= toSrbStage(it->shader.stage());
+ } else {
+ qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
+ }
+ }
+
+ const QVector<QShaderDescription::InOutVariable> imageSamplers = desc.combinedImageSamplers();
+ const int imageSamplersCount = imageSamplers.count();
+ for (int i = 0; i < imageSamplersCount; ++i) {
+ const QShaderDescription::InOutVariable &var(imageSamplers[i]);
+ if (var.binding >= 0 && var.binding < MAX_SHADER_RESOURCE_BINDINGS)
+ combinedImageSamplerBindings[var.binding] |= toSrbStage(it->shader.stage());
+ else
+ qWarning("Encountered invalid combined image sampler (%s) binding %d",
+ qPrintable(var.name), var.binding);
+ }
+
+ if (it.key() == QShader::VertexStage)
+ vertexShader = &it.value();
+ else if (it.key() == QShader::FragmentStage)
+ fragmentShader = &it.value();
+ }
+
+ if (vertexShader && vertexShaderVariant == QShader::BatchableVertexShader && vertexShader->qt_order_attrib_location == -1)
+ qWarning("No rewriter-inserted attribute found, this should not happen.");
+}
+
+/*!
+ Constructs a new QSGMaterialRhiShader.
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader()
+ : d_ptr(new QSGMaterialRhiShaderPrivate(this))
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::~QSGMaterialRhiShader()
+{
+}
+
+// We have our own enum as QShader is not initially public. Internally
+// everything works with QShader::Stage however. So convert.
+static inline QShader::Stage toShaderStage(QSGMaterialRhiShader::Stage stage)
+{
+ switch (stage) {
+ case QSGMaterialRhiShader::VertexStage:
+ return QShader::VertexStage;
+ case QSGMaterialRhiShader::FragmentStage:
+ return QShader::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ return QShader::VertexStage;
+ }
+}
+
+/*!
+ Sets the \a shader for the specified \a stage.
+ */
+void QSGMaterialRhiShader::setShader(Stage stage, const QShader &shader)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaders[toShaderStage(stage)] = QSGMaterialRhiShaderPrivate::ShaderStageData(shader);
+}
+
+/*!
+ Sets the \a filename for the shader for the specified \a stage.
+
+ The file is expected to contain a serialized QRhiShader.
+ */
+void QSGMaterialRhiShader::setShaderFileName(Stage stage, const QString &filename)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaderFileNames[toShaderStage(stage)] = filename;
+}
+
+/*!
+ \return the currently set flags for this material shader.
+ */
+QSGMaterialRhiShader::Flags QSGMaterialRhiShader::flags() const
+{
+ Q_D(const QSGMaterialRhiShader);
+ return d->flags;
+}
+
+/*!
+ Sets the \a flags on this material shader if \a on is true;
+ otherwise clears the specified flags.
+*/
+void QSGMaterialRhiShader::setFlag(Flags flags, bool on)
+{
+ Q_D(QSGMaterialRhiShader);
+ if (on)
+ d->flags |= flags;
+ else
+ d->flags &= ~flags;
+}
+
+/*!
+ This function is called by the scene graph to get the contents of the
+ shader program's uniform buffer updated. The implementation is not expected
+ to perform any real graphics operations, it is merely responsible for
+ copying data to the QByteArray returned from RenderState::uniformData().
+ The scene graph takes care of making that buffer visible in the shaders.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any relevant state is dirty, the implementation must update
+ the appropriate region in the buffer data that is accessible via
+ RenderState::uniformData(). When a state, such as, matrix or opacity, is
+ not dirty, there is no need to touch the corresponding region since the
+ data is persistent.
+
+ The return value must be \c true whenever any change was made to the uniform data.
+
+ The subclass specific state, such as the color of a flat color material,
+ should be extracted from \a newMaterial to update the relevant regions in
+ the buffer accordingly.
+
+ \a oldMaterial can be used to minimize buffer changes (which are typically
+ memcpy calls) when updating material states. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ This function is called by the scene graph to prepare using a sampled image
+ in the shader, typically in form of a combined image sampler.
+
+ \a binding is the binding number of the sampler. The function is called for
+ each variable in the material's shaders'
+ \l{QShaderDescription::combinedImageSamplers()}.
+
+ When \c{*texture} is null, it must be set to a QSGTexture pointer before
+ returning. When non-null, it is up to the material to decide if a new
+ \c{QSGTexture *} is stored to it, or if it updates some parameters on the
+ already known QSGTexture. The ownership of the QSGTexture is not
+ transferred.
+
+ The current rendering \a state is passed from the scene graph. It is up to
+ the material to enqueue the texture data uploads to the
+ QRhiResourceUpdateBatch retriveable via RenderState::resourceUpdateBatch().
+
+ The subclass specific state can be extracted from \a newMaterial.
+
+ \a oldMaterial can be used to minimize changes. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+void QSGMaterialRhiShader::updateSampledImage(const RenderState &state,
+ int binding,
+ QSGTexture **texture,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(binding);
+ Q_UNUSED(texture);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+}
+
+/*!
+ This function is called by the scene graph to enable the material to
+ provide a custom set of graphics state. The set of states that are
+ customizable by material is limited to blending and related settings.
+
+ \note This function is only called when the UpdatesGraphicsPipelineState
+ flag was enabled via setFlags(). By default it is not set, and so this
+ function is never called.
+
+ The return value must be \c true whenever a change was made to any of the
+ members in \a ps.
+
+ \note The contents of \a ps is not persistent between invocations of this
+ function.
+
+ The current rendering \a state is passed from the scene graph.
+
+ The subclass specific state can be extracted from \a newMaterial. When \a
+ oldMaterial is null, this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(ps);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ \class QSGMaterialRhiShader::RenderState
+
+ \brief Encapsulates the current rendering state during a call to
+ QSGMaterialRhiShader::updateUniformData() and the other \c update type of
+ functions.
+
+ \inmodule QtQuick
+ \since 5.14
+
+ The render state contains a number of accessors that the shader needs to
+ respect in order to conform to the current state of the scene graph.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be
+ updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must
+ be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialRhiShader::RenderState::DirtyStates QSGMaterialRhiShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialRhiShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag set, this is guaranteed to
+ be the complete transform matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may choose to alter this
+ matrix. For example, it may pre-transform vertices on the CPU and set this
+ matrix to identity.
+
+ In a situation such as the above, it is still possible to retrieve the
+ actual matrix determinant by setting the RequiresDeterminant flag in the
+ material and calling the determinant() accessor.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+QRect QSGMaterialRhiShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+QRect QSGMaterialRhiShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+/*!
+ Returns a pointer to the data for the uniform (constant) buffer in the
+ shader.
+
+ \note It is strongly recommended to declare the uniform block with \c
+ std140 in the shader, and to carefully study the standard uniform block
+ layout as described in section 7.6.2.2 of the OpenGL specification. It is
+ up to the QSGMaterialRhiShader implementation to ensure data gets placed
+ at the right location in this QByteArray, taking alignment requirements
+ into account. Shader code translated to other shading languages is expected
+ to use the same offsets for block members, even when the target language
+ uses different packing rules by default.
+
+ \note Avoid copying from C++ POD types, such as, structs, in order to
+ update multiple members at once, unless it has been verified that the
+ layouts of the C++ struct and the GLSL uniform block match.
+
+ \note Uniform data must only be updated from
+ QSGMaterialRhiShader::updateUniformData().
+ */
+QByteArray *QSGMaterialRhiShader::RenderState::uniformData() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentUniformData();
+}
+
+/*!
+ Returns a resource update batch to which upload and copy operatoins can be
+ queued. This is typically used by
+ QSGMaterialRhiShader::updateSampledImage() to enqueue texture image
+ content updates.
+ */
+QRhiResourceUpdateBatch *QSGMaterialRhiShader::RenderState::resourceUpdateBatch() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentResourceUpdateBatch();
+}
+
+/*!
+ Returns the current QRhi.
+ */
+QRhi *QSGMaterialRhiShader::RenderState::rhi() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentRhi();
+}
+
+char const *const *QSGMaterialRhiShader::attributeNames() const
+{
+ Q_ASSERT_X(false, "QSGMaterialRhiShader::attributeNames()", "Not implemented for RHI");
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
new file mode 100644
index 0000000000..01991135ec
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMATERIALRHISHADER_H
+#define QSGMATERIALRHISHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtCore/QRect>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/QColor>
+#include <QtQuick/qsgmaterialshader.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialRhiShaderPrivate;
+class QSGTexture;
+class QRhiResourceUpdateBatch;
+class QRhi;
+class QShader;
+
+class Q_QUICK_EXPORT QSGMaterialRhiShader : public QSGMaterialShader // ### Qt 6: remove inheritance
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ using DirtyStates = QSGMaterialShader::RenderState::DirtyStates;
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyOpacity; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+
+ QByteArray *uniformData() const;
+ QRhiResourceUpdateBatch *resourceUpdateBatch() const;
+ QRhi *rhi() const;
+
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ struct Q_QUICK_EXPORT GraphicsPipelineState {
+ enum BlendFactor {
+ Zero,
+ One,
+ SrcColor,
+ OneMinusSrcColor,
+ DstColor,
+ OneMinusDstColor,
+ SrcAlpha,
+ OneMinusSrcAlpha,
+ DstAlpha,
+ OneMinusDstAlpha,
+ ConstantColor,
+ OneMinusConstantColor,
+ ConstantAlpha,
+ OneMinusConstantAlpha,
+ SrcAlphaSaturate,
+ Src1Color,
+ OneMinusSrc1Color,
+ Src1Alpha,
+ OneMinusSrc1Alpha
+ };
+
+ enum ColorMaskComponent {
+ R = 1 << 0,
+ G = 1 << 1,
+ B = 1 << 2,
+ A = 1 << 3
+ };
+ Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent)
+
+ enum CullMode {
+ CullNone,
+ CullFront,
+ CullBack
+ };
+
+ bool blendEnable;
+ BlendFactor srcColor;
+ BlendFactor dstColor;
+ ColorMask colorWrite;
+ QColor blendConstant;
+ CullMode cullMode;
+ // This struct is extensible while keeping BC since apps only ever get
+ // a ptr to the struct, it is not created by them.
+ };
+
+ enum Flag {
+ UpdatesGraphicsPipelineState = 0x0001
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Stage {
+ VertexStage,
+ FragmentStage,
+ };
+
+ QSGMaterialRhiShader();
+ virtual ~QSGMaterialRhiShader();
+
+ virtual bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ Flags flags() const;
+ void setFlag(Flags flags, bool on = true);
+
+ // dummy impl for base class pure virtual, never called
+ char const *const *attributeNames() const override;
+
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialRhiShader)
+ QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd);
+
+ // filename is for a file containing a serialized QShader.
+ void setShaderFileName(Stage stage, const QString &filename);
+
+ void setShader(Stage stage, const QShader &shader);
+
+private:
+ QScopedPointer<QSGMaterialRhiShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::GraphicsPipelineState::ColorMask)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::Flags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
new file mode 100644
index 0000000000..153b4b120a
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMATERIALRHISHADER_P_H
+#define QSGMATERIALRHISHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+#include "qsgmaterialrhishader.h"
+#include "qsgmaterial.h"
+#include <QtGui/private/qrhi_p.h>
+#include <QtGui/private/qshader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiSampler;
+
+class Q_QUICK_PRIVATE_EXPORT QSGMaterialRhiShaderPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QSGMaterialRhiShader)
+
+ QSGMaterialRhiShaderPrivate(QSGMaterialRhiShader *q) : q_ptr(q) { }
+ static QSGMaterialRhiShaderPrivate *get(QSGMaterialRhiShader *s) { return s->d_func(); }
+ static const QSGMaterialRhiShaderPrivate *get(const QSGMaterialRhiShader *s) { return s->d_func(); }
+
+ void clearCachedRendererData();
+ void prepare(QShader::Variant vertexShaderVariant);
+
+ QShader shader(QShader::Stage stage) const { return shaders[stage].shader; }
+
+ static QShader loadShader(const QString &filename);
+
+ QSGMaterialRhiShader *q_ptr;
+ QHash<QShader::Stage, QString> shaderFileNames;
+ QSGMaterialRhiShader::Flags flags;
+
+ struct ShaderStageData {
+ ShaderStageData() { } // so shader.isValid() == false
+ ShaderStageData(const QShader &shader) : shader(shader) { }
+ QShader shader;
+ QShader::Variant shaderVariant = QShader::StandardShader;
+ QVector<int> vertexInputLocations; // excluding rewriter-inserted ones
+ int qt_order_attrib_location = -1; // rewriter-inserted
+ };
+ QHash<QShader::Stage, ShaderStageData> shaders;
+
+ static const int MAX_SHADER_RESOURCE_BINDINGS = 32;
+
+ int ubufBinding = -1;
+ int ubufSize = 0;
+ QRhiShaderResourceBinding::StageFlags ubufStages;
+ QRhiShaderResourceBinding::StageFlags combinedImageSamplerBindings[MAX_SHADER_RESOURCE_BINDINGS];
+
+ ShaderStageData *vertexShader = nullptr;
+ ShaderStageData *fragmentShader = nullptr;
+
+ QByteArray masterUniformData;
+
+ QSGTexture *textureBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+ QRhiSampler *samplerBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+};
+
+Q_DECLARE_TYPEINFO(QSGMaterialRhiShaderPrivate::ShaderStageData, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QSGMATERIALRHISHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
new file mode 100644
index 0000000000..d614f9be4c
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialshader_p.h"
+#if QT_CONFIG(opengl)
+# include <private/qsgshadersourcebuilder_p.h>
+# include <private/qsgdefaultcontext_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <QtGui/QOpenGLFunctions>
+# include <QtGui/QOpenGLContext>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(opengl)
+const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
+{
+ const QStringList files = m_sourceFiles[type];
+ QSGShaderSourceBuilder builder;
+ for (const QString &file : files)
+ builder.appendSourceFile(file);
+ m_sources[type] = builder.source();
+ return m_sources[type].constData();
+}
+#endif
+
+/*!
+ \class QSGMaterialShader
+ \brief The QSGMaterialShader class represents an OpenGL shader program
+ in the renderer.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+
+ The QSGMaterialShader API is relatively low-level. A more convenient API,
+ which provides almost all the same features, is available through
+ QSGSimpleMaterialShader.
+
+ \warning This class is only functional when running with the legacy OpenGL
+ renderer of the Qt Quick scenegraph.
+
+ The QSGMaterial and QSGMaterialShader form a tight relationship. For one
+ scene graph (including nested graphs), there is one unique QSGMaterialShader
+ instance which encapsulates the QOpenGLShaderProgram the scene graph uses
+ to render that material, such as a shader to flat coloring of geometry.
+ Each QSGGeometryNode can have a unique QSGMaterial containing the
+ how the shader should be configured when drawing that node, such as
+ the actual color used to render the geometry.
+
+ An instance of QSGMaterialShader is never created explicitly by the user,
+ it will be created on demand by the scene graph through
+ QSGMaterial::createShader(). The scene graph will make sure that there
+ is only one instance of each shader implementation through a scene graph.
+
+ The source code returned from vertexShader() is used to control what the
+ material does with the vertiex data that comes in from the geometry.
+ The source code returned from the fragmentShader() is used to control
+ what how the material should fill each individual pixel in the geometry.
+ The vertex and fragment source code is queried once during initialization,
+ changing what is returned from these functions later will not have
+ any effect.
+
+ The activate() function is called by the scene graph when a shader is
+ is starting to be used. The deactivate function is called by the scene
+ graph when the shader is no longer going to be used. While active,
+ the scene graph may make one or more calls to updateState() which
+ will update the state of the shader for each individual geometry to
+ render.
+
+ The attributeNames() returns the name of the attributes used in the
+ vertexShader(). These are used in the default implementation of
+ activate() and deactivate() to decide whice vertex registers are enabled.
+
+ The initialize() function is called during program creation to allow
+ subclasses to prepare for use, such as resolve uniform names in the
+ vertexShader() and fragmentShader().
+
+ A minimal example:
+ \code
+ class Shader : public QSGMaterialShader
+ {
+ public:
+ const char *vertexShader() const {
+ return
+ "attribute highp vec4 vertex; \n"
+ "uniform highp mat4 matrix; \n"
+ "void main() { \n"
+ " gl_Position = matrix * vertex; \n"
+ "}";
+ }
+
+ const char *fragmentShader() const {
+ return
+ "uniform lowp float opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
+ "}";
+ }
+
+ char const *const *attributeNames() const
+ {
+ static char const *const names[] = { "vertex", 0 };
+ return names;
+ }
+
+ void initialize()
+ {
+ QSGMaterialShader::initialize();
+ m_id_matrix = program()->uniformLocation("matrix");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_ASSERT(program()->isLinked());
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_id_opacity, state.opacity());
+ }
+
+ private:
+ int m_id_matrix;
+ int m_id_opacity;
+ };
+ \endcode
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+
+ */
+
+
+
+/*!
+ Creates a new QSGMaterialShader.
+ */
+QSGMaterialShader::QSGMaterialShader()
+ : d_ptr(new QSGMaterialShaderPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::~QSGMaterialShader()
+{
+}
+
+/*!
+ \fn char const *const *QSGMaterialShader::attributeNames() const
+
+ Returns a zero-terminated array describing the names of the
+ attributes used in the vertex shader.
+
+ This function is called when the shader is compiled to specify
+ which attributes exist. The order of the attribute names
+ defines the attribute register position in the vertex shader.
+ */
+
+#if QT_CONFIG(opengl)
+/*!
+ \fn const char *QSGMaterialShader::vertexShader() const
+
+ Called when the shader is being initialized to get the vertex
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::vertexShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Vertex);
+}
+
+
+/*!
+ \fn const char *QSGMaterialShader::fragmentShader() const
+
+ Called when the shader is being initialized to get the fragment
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::fragmentShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Fragment);
+}
+
+
+/*!
+ \fn QOpenGLShaderProgram *QSGMaterialShader::program()
+
+ Returns the shader program used by this QSGMaterialShader.
+ */
+#endif
+
+/*!
+ \fn void QSGMaterialShader::initialize()
+
+ Reimplement this function to do one-time initialization when the
+ shader program is compiled. The OpenGL shader program is compiled
+ and linked, but not bound, when this function is called.
+ */
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry is
+ about to be rendered using this shader.
+
+ State that is global for all uses of the shader, independent of the geometry
+ that is being drawn, can be setup in this function.
+ */
+
+void QSGMaterialShader::activate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry will
+ no longer to be rendered using this shader.
+ */
+
+void QSGMaterialShader::deactivate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph before geometry is rendered
+ to make sure the shader is in the right state.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any state is dirty, the updateState implementation must
+ update accordingly for the geometry to render correctly.
+
+ The subclass specific state, such as the color of a flat color material, should
+ be extracted from \a newMaterial to update the color uniforms accordingly.
+
+ The \a oldMaterial can be used to minimze state changes when updating
+ material states. The \a oldMaterial is 0 if this shader was just activated.
+
+ \sa activate(), deactivate()
+ */
+
+void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
+{
+}
+
+#if QT_CONFIG(opengl)
+/*!
+ Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function.
+
+ This function is useful when you have a single source file for a given shader
+ stage. If your shader consists of multiple source files then use
+ setShaderSourceFiles()
+
+ \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = (QStringList() << sourceFile);
+}
+
+/*!
+ Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function in the order given.
+
+ \sa setShaderSourceFile(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = sourceFiles;
+}
+
+/*!
+ This function is called when the shader is initialized to compile the
+ actual QOpenGLShaderProgram. Do not call it explicitly.
+
+ The default implementation will extract the vertexShader() and
+ fragmentShader() and bind the names returned from attributeNames()
+ to consecutive vertex attribute registers starting at 0.
+ */
+
+void QSGMaterialShader::compile()
+{
+ Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
+
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
+
+ char const *const *attr = attributeNames();
+#ifndef QT_NO_DEBUG
+ int maxVertexAttribs = 0;
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
+ for (int i = 0; attr[i]; ++i) {
+ if (i >= maxVertexAttribs) {
+ qFatal("List of attribute names is either too long or not null-terminated.\n"
+ "Maximum number of attributes on this hardware is %i.\n"
+ "Vertex shader:\n%s\n"
+ "Fragment shader:\n%s\n",
+ maxVertexAttribs, vertexShader(), fragmentShader());
+ }
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#else
+ for (int i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#endif
+
+ if (!program()->link()) {
+ qWarning("QSGMaterialShader: Shader compilation failed:");
+ qWarning() << program()->log();
+ }
+}
+
+#endif
+
+/*!
+ \class QSGMaterialShader::RenderState
+ \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
+ during a call to QSGMaterialShader::updateState().
+ \inmodule QtQuick
+
+ The render state contains a number of accessors that the shader needs to respect
+ in order to conform to the current state of the scene graph.
+
+ The instance is only valid inside a call to QSGMaterialShader::updateState() and
+ should not be used outisde this function.
+ */
+
+
+
+/*!
+ \enum QSGMaterialShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
+
+ \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty cached material state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag
+ set, this is guaranteed to be the complete transform
+ matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may
+ choose to alter this matrix. For example, it may
+ pre-transform vertices on the CPU and set this matrix
+ to identity.
+
+ In a situation such as the above, it is still possible
+ to retrieve the actual matrix determinant by setting
+ the RequiresDeterminant flag in the material and
+ calling the determinant() accessor.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+
+QRect QSGMaterialShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+
+QRect QSGMaterialShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+#if QT_CONFIG(opengl)
+
+/*!
+ Returns the QOpenGLContext that is being used for rendering
+ */
+
+QOpenGLContext *QSGMaterialShader::RenderState::context() const
+{
+ // Only the QSGDefaultRenderContext will have an OpenGL Context to query
+ auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
+ if (openGLRenderContext != nullptr)
+ return openGLRenderContext->openglContext();
+ else
+ return nullptr;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
new file mode 100644
index 0000000000..d7ee23384f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMATERIALSHADER_H
+#define QSGMATERIALSHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglshaderprogram.h>
+#endif
+#include <QtGui/QMatrix4x4>
+#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialtype.h> // for source compat
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialShaderPrivate;
+
+namespace QSGBatchRenderer {
+ class ShaderManager;
+}
+
+class Q_QUICK_EXPORT QSGMaterialShader
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ enum DirtyState
+ {
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyCachedMaterialData = 0x0004,
+ DirtyAll = 0xFFFF
+ };
+ Q_DECLARE_FLAGS(DirtyStates, DirtyState)
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+ bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+#if QT_CONFIG(opengl)
+ QOpenGLContext *context() const;
+#endif
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ QSGMaterialShader();
+ virtual ~QSGMaterialShader();
+
+ virtual void activate();
+ virtual void deactivate();
+ // First time a material is used, oldMaterial is null.
+ virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+ virtual char const *const *attributeNames() const = 0; // Array must end with null.
+#if QT_CONFIG(opengl)
+ inline QOpenGLShaderProgram *program() { return &m_program; }
+#endif
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialShader)
+ QSGMaterialShader(QSGMaterialShaderPrivate &dd);
+
+ friend class QSGDefaultRenderContext;
+ friend class QSGBatchRenderer::ShaderManager;
+#if QT_CONFIG(opengl)
+ void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
+ void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
+
+ virtual void compile();
+#endif
+ virtual void initialize() { }
+#if QT_CONFIG(opengl)
+ virtual const char *vertexShader() const;
+ virtual const char *fragmentShader() const;
+#endif
+private:
+#if QT_CONFIG(opengl)
+ QOpenGLShaderProgram m_program;
+#endif
+ QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
index 47f5e5de09..ae23b4a8ce 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
@@ -52,7 +53,7 @@
//
#include <private/qtquickglobal_p.h>
-#include <QOpenGLShader>
+#include "qsgmaterial.h"
QT_BEGIN_NAMESPACE
@@ -67,11 +68,6 @@ public:
#endif
};
-#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
-Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
-#endif
-
QT_END_NAMESPACE
#endif // QSGMATERIALSHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialtype.h b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
new file mode 100644
index 0000000000..15141c2d9e
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMATERIALTYPE_H
+#define QSGMATERIALTYPE_H
+
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSGMaterialType { };
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 1976538aec..7d9b74bc2b 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -1466,7 +1466,7 @@ QDebug operator<<(QDebug d, const QSGGeometryNode *n)
d << "Geometry(null)";
return d;
}
- d << "GeometryNode(" << hex << (const void *) n << dec;
+ d << "GeometryNode(" << Qt::hex << (const void *) n << Qt::dec;
const QSGGeometry *g = n->geometry();
@@ -1517,7 +1517,7 @@ QDebug operator<<(QDebug d, const QSGClipNode *n)
d << "ClipNode(null)";
return d;
}
- d << "ClipNode(" << hex << (const void *) n << dec;
+ d << "ClipNode(" << Qt::hex << (const void *) n << Qt::dec;
if (n->childCount())
d << "children=" << n->childCount();
@@ -1540,7 +1540,7 @@ QDebug operator<<(QDebug d, const QSGTransformNode *n)
}
const QMatrix4x4 m = n->matrix();
d << "TransformNode(";
- d << hex << (const void *) n << dec;
+ d << Qt::hex << (const void *) n << Qt::dec;
if (m.isIdentity())
d << "identity";
else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1)
@@ -1562,7 +1562,7 @@ QDebug operator<<(QDebug d, const QSGOpacityNode *n)
return d;
}
d << "OpacityNode(";
- d << hex << (const void *) n << dec;
+ d << Qt::hex << (const void *) n << Qt::dec;
d << "opacity=" << n->opacity()
<< "combined=" << n->combinedOpacity()
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
@@ -1581,7 +1581,7 @@ QDebug operator<<(QDebug d, const QSGRootNode *n)
return d;
}
QDebugStateSaver saver(d);
- d << "RootNode" << hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
+ d << "RootNode" << Qt::hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
#endif
@@ -1614,8 +1614,8 @@ QDebug operator<<(QDebug d, const QSGNode *n)
d << static_cast<const QSGOpacityNode *>(n);
break;
case QSGNode::RenderNodeType:
- d << "RenderNode(" << hex << (const void *) n << dec
- << "flags=" << (int) n->flags() << dec
+ d << "RenderNode(" << Qt::hex << (const void *) n << Qt::dec
+ << "flags=" << (int) n->flags() << Qt::dec
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
@@ -1623,8 +1623,8 @@ QDebug operator<<(QDebug d, const QSGNode *n)
d << ')';
break;
default:
- d << "Node(" << hex << (const void *) n << dec
- << "flags=" << (int) n->flags() << dec
+ d << "Node(" << Qt::hex << (const void *) n << Qt::dec
+ << "flags=" << (int) n->flags() << Qt::dec
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index e1ba001d2d..7af932eeb5 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -132,6 +132,12 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_current_determinant(1)
, m_device_pixel_ratio(1)
, m_context(context)
+ , m_current_uniform_data(nullptr)
+ , m_current_resource_update_batch(nullptr)
+ , m_rhi(nullptr)
+ , m_rt(nullptr)
+ , m_cb(nullptr)
+ , m_rp_desc(nullptr)
, m_node_updater(nullptr)
, m_bindable(nullptr)
, m_changed_emitted(false)
@@ -184,21 +190,30 @@ bool QSGRenderer::isMirrored() const
void QSGRenderer::renderScene(uint fboId)
{
-#if QT_CONFIG(opengl)
- if (fboId) {
- QSGBindableFboId bindable(fboId);
- renderScene(bindable);
- } else {
+ if (m_rt) {
class B : public QSGBindable
{
public:
- void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ void bind() const override { }
} bindable;
renderScene(bindable);
- }
+ } else {
+#if QT_CONFIG(opengl)
+ if (fboId) {
+ QSGBindableFboId bindable(fboId);
+ renderScene(bindable);
+ } else {
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ } bindable;
+ renderScene(bindable);
+ }
#else
- Q_UNUSED(fboId)
+ Q_UNUSED(fboId)
#endif
+ }
}
void QSGRenderer::renderScene(const QSGBindable &bindable)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index d4ff6ea9fe..9c83ddf111 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -62,6 +62,10 @@ QT_BEGIN_NAMESPACE
class QSGBindable;
class QSGNodeUpdater;
+class QRhiRenderTarget;
+class QRhiCommandBuffer;
+class QRhiRenderPassDescriptor;
+class QRhiResourceUpdateBatch;
Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error();
Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
@@ -69,11 +73,10 @@ Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer
{
public:
-
QSGRenderer(QSGRenderContext *context);
virtual ~QSGRenderer();
- // Accessed by QSGMaterialShader::RenderState.
+ // Accessed by QSGMaterial[Rhi]Shader::RenderState.
QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; }
QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; }
QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; }
@@ -92,11 +95,35 @@ public:
QSGNodeUpdater *nodeUpdater() const;
void setNodeUpdater(QSGNodeUpdater *updater);
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
+ inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const;
virtual void setCustomRenderMode(const QByteArray &) { }
virtual void releaseCachedResources() { }
void clearChangedFlag() { m_changed_emitted = false; }
+ // Accessed by QSGMaterialRhiShader::RenderState.
+ QByteArray *currentUniformData() const { return m_current_uniform_data; }
+ QRhiResourceUpdateBatch *currentResourceUpdateBatch() const { return m_current_resource_update_batch; }
+ QRhi *currentRhi() const { return m_rhi; }
+
+ void setRenderTarget(QRhiRenderTarget *rt) { m_rt = rt; }
+ QRhiRenderTarget *renderTarget() const { return m_rt; }
+
+ void setCommandBuffer(QRhiCommandBuffer *cb) { m_cb = cb; }
+ QRhiCommandBuffer *commandBuffer() const { return m_cb; }
+
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc) { m_rp_desc = rpDesc; }
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_rp_desc; }
+
+ void setRenderPassRecordingCallbacks(QSGRenderContext::RenderPassCallback start,
+ QSGRenderContext::RenderPassCallback end,
+ void *userData)
+ {
+ m_renderPassRecordingCallbacks.start = start;
+ m_renderPassRecordingCallbacks.end = end;
+ m_renderPassRecordingCallbacks.userData = userData;
+ }
+
protected:
virtual void render() = 0;
@@ -107,7 +134,8 @@ protected:
void addNodesToPreprocess(QSGNode *node);
void removeNodesToPreprocess(QSGNode *node);
- QMatrix4x4 m_current_projection_matrix;
+ QMatrix4x4 m_current_projection_matrix; // includes adjustment, where applicable, so can be treated as Y up in NDC always
+ QMatrix4x4 m_current_projection_matrix_native_ndc; // Vulkan has Y down in normalized device coordinates, others Y up...
QMatrix4x4 m_current_model_view_matrix;
qreal m_current_opacity;
qreal m_current_determinant;
@@ -115,6 +143,18 @@ protected:
QSGRenderContext *m_context;
+ QByteArray *m_current_uniform_data;
+ QRhiResourceUpdateBatch *m_current_resource_update_batch;
+ QRhi *m_rhi;
+ QRhiRenderTarget *m_rt;
+ QRhiCommandBuffer *m_cb;
+ QRhiRenderPassDescriptor *m_rp_desc;
+ struct {
+ QSGRenderContext::RenderPassCallback start = nullptr;
+ QSGRenderContext::RenderPassCallback end = nullptr;
+ void *userData = nullptr;
+ } m_renderPassRecordingCallbacks;
+
private:
QSGNodeUpdater *m_node_updater;
@@ -156,6 +196,14 @@ QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState
return s;
}
+QSGMaterialRhiShader::RenderState QSGRenderer::rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const
+{
+ QSGMaterialRhiShader::RenderState s;
+ s.m_dirty = dirty;
+ s.m_data = this;
+ return s;
+}
+
class Q_QUICK_PRIVATE_EXPORT QSGNodeDumper : public QSGNodeVisitor {
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
index 0f49e615e4..e504fe1c62 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -55,13 +55,13 @@ QT_BEGIN_NAMESPACE
the Direct3D or Vulkan device) that is used by the scenegraph.
QSGRendererInterface's functions have varying availability. API and
- language queries, like graphicsApi() or shaderType() are always available,
- meaning it is sufficient to construct a QQuickWindow or QQuickView, and the
- graphics API or shading language in use can be queried right after via
- QQuickWindow::rendererInterface(). This guarantees that utilities like the
- GraphicsInfo QML type are able to report the correct values as early as
- possible, without having conditional property values - depending on for
- instance shaderType() - evaluate to unexpected values.
+ language queries, such as, graphicsApi() or shaderType() are always
+ available, meaning it is sufficient to construct a QQuickWindow or
+ QQuickView, and the graphics API or shading language in use can be queried
+ right after via QQuickWindow::rendererInterface(). This guarantees that
+ utilities like the GraphicsInfo QML type are able to report the correct
+ values as early as possible, without having conditional property values -
+ depending on for instance shaderType() - evaluate to unexpected values.
Engine-specific accessors, like getResource(), are however available only
after the scenegraph is initialized. Additionally, there may be
@@ -78,14 +78,62 @@ QT_BEGIN_NAMESPACE
\value OpenGL OpenGL ES 2.0 or higher
\value Direct3D12 Direct3D 12
\value OpenVG OpenVG via EGL
+ \value OpenGLRhi OpenGL ES 2.0 or higher via a graphics abstraction layer
+ \value Direct3D11Rhi Direct3D 11 via a graphics abstraction layer
+ \value VulkanRhi Vulkan 1.0 via a graphics abstraction layer
+ \value MetalRhi Metal via a graphics abstraction layer
+ \value NullRhi Null (no output) via a graphics abstraction layer
*/
/*!
\enum QSGRendererInterface::Resource
- \value DeviceResource The graphics device, when applicable.
- \value CommandQueueResource The graphics command queue used by the scenegraph, when applicable.
- \value CommandListResource The command list or buffer used by the scenegraph, when applicable.
- \value PainterResource The active QPainter used by the scenegraph, when running with the software backend.
+
+ \value DeviceResource The resource is a pointer to the graphics device,
+ when applicable. For example, a \c{VkDevice *}, \c{MTLDevice *} or
+ \c{ID3D11Device *}. Note that with Vulkan the returned value is a pointer
+ to the VkDevice, not the handle itself. This is because Vulkan handles may
+ not be pointers, and may use a different size from the architecture's
+ pointer size so merely casting to/from \c{void *} is wrong.
+
+ \value CommandQueueResource The resource is a pointer to the graphics
+ command queue used by the scenegraph, when applicable. For example, a
+ \c{VkQueue *} or \c{MTLCommandQueue *}. Note that with Vulkan the returned
+ value is a pointer to the VkQueue, not the handle itself.
+
+ \value CommandListResource The resource is a pointer to the command list or
+ buffer used by the scenegraph, when applicable. For example, a
+ \c{VkCommandBuffer *} or \c{MTLCommandBuffer *}. This object has limited
+ validity, and is only valid while the scene graph is preparing the next
+ frame. Note that with Vulkan the returned value is a pointer to the
+ VkCommandBuffer, not the handle itself.
+
+ \value PainterResource The resource is a pointer to the active QPainter
+ used by the scenegraph, when running with the software backend.
+
+ \value RhiResource The resource is a pointer to the QRhi instance used by
+ the scenegraph, when applicable.
+
+ \value PhysicalDeviceResource The resource is a pointer to the pysical
+ device object used by the scenegraph, when applicable. For example, a
+ \c{VkPhysicalDevice *}. Note that with Vulkan the returned value is a
+ pointer to the VkPhysicalDevice, not the handle itself.
+
+ \value OpenGLContextResource The resource is a pointer to the
+ QOpenGLContext used by the scenegraph (on the render thread), when
+ applicable.
+
+ \value DeviceContextResource The resource is a pointer to the device
+ context used by the scenegraph, when applicable. For example, a
+ \c{ID3D11DeviceContext *}.
+
+ \value CommandEncoderResource The resource is a pointer to the currently
+ active render command encoder object used by the scenegraph, when
+ applicable. For example, a \c{MTLRenderCommandEncoder *}. This object has
+ limited validity, and is only valid while the scene graph is recording a
+ render pass for the next frame.
+
+ \value VulkanInstanceResource The resource is a pointer to the
+ QVulkanInstance used by the scenegraph, when applicable.
*/
/*!
@@ -93,6 +141,8 @@ QT_BEGIN_NAMESPACE
\value UnknownShadingLanguage Not yet known due to no window and scenegraph associated
\value GLSL GLSL or GLSL ES
\value HLSL HLSL
+ \value RhiShader Consumes QShader instances containing shader
+ variants for multiple target languages and bytecode formats
*/
/*!
@@ -164,6 +214,30 @@ void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resour
}
/*!
+ \return true if \a api is based on a graphics abstraction layer (QRhi)
+ instead of directly calling the native graphics API.
+
+ \note This function can be called on any thread.
+ */
+bool QSGRendererInterface::isApiRhiBased(GraphicsApi api)
+{
+ switch (api) {
+ case OpenGLRhi:
+ Q_FALLTHROUGH();
+ case Direct3D11Rhi:
+ Q_FALLTHROUGH();
+ case VulkanRhi:
+ Q_FALLTHROUGH();
+ case MetalRhi:
+ Q_FALLTHROUGH();
+ case NullRhi:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
\fn QSGRendererInterface::ShaderType QSGRendererInterface::shaderType() const
\return the shading language supported by the Qt Quick backend the
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
index 722488201b..3052c81f6c 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -54,20 +54,32 @@ public:
Software,
OpenGL,
Direct3D12,
- OpenVG
+ OpenVG,
+ OpenGLRhi,
+ Direct3D11Rhi,
+ VulkanRhi,
+ MetalRhi,
+ NullRhi,
};
enum Resource {
DeviceResource,
CommandQueueResource,
CommandListResource,
- PainterResource
+ PainterResource,
+ RhiResource,
+ PhysicalDeviceResource,
+ OpenGLContextResource,
+ DeviceContextResource,
+ CommandEncoderResource,
+ VulkanInstanceResource
};
enum ShaderType {
UnknownShadingLanguage,
GLSL,
- HLSL
+ HLSL,
+ RhiShader
};
enum ShaderCompilationType {
@@ -93,6 +105,8 @@ public:
virtual ShaderType shaderType() const = 0;
virtual ShaderCompilationTypes shaderCompilationType() const = 0;
virtual ShaderSourceTypes shaderSourceType() const = 0;
+
+ static bool isApiRhiBased(GraphicsApi api);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderCompilationTypes)
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
index 042eee19f5..cfd0cb9f06 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -38,21 +38,13 @@
****************************************************************************/
#include "qsgtexture_p.h"
-#include <QtQuick/private/qsgcontext_p.h>
-#include <qthread.h>
-#include <qmath.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmlglobal_p.h>
-#include <QtGui/qguiapplication.h>
-#include <QtGui/qpa/qplatformnativeinterface.h>
#if QT_CONFIG(opengl)
-# include <qopenglfunctions.h>
# include <QtGui/qopenglcontext.h>
# include <QtGui/qopenglfunctions.h>
-# include <QtGui/private/qopengltextureuploader_p.h>
-# include <private/qsgdefaultrendercontext_p.h>
#endif
+#include <private/qqmlglobal_p.h>
#include <private/qsgmaterialshader_p.h>
+#include <QtGui/private/qrhi_p.h>
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
#define CAN_BACKTRACE_EXECINFO
@@ -71,32 +63,55 @@
#include <QHash>
#endif
-#if QT_CONFIG(opengl)
-static QElapsedTimer qsg_renderer_timer;
-#endif
-
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
#endif
+QT_BEGIN_NAMESPACE
-#ifndef GL_BGRA
-#define GL_BGRA 0x80E1
-#endif
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return a.filtering == b.filtering
+ && a.mipmapFiltering == b.mipmapFiltering
+ && a.horizontalWrap == b.horizontalWrap
+ && a.verticalWrap == b.verticalWrap
+ && a.anisotropylevel == b.anisotropylevel;
+}
-#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
-#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
-#endif
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
-QT_BEGIN_NAMESPACE
+uint qHash(const QSGSamplerDescription &s, uint seed) Q_DECL_NOTHROW
+{
+ const int f = s.filtering;
+ const int m = s.mipmapFiltering;
+ const int w = s.horizontalWrap;
+ const int a = s.anisotropylevel;
+ return (((f & 7) << 24) | ((m & 7) << 16) | ((w & 7) << 8) | (a & 7)) ^ seed;
+}
+
+QSGSamplerDescription QSGSamplerDescription::fromTexture(QSGTexture *t)
+{
+ QSGSamplerDescription s;
+ s.filtering = t->filtering();
+ s.mipmapFiltering = t->mipmapFiltering();
+ s.horizontalWrap = t->horizontalWrapMode();
+ s.verticalWrap = t->verticalWrapMode();
+ s.anisotropylevel = t->anisotropyLevel();
+ return s;
+}
-#if QT_CONFIG(opengl) && !defined(QT_NO_DEBUG)
+#if QT_CONFIG(opengl)
+#ifndef QT_NO_DEBUG
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
#endif
+#endif
QSGTexturePrivate::QSGTexturePrivate()
: wrapChanged(false)
@@ -302,10 +317,9 @@ static void qt_debug_remove_texture(QSGTexture* texture)
\since 5.9
*/
-/*!
- \fn QSGTexture::QSGTexture(QSGTexturePrivate &dd)
- \internal
- */
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
+#endif
#ifndef QT_NO_DEBUG
Q_GLOBAL_STATIC(QSet<QSGTexture *>, qsg_valid_texture_set)
@@ -344,6 +358,21 @@ QSGTexture::QSGTexture()
}
/*!
+ \internal
+ */
+QSGTexture::QSGTexture(QSGTexturePrivate &dd)
+ : QObject(dd)
+{
+#ifndef QT_NO_DEBUG
+ if (qsg_leak_check)
+ qt_debug_add_texture(this);
+
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ qsg_valid_texture_set()->insert(this);
+#endif
+}
+
+/*!
Destroys the QSGTexture.
*/
QSGTexture::~QSGTexture()
@@ -357,7 +386,6 @@ QSGTexture::~QSGTexture()
#endif
}
-
/*!
\fn void QSGTexture::bind()
@@ -389,7 +417,7 @@ QSGTexture::~QSGTexture()
it to a shader that operates on the texture coordinates 0-1 instead
of the texture subrect inside the atlas.
- If the texture is not part of a texture atlas, this function returns \nullptr.
+ If the texture is not part of a texture atlas, this function returns 0.
Implementations of this function are recommended to return the same instance
for multiple calls to limit memory usage.
@@ -426,6 +454,34 @@ bool QSGTexture::isAtlasTexture() const
*/
/*!
+ Returns a key suitable for comparing textures. Typically used in
+ QSGMaterial::compare() implementations.
+
+ Just comparing QSGTexture pointers is not always sufficient because two
+ QSGTexture instances that refer to the same native texture object
+ underneath should also be considered equal. Hence this function.
+
+ \note Unlike textureId(), implementations of this function are not expected
+ to and should not create any graphics resources (so texture objects) in
+ case there is none yet.
+
+ A QSGTexture that does not have a native texture object underneath is
+ typically not equal to any other QSGTexture. There are exceptions to this,
+ in particular when atlasing is used (where multiple textures share the same
+ atlas texture under the hood), that is then up to the subclass
+ implementations to deal with as appropriate.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+int QSGTexture::comparisonKey() const
+{
+ Q_D(const QSGTexture);
+ return d->comparisonKey();
+}
+
+/*!
\fn QSize QSGTexture::textureSize() const
Returns the size of the texture.
@@ -579,7 +635,7 @@ QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
If \a force is true, all properties will be updated regardless of weither
they have changed or not.
*/
-void QSGTexture::updateBindOptions(bool force)
+void QSGTexture::updateBindOptions(bool force) // legacy (GL-only)
{
#if QT_CONFIG(opengl)
Q_D(QSGTexture);
@@ -639,185 +695,83 @@ void QSGTexture::updateBindOptions(bool force)
#endif
}
-QSGPlainTexture::QSGPlainTexture()
- : QSGTexture()
- , m_texture_id(0)
- , m_has_alpha(false)
- , m_dirty_texture(false)
- , m_dirty_bind_options(false)
- , m_owns_texture(true)
- , m_mipmaps_generated(false)
- , m_retain_image(false)
+/*!
+ \return the QRhiTexture for this QSGTexture or null if there is none.
+
+ Unlike textureId(), this function is not expected to create a new
+ QRhiTexture in case there is none. Just return null in that case. The
+ expectation towards the renderer is that a null texture leads to using a
+ transparent, dummy texture instead.
+
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+QRhiTexture *QSGTexture::rhiTexture() const
{
+ Q_D(const QSGTexture);
+ return d->rhiTexture();
}
+/*!
+ Call this function to enqueue image upload operations to \a
+ resourceUpdates, in case there are any pending ones. When there is no new
+ data (for example, because there was no setImage() since the last call to
+ this function), the function does nothing.
+
+ Materials involving textures are expected to call this function from their
+ updateSampledImage() implementation, typically without any conditions.
+
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
-QSGPlainTexture::~QSGPlainTexture()
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+void QSGTexture::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
{
-#if QT_CONFIG(opengl)
- if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext())
- QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
-#endif
+ Q_D(QSGTexture);
+ d->updateRhiTexture(rhi, resourceUpdates);
}
-void QSGPlainTexture::setImage(const QImage &image)
+void QSGTexture::setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates)
{
- m_image = image;
- m_texture_size = image.size();
- m_has_alpha = image.hasAlphaChannel();
- m_dirty_texture = true;
- m_dirty_bind_options = true;
- m_mipmaps_generated = false;
- }
+ Q_D(QSGTexture);
+ d->workResourceUpdateBatch = resourceUpdates;
+}
-int QSGPlainTexture::textureId() const
+bool QSGTexturePrivate::hasDirtySamplerOptions() const
{
- if (m_dirty_texture) {
- if (m_image.isNull()) {
- // The actual texture and id will be updated/deleted in a later bind()
- // or ~QSGPlainTexture so just keep it minimal here.
- return 0;
- } else if (m_texture_id == 0){
-#if QT_CONFIG(opengl)
- // Generate a texture id for use later and return it.
- QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
-#endif
- return m_texture_id;
- }
- }
- return m_texture_id;
+ return wrapChanged || filteringChanged || anisotropyChanged;
}
-void QSGPlainTexture::setTextureId(int id)
+void QSGTexturePrivate::resetDirtySamplerOptions()
{
-#if QT_CONFIG(opengl)
- if (m_texture_id && m_owns_texture)
- QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
-#endif
-
- m_texture_id = id;
- m_dirty_texture = false;
- m_dirty_bind_options = true;
- m_image = QImage();
- m_mipmaps_generated = false;
+ wrapChanged = filteringChanged = anisotropyChanged = false;
}
-void QSGPlainTexture::bind()
+int QSGTexturePrivate::comparisonKey() const
{
-#if QT_CONFIG(opengl)
- QOpenGLContext *context = QOpenGLContext::currentContext();
- QOpenGLFunctions *funcs = context->functions();
- if (!m_dirty_texture) {
- funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
- if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) {
- funcs->glGenerateMipmap(GL_TEXTURE_2D);
- m_mipmaps_generated = true;
- }
- updateBindOptions(m_dirty_bind_options);
- m_dirty_bind_options = false;
- return;
- }
-
- m_dirty_texture = false;
-
- bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
- if (profileFrames)
- qsg_renderer_timer.start();
- Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTextureDeletion);
-
-
- if (m_image.isNull()) {
- if (m_texture_id && m_owns_texture) {
- funcs->glDeleteTextures(1, &m_texture_id);
- qCDebug(QSG_LOG_TIME_TEXTURE, "plain texture deleted in %dms - %dx%d",
- (int) qsg_renderer_timer.elapsed(),
- m_texture_size.width(),
- m_texture_size.height());
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTextureDeletion,
- QQuickProfiler::SceneGraphTextureDeletionDelete);
- }
- m_texture_id = 0;
- m_texture_size = QSize();
- m_has_alpha = false;
-
- return;
- }
-
- if (m_texture_id == 0)
- funcs->glGenTextures(1, &m_texture_id);
- funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
-
- qint64 bindTime = 0;
- if (profileFrames)
- bindTime = qsg_renderer_timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareBind);
-
- // ### TODO: check for out-of-memory situations...
-
- QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
-
- // Downscale the texture to fit inside the max texture limit if it is too big.
- // It would be better if the image was already downscaled to the right size,
- // but this information is not always available at that time, so as a last
- // resort we can do it here. Texture coordinates are normalized, so it
- // won't cause any problems and actual texture sizes will be written
- // based on QSGTexture::textureSize which is updated after this, so that
- // should be ok.
- int max;
- if (auto rc = QSGDefaultRenderContext::from(context))
- max = rc->maxTextureSize();
- else
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
-
- m_texture_size = m_texture_size.boundedTo(QSize(max, max));
-
- // Scale to a power of two size if mipmapping is requested and the
- // texture is npot and npot textures are not properly supported.
- if (mipmapFiltering() != QSGTexture::None
- && !funcs->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
- options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
- }
-
- updateBindOptions(m_dirty_bind_options);
-
- QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, m_image, options, QSize(max, max));
-
- qint64 uploadTime = 0;
- if (profileFrames)
- uploadTime = qsg_renderer_timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareUpload);
-
- if (mipmapFiltering() != QSGTexture::None) {
- funcs->glGenerateMipmap(GL_TEXTURE_2D);
- m_mipmaps_generated = true;
- }
-
- qint64 mipmapTime = 0;
- if (profileFrames) {
- mipmapTime = qsg_renderer_timer.nsecsElapsed();
- qCDebug(QSG_LOG_TIME_TEXTURE,
- "plain texture uploaded in: %dms (%dx%d), bind=%d, upload=%d, mipmap=%d%s",
- int(mipmapTime / 1000000),
- m_texture_size.width(), m_texture_size.height(),
- int(bindTime / 1000000),
- int((uploadTime - bindTime)/1000000),
- int((mipmapTime - uploadTime)/1000000),
- m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : "");
- }
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareMipmap);
-
- m_texture_rect = QRectF(0, 0, 1, 1);
+ // Must be overridden in subclasses but we cannot make this pure virtual
+ // before Qt 6 because the simple QSGTexture ctor must be kept working.
+ Q_Q(const QSGTexture);
+ return q->textureId(); // this is semantically wrong but at least compatible with existing, non-RHI-aware subclasses
+}
- m_dirty_bind_options = false;
- if (!m_retain_image)
- m_image = QImage();
-#endif
+QRhiTexture *QSGTexturePrivate::rhiTexture() const
+{
+ return nullptr;
}
+void QSGTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(resourceUpdates);
+}
/*!
\class QSGDynamicTexture
@@ -843,9 +797,14 @@ void QSGPlainTexture::bind()
returns false.
*/
-
+/*!
+ \internal
+ */
+QSGDynamicTexture::QSGDynamicTexture(QSGTexturePrivate &dd)
+ : QSGTexture(dd)
+{
+}
QT_END_NAMESPACE
#include "moc_qsgtexture.cpp"
-#include "moc_qsgtexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/coreapi/qsgtexture.h
index 7bd57a16e3..f2b0e902f3 100644
--- a/src/quick/scenegraph/util/qsgtexture.h
+++ b/src/quick/scenegraph/coreapi/qsgtexture.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -47,6 +47,10 @@
QT_BEGIN_NAMESPACE
class QSGTexturePrivate;
+class QRhi;
+class QRhiTexture;
+class QRhiResourceUpdateBatch;
+
class Q_QUICK_EXPORT QSGTexture : public QObject
{
Q_OBJECT
@@ -76,7 +80,7 @@ public:
Anisotropy16x
};
- virtual int textureId() const = 0;
+ virtual int textureId() const = 0; // ### Qt 6: remove
virtual QSize textureSize() const = 0;
virtual bool hasAlphaChannel() const = 0;
virtual bool hasMipmaps() const = 0;
@@ -107,6 +111,14 @@ public:
inline QRectF convertToNormalizedSourceRect(const QRectF &rect) const;
+ // ### Qt 6: make these virtual
+ int comparisonKey() const;
+ QRhiTexture *rhiTexture() const;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ // ### Qt 6: make this an argument for removedFromAtlas()
+ void setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates);
+
protected:
QSGTexture(QSGTexturePrivate &dd);
};
@@ -125,12 +137,15 @@ QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
rect.height() * sy);
}
-
class Q_QUICK_EXPORT QSGDynamicTexture : public QSGTexture
{
Q_OBJECT
+
public:
virtual bool updateTexture() = 0;
+
+protected:
+ QSGDynamicTexture(QSGTexturePrivate &dd);
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_p.h b/src/quick/scenegraph/coreapi/qsgtexture_p.h
new file mode 100644
index 0000000000..1d248b0305
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgtexture_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTURE_P_H
+#define QSGTEXTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <private/qobject_p.h>
+#include "qsgtexture.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QSGSamplerDescription
+{
+ QSGTexture::Filtering filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering mipmapFiltering = QSGTexture::None;
+ QSGTexture::WrapMode horizontalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode verticalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::AnisotropyLevel anisotropylevel = QSGTexture::AnisotropyNone;
+
+ static QSGSamplerDescription fromTexture(QSGTexture *t);
+};
+
+Q_DECLARE_TYPEINFO(QSGSamplerDescription, Q_MOVABLE_TYPE);
+
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+uint qHash(const QSGSamplerDescription &s, uint seed = 0) Q_DECL_NOTHROW;
+
+class Q_QUICK_PRIVATE_EXPORT QSGTexturePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTexture)
+public:
+ QSGTexturePrivate();
+ static QSGTexturePrivate *get(QSGTexture *t) { return t->d_func(); }
+ void resetDirtySamplerOptions();
+ bool hasDirtySamplerOptions() const;
+
+ // ### Qt 6: these should be virtuals in the public class instead
+ virtual int comparisonKey() const; // ### Qt 6: pure virtual
+ virtual QRhiTexture *rhiTexture() const;
+ virtual void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ QRhiResourceUpdateBatch *workResourceUpdateBatch = nullptr; // ### Qt 6: remove
+
+ uint wrapChanged : 1;
+ uint filteringChanged : 1;
+ uint anisotropyChanged : 1;
+
+ uint horizontalWrap : 2;
+ uint verticalWrap : 2;
+ uint mipmapMode : 2;
+ uint filterMode : 2;
+ uint anisotropyLevel: 3;
+};
+
+Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTURE_P_H
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 252e5a9c55..f7b07d724a 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -306,6 +306,19 @@ void QSGDistanceFieldGlyphCache::updateTexture(uint oldTex, uint newTex, const Q
}
}
+void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
+{
+ int count = m_textures.count();
+ for (int i = 0; i < count; ++i) {
+ Texture &tex = m_textures[i];
+ if (tex.texture == oldTex) {
+ tex.texture = newTex;
+ tex.size = newTexSize;
+ return;
+ }
+ }
+}
+
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
#include <QtGui/qopenglfunctions.h>
@@ -526,14 +539,6 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
}
#ifndef QT_NO_DEBUG_STREAM
-QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p)
-{
- QDebugStateSaver saver(debug);
- debug.space();
- debug << p.semanticName << "semindex" << p.semanticIndex;
- return debug;
-}
-
QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
{
QDebugStateSaver saver(debug);
@@ -564,6 +569,14 @@ QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
}
#endif
+/*!
+ \internal
+ */
+QSGLayer::QSGLayer(QSGTexturePrivate &dd)
+ : QSGDynamicTexture(dd)
+{
+}
+
QT_END_NAMESPACE
#include "moc_qsgadaptationlayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 58ecae94e7..6baee33b53 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -64,6 +64,7 @@
#include <QtGui/private/qdatabuffer_p.h>
#include <private/qdistancefield_p.h>
#include <private/qintrusivelist_p.h>
+#include <QtGui/private/qshader_p.h>
// ### remove
#include <QtQuick/private/qquicktext_p.h>
@@ -82,6 +83,7 @@ class QSGRootNode;
class QSGSpriteNode;
class QSGRenderNode;
class QSGRenderContext;
+class QRhiTexture;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -216,6 +218,9 @@ public:
Q_SIGNALS:
void updateRequested();
void scheduledUpdateCompleted();
+
+protected:
+ QSGLayer(QSGTexturePrivate &dd);
};
#if QT_CONFIG(quick_sprite)
@@ -266,27 +271,26 @@ public:
Sampler,
Texture // for APIs with separate texture and sampler objects
};
- struct InputParameter {
- InputParameter() {}
- // Semantics use the D3D keys (POSITION, TEXCOORD).
- // Attribute name based APIs can map based on pre-defined names.
- QByteArray semanticName;
- int semanticIndex = 0;
- };
struct Variable {
Variable() {}
VariableType type = Constant;
QByteArray name;
uint offset = 0; // for cbuffer members
uint size = 0; // for cbuffer members
- int bindPoint = 0; // for textures and samplers; for register-based APIs
+ int bindPoint = 0; // for textures/samplers, where applicable
};
- QByteArray blob; // source or bytecode
+ QString name; // optional, f.ex. the filename, used for debugging purposes only
+ QByteArray blob; // source or bytecode (when not using QRhi)
+ QShader rhiShader;
Type type;
- QVector<InputParameter> inputParameters;
QVector<Variable> variables;
uint constantDataSize;
+
+ // Vertex inputs are not tracked here as QSGGeometry::AttributeSet
+ // hardwires that anyways so it is up to the shader to provide
+ // compatible inputs (e.g. compatible with
+ // QSGGeometry::defaultAttributes_TexturedPoint2D()).
};
virtual void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) = 0;
@@ -298,7 +302,6 @@ Q_SIGNALS:
};
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p);
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v);
#endif
@@ -439,10 +442,18 @@ public:
struct Texture {
uint textureId = 0;
+ QRhiTexture *texture = nullptr;
QSize size;
-
- Texture() : size(QSize()) { }
- bool operator == (const Texture &other) const { return textureId == other.textureId; }
+ bool rhiBased = false;
+
+ bool operator == (const Texture &other) const {
+ if (rhiBased != other.rhiBased)
+ return false;
+ if (rhiBased)
+ return texture == other.texture;
+ else
+ return textureId == other.textureId;
+ }
};
const QRawFont &referenceFont() const { return m_referenceFont; }
@@ -474,6 +485,8 @@ public:
virtual void unregisterOwnerElement(QQuickItem *ownerElement);
virtual void processPendingGlyphs();
+ virtual bool eightBitFormatIsAlphaSwizzled() const = 0;
+
protected:
struct GlyphPosition {
glyph_t glyph;
@@ -501,6 +514,7 @@ protected:
inline void removeGlyph(glyph_t glyph);
void updateTexture(uint oldTex, uint newTex, const QSize &newTexSize);
+ void updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize);
inline bool containsGlyph(glyph_t glyph);
uint textureIdForGlyph(glyph_t glyph) const;
diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
index c434563c90..d8efda1ecc 100644
--- a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
@@ -258,7 +258,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect,
xs[1].tx = innerSourceRect.left();
xs += 2;
}
- if (innerTargetRect.width() != 0) {
+ if (innerTargetRect.width() != 0 && hTiles > 0) {
xs[0].x = innerTargetRect.left();
xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width();
++xs;
@@ -299,7 +299,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect,
ys[1].ty = innerSourceRect.top();
ys += 2;
}
- if (innerTargetRect.height() != 0) {
+ if (innerTargetRect.height() != 0 && vTiles > 0) {
ys[0].y = innerTargetRect.top();
ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height();
++ys;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 53648e352a..97fd49e4c7 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -332,15 +332,56 @@ QSGRenderContext::~QSGRenderContext()
{
}
-void QSGRenderContext::initialize(void *context)
+void QSGRenderContext::initialize(const InitParams *params)
{
- Q_UNUSED(context);
+ Q_UNUSED(params);
}
void QSGRenderContext::invalidate()
{
}
+void QSGRenderContext::beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_UNUSED(renderer);
+ Q_UNUSED(mainPassRecordingStart);
+ Q_UNUSED(mainPassRecordingEnd);
+ Q_UNUSED(callbackUserData);
+}
+
+void QSGRenderContext::endNextFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGRenderContext::beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_UNUSED(renderer);
+ Q_UNUSED(rt);
+ Q_UNUSED(rp);
+ Q_UNUSED(cb);
+ Q_UNUSED(mainPassRecordingStart);
+ Q_UNUSED(mainPassRecordingEnd);
+ Q_UNUSED(callbackUserData);
+}
+
+void QSGRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGRenderContext::endNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
void QSGRenderContext::endSync()
{
qDeleteAll(m_texturesToDelete);
@@ -362,6 +403,11 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
m_fontEnginesToClean << engine;
}
+QRhi *QSGRenderContext::rhi() const
+{
+ return nullptr;
+}
+
/*!
Factory function for the scene graph renderers.
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 282ce828af..4e712ab7c3 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -89,6 +89,10 @@ class QSGImageNode;
class QSGNinePatchNode;
class QSGSpriteNode;
class QSGRenderContext;
+class QRhi;
+class QRhiRenderTarget;
+class QRhiRenderPassDescriptor;
+class QRhiCommandBuffer;
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION)
@@ -119,7 +123,7 @@ public:
QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c);
virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0;
- virtual QSGInternalImageNode *createInternalImageNode() = 0;
+ virtual QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) = 0;
virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0;
virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0;
virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0;
@@ -164,9 +168,27 @@ public:
QSGContext *sceneGraphContext() const { return m_sg; }
virtual bool isValid() const { return true; }
- virtual void initialize(void *context);
+ struct InitParams { };
+ virtual void initialize(const InitParams *params);
virtual void invalidate();
+
+ using RenderPassCallback = void (*)(void *);
+
+ virtual void beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData);
virtual void renderNextFrame(QSGRenderer *renderer, uint fboId) = 0;
+ virtual void endNextFrame(QSGRenderer *renderer);
+
+ virtual void beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData);
+ virtual void renderNextRhiFrame(QSGRenderer *renderer);
+ virtual void endNextRhiFrame(QSGRenderer *renderer);
+
virtual void endSync();
virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
@@ -182,6 +204,8 @@ public:
void registerFontengineForCleanup(QFontEngine *engine);
+ virtual QRhi *rhi() const;
+
Q_SIGNALS:
void initialized();
void invalidated();
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
index a5b2b04c93..ea01b0a3b4 100644
--- a/src/quick/scenegraph/qsgdefaultcontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -45,8 +45,9 @@
#include <QtQuick/private/qsgdefaultglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
-#include <QtQuick/private/qsgrenderloop_p.h>
-#include <QtQuick/private/qsgdefaultlayer_p.h>
+#include <QtQuick/private/qsgopengllayer_p.h>
+#include <QtQuick/private/qsgrhisupport_p.h>
+#include <QtQuick/private/qsgrhilayer_p.h>
#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
#include <QtQuick/private/qsgdefaultimagenode_p.h>
@@ -54,25 +55,29 @@
#if QT_CONFIG(quick_sprite)
#include <QtQuick/private/qsgdefaultspritenode_p.h>
#endif
+#include <QtQuick/private/qsgrhishadereffectnode_p.h>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFramebufferObject>
-#include <QtQuick/QQuickWindow>
+#include <QtQuick/private/qquickwindow_p.h>
#include <private/qqmlglobal_p.h>
#include <algorithm>
+#include <QtGui/private/qrhi_p.h>
+#include <QtGui/private/qrhigles2_p.h>
+
QT_BEGIN_NAMESPACE
namespace QSGMultisampleAntialiasing {
class ImageNode : public QSGDefaultInternalImageNode {
public:
+ ImageNode(QSGDefaultRenderContext *rc) : QSGDefaultInternalImageNode(rc) { }
void setAntialiasing(bool) override { }
};
-
class RectangleNode : public QSGDefaultInternalRectangleNode {
public:
void setAntialiasing(bool) override { }
@@ -118,7 +123,7 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
{
m_mutex.lock();
- auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(renderContext);
+ auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
if (m_antialiasingMethod == UndecidedAntialiasing) {
if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) {
const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
@@ -127,12 +132,8 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
else if (aaType == "vertex")
m_antialiasingMethod = VertexAntialiasing;
}
- if (m_antialiasingMethod == UndecidedAntialiasing) {
- if (openglRenderContext->openglContext()->format().samples() > 0)
- m_antialiasingMethod = MsaaAntialiasing;
- else
- m_antialiasingMethod = VertexAntialiasing;
- }
+ if (m_antialiasingMethod == UndecidedAntialiasing)
+ m_antialiasingMethod = rc->msaaSampleCount() > 1 ? MsaaAntialiasing : VertexAntialiasing;
}
// With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless
@@ -141,15 +142,23 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
if (!m_distanceFieldAntialiasingDecided) {
m_distanceFieldAntialiasingDecided = true;
#ifndef Q_OS_WIN32
- if (openglRenderContext->openglContext()->isOpenGLES())
- m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ if (rc->rhi()) {
+ if (rc->rhi()->backend() == QRhi::OpenGLES2
+ && static_cast<const QRhiGles2NativeHandles *>(rc->rhi()->nativeHandles())->context->isOpenGLES())
+ {
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ }
+ } else {
+ if (rc->openglContext()->isOpenGLES())
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ }
#endif
}
static bool dumped = false;
- if (!dumped && QSG_LOG_INFO().isDebugEnabled()) {
+ if (!dumped && QSG_LOG_INFO().isDebugEnabled() && !rc->rhi()) {
dumped = true;
- QSurfaceFormat format = openglRenderContext->openglContext()->format();
+ QSurfaceFormat format = rc->openglContext()->format();
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
qCDebug(QSG_LOG_INFO, "R/G/B/A Buffers: %d %d %d %d", format.redBufferSize(),
format.greenBufferSize(), format.blueBufferSize(), format.alphaBufferSize());
@@ -160,10 +169,10 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
qCDebug(QSG_LOG_INFO, "GL_RENDERER: %s",
(const char*)funcs->glGetString(GL_RENDERER));
qCDebug(QSG_LOG_INFO, "GL_VERSION: %s", (const char*)funcs->glGetString(GL_VERSION));
- QByteArrayList exts = openglRenderContext->openglContext()->extensions().values();
+ QByteArrayList exts = rc->openglContext()->extensions().values();
std::sort(exts.begin(), exts.end());
qCDebug(QSG_LOG_INFO, "GL_EXTENSIONS: %s", exts.join(' ').constData());
- qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", openglRenderContext->maxTextureSize());
+ qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", rc->maxTextureSize());
qCDebug(QSG_LOG_INFO, "Debug context: %s",
format.testOption(QSurfaceFormat::DebugContext) ? "true" : "false");
}
@@ -187,11 +196,11 @@ QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode()
: new QSGDefaultInternalRectangleNode;
}
-QSGInternalImageNode *QSGDefaultContext::createInternalImageNode()
+QSGInternalImageNode *QSGDefaultContext::createInternalImageNode(QSGRenderContext *renderContext)
{
return m_antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::ImageNode
- : new QSGDefaultInternalImageNode;
+ ? new QSGMultisampleAntialiasing::ImageNode(static_cast<QSGDefaultRenderContext *>(renderContext))
+ : new QSGDefaultInternalImageNode(static_cast<QSGDefaultRenderContext *>(renderContext));
}
QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
@@ -202,7 +211,7 @@ QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
{
if (m_distanceFieldDisabled || preferNativeGlyphNode) {
- return new QSGDefaultGlyphNode;
+ return new QSGDefaultGlyphNode(rc);
} else {
QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
node->setPreferredAntialiasingMode(m_distanceFieldAntialiasing);
@@ -212,7 +221,11 @@ QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool pref
QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext)
{
- return new QSGDefaultLayer(renderContext);
+ auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
+ if (rc->rhi())
+ return new QSGRhiLayer(renderContext);
+ else
+ return new QSGOpenGLLayer(renderContext);
}
QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const
@@ -275,24 +288,73 @@ QSGSpriteNode *QSGDefaultContext::createSpriteNode()
}
#endif
+QSGGuiThreadShaderEffectManager *QSGDefaultContext::createGuiThreadShaderEffectManager()
+{
+ if (QSGRhiSupport::instance()->isRhiEnabled())
+ return new QSGRhiGuiThreadShaderEffectManager;
+
+ return nullptr;
+}
+
+QSGShaderEffectNode *QSGDefaultContext::createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr)
+{
+ if (QSGRhiSupport::instance()->isRhiEnabled()) {
+ return new QSGRhiShaderEffectNode(static_cast<QSGDefaultRenderContext *>(renderContext),
+ static_cast<QSGRhiGuiThreadShaderEffectManager *>(mgr));
+ }
+
+ return nullptr;
+}
+
QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const
{
- return OpenGL;
+ return QSGRhiSupport::instance()->graphicsApi();
+}
+
+void *QSGDefaultContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (!window)
+ return nullptr;
+
+ // Unlike the graphicsApi and shaderType and similar queries, getting a
+ // native resource is only possible when there is an initialized
+ // rendercontext, or rather, only within rendering a frame, as per
+ // QSGRendererInterface docs. This is good since getting some things is
+ // only possible within a beginFrame - endFrame with the RHI.
+
+ const QSGDefaultRenderContext *rc = static_cast<const QSGDefaultRenderContext *>(
+ QQuickWindowPrivate::get(window)->context);
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+
+ switch (resource) {
+ case OpenGLContextResource:
+ if (rhiSupport->graphicsApi() == OpenGL)
+ return rc->openglContext();
+ else
+ return const_cast<void *>(rhiSupport->rifResource(resource, rc));
+#if QT_CONFIG(vulkan)
+ case VulkanInstanceResource:
+ return window->vulkanInstance();
+#endif
+ default:
+ return const_cast<void *>(rhiSupport->rifResource(resource, rc));
+ }
}
QSGRendererInterface::ShaderType QSGDefaultContext::shaderType() const
{
- return GLSL;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? RhiShader : GLSL;
}
QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilationType() const
{
- return RuntimeCompilation;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? OfflineCompilation : RuntimeCompilation;
}
QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const
{
- return ShaderSourceString | ShaderSourceFile;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? ShaderSourceFile : (ShaderSourceString | ShaderSourceFile);
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h
index 6dfd197cf6..8fdd29caee 100644
--- a/src/quick/scenegraph/qsgdefaultcontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -67,7 +67,7 @@ public:
void renderContextInvalidated(QSGRenderContext *) override;
QSGRenderContext *createRenderContext() override;
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
@@ -79,11 +79,15 @@ public:
#if QT_CONFIG(quick_sprite)
QSGSpriteNode *createSpriteNode() override;
#endif
+ QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override;
+ QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr) override;
void setDistanceFieldEnabled(bool enabled);
bool isDistanceFieldEnabled() const;
GraphicsApi graphicsApi() const override;
+ void *getResource(QQuickWindow *window, Resource resource) const override;
ShaderType shaderType() const override;
ShaderCompilationTypes shaderCompilationType() const override;
ShaderSourceTypes shaderSourceType() const override;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index 0d42102f36..ba286b8a36 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -42,8 +42,9 @@
QT_BEGIN_NAMESPACE
-QSGDefaultGlyphNode::QSGDefaultGlyphNode()
- : m_glyphNodeType(RootGlyphNode)
+QSGDefaultGlyphNode::QSGDefaultGlyphNode(QSGRenderContext *context)
+ : m_context(context)
+ , m_glyphNodeType(RootGlyphNode)
, m_dirtyGeometry(false)
{
setFlag(UsePreprocess);
@@ -75,14 +76,14 @@ void QSGDefaultGlyphNode::update()
QMargins margins(0, 0, 0, 0);
if (m_style == QQuickText::Normal) {
- m_material = new QSGTextMaskMaterial(font);
+ m_material = new QSGTextMaskMaterial(m_context, font);
} else if (m_style == QQuickText::Outline) {
- QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(font);
+ QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
material->setStyleColor(m_styleColor);
m_material = material;
margins = QMargins(1, 1, 1, 1);
} else {
- QSGStyledTextMaterial *material = new QSGStyledTextMaterial(font);
+ QSGStyledTextMaterial *material = new QSGStyledTextMaterial(m_context, font);
if (m_style == QQuickText::Sunken) {
material->setStyleShift(QVector2D(0, -1));
margins.setTop(1);
@@ -158,7 +159,7 @@ void QSGDefaultGlyphNode::updateGeometry()
subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
subNodeGlyphRun.setPositions(glyphInfo.positions);
- QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode();
+ QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(m_context);
subNode->setGlyphNodeType(SubGlyphNode);
subNode->setColor(m_color);
subNode->setStyle(m_style);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index ce706d76f7..8ce469b39b 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -71,6 +71,28 @@ static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
}
+static inline qreal qt_sRGB_to_linear_RGB(qreal f)
+{
+ return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
+}
+
+static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
+{
+ return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
+ qt_sRGB_to_linear_RGB(color.y()),
+ qt_sRGB_to_linear_RGB(color.z()),
+ color.w());
+}
+
+static inline qreal fontSmoothingGamma()
+{
+ static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
+ return fontSmoothingGamma;
+}
+
+
+// ***** legacy (GL) material shader implementations
+
static inline qreal qsg_device_pixel_ratio(QOpenGLContext *ctx)
{
qreal devicePixelRatio = 1;
@@ -122,12 +144,6 @@ QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/textmask.frag"));
}
-static inline qreal fontSmoothingGamma()
-{
- static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
- return fontSmoothingGamma;
-}
-
void QSGTextMaskShader::initialize()
{
m_matrix_id = program()->uniformLocation("matrix");
@@ -149,8 +165,8 @@ void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEf
if (updated
|| oldMaterial == nullptr
|| oldMaterial->texture()->textureId() != material->texture()->textureId()) {
- program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
- 1.0 / material->cacheTextureHeight()));
+ program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(),
+ 1.0 / material->openglGlyphCache()->height()));
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
@@ -263,19 +279,6 @@ void QSG24BitTextMaskShader::deactivate()
funcs->glDisable(GL_FRAMEBUFFER_SRGB);
}
-static inline qreal qt_sRGB_to_linear_RGB(qreal f)
-{
- return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
-}
-
-static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
-{
- return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
- qt_sRGB_to_linear_RGB(color.y()),
- qt_sRGB_to_linear_RGB(color.z()),
- color.w());
-}
-
void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
QSGTextMaskShader::updateState(state, newEffect, oldEffect);
@@ -371,12 +374,12 @@ void QSGStyledTextShader::updateState(const RenderState &state,
if (updated
|| oldMaterial == nullptr
|| oldMaterial->texture()->textureId() != material->texture()->textureId()) {
- program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
- 1.0 / material->cacheTextureHeight()));
+ program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(),
+ 1.0 / material->openglGlyphCache()->height()));
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
- // Set the mag/min filters to be linear. We only need to do this when the texture
+ // Set the mag/min filters to be nearest. We only need to do this when the texture
// has been recreated.
if (updated) {
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -399,8 +402,294 @@ public:
}
};
-QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
- : m_texture(nullptr)
+
+// ***** RHI shader implementations
+
+class QSGTextMaskRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat);
+
+ bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+protected:
+ QFontEngine::GlyphFormat m_glyphFormat;
+};
+
+QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : m_glyphFormat(glyphFormat)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.vert.qsb"));
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"));
+}
+
+bool QSGTextMaskRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ // updateUniformData() is called before updateSampledImage() by the
+ // renderer. Hence updating the glyph cache stuff here.
+ const bool updated = mat->ensureUpToDate();
+ Q_ASSERT(mat->texture());
+ Q_ASSERT(oldMat == nullptr || oldMat->texture());
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (updated || !oldMat || oldMat->texture()->rhiTexture() != mat->texture()->rhiTexture()) {
+ const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(),
+ 1.0f / mat->rhiGlyphCache()->height());
+ memcpy(buf->data() + 64 + 16, &textureScale, 8);
+ changed = true;
+ }
+
+ if (!oldMat) {
+ float dpr = state.devicePixelRatio();
+ memcpy(buf->data() + 64 + 16 + 8, &dpr, 4);
+ }
+
+ // move texture uploads/copies onto the renderer's soon-to-be-committed list
+ mat->rhiGlyphCache()->commitResourceUpdates(state.resourceUpdateBatch());
+
+ return changed;
+}
+
+void QSGTextMaskRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ Q_UNUSED(state);
+ if (binding != 1)
+ return;
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTexture *t = mat->texture();
+ t->setFiltering(QSGTexture::Nearest);
+ *texture = t;
+}
+
+class QSG8BitTextMaskRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG8BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask.frag.qsb"));
+ }
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSG8BitTextMaskRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 80);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSG24BitTextMaskRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG24BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ setFlag(UpdatesGraphicsPipelineState, true);
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/24bittextmask.frag.qsb"));
+ }
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+// ### gamma correction (sRGB) Unsurprisingly, the GL approach is not portable
+// to anything else - it just does not work that way, there is no opt-in/out
+// switch and magic winsys-provided maybe-sRGB buffers. When requesting an sRGB
+// QRhiSwapChain (which we do not do), it is full sRGB, with the sRGB
+// framebuffer update and blending always on... Could we do gamma correction in
+// the shader for text? (but that's bad for blending?)
+
+bool QSG24BitTextMaskRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ // shader takes vec4 but uses alpha only; coloring happens via the blend constant
+ const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+
+ ps->blendEnable = true;
+ ps->srcColor = GraphicsPipelineState::ConstantColor;
+ ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
+
+ QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ // if (useSRGB())
+ // color = qt_sRGB_to_linear_RGB(color);
+
+ // this is dynamic state but it's - magic! - taken care of by the renderer
+ ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), color.w());
+
+ return true;
+}
+
+class QSG32BitColorTextRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG32BitColorTextRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/32bitcolortext.frag.qsb"));
+ }
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSG32BitColorTextRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ // shader takes vec4 but uses alpha only
+ const QVector4D color(0, 0, 0, mat->color().w() * state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSGStyledTextRhiShader : public QSG8BitTextMaskRhiShader
+{
+public:
+ QSGStyledTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSG8BitTextMaskRhiShader(glyphFormat, alphaTexture)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.frag.qsb"));
+ }
+
+ bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSGStyledTextRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSG8BitTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGStyledTextMaterial *mat = static_cast<QSGStyledTextMaterial *>(newMaterial);
+ QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 120);
+
+ // matrix..dpr + 1 float padding (vec4 must be aligned to 16)
+ const int startOffset = 64 + 16 + 8 + 4 + 4;
+
+ if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
+ const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity());
+ memcpy(buf->data() + startOffset, &styleColor, 16);
+ changed = true;
+ }
+
+ if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) {
+ const QVector2D v = mat->styleShift();
+ memcpy(buf->data() + startOffset + 16, &v, 8);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSGOutlinedTextRhiShader : public QSGStyledTextRhiShader
+{
+public:
+ QSGOutlinedTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSGStyledTextRhiShader(glyphFormat, alphaTexture)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.frag.qsb"));
+ }
+};
+
+
+// ***** common material stuff
+
+QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+ : m_rc(qobject_cast<QSGDefaultRenderContext *>(rc))
+ , m_texture(nullptr)
, m_glyphCache(nullptr)
, m_font(font)
{
@@ -416,10 +705,11 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
{
Q_ASSERT(m_font.isValid());
+ setFlag(SupportsRhiShader, true);
setFlag(Blending, true);
- QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
- Q_ASSERT(ctx != nullptr);
+ Q_ASSERT(m_rc);
+ m_rhi = m_rc->rhi();
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
@@ -435,20 +725,36 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
: QFontEngine::Format_A32;
}
- qreal devicePixelRatio = qsg_device_pixel_ratio(ctx);
-
+ QOpenGLContext *ctx = nullptr;
+ qreal devicePixelRatio;
+ void *cacheKey;
+ if (m_rhi) {
+ cacheKey = m_rhi;
+ // ### no idea what the QWindow is (esp. since we are not even
+ // rendering at this point), and anyway is the original logic correct
+ // even...
+ devicePixelRatio = qGuiApp->devicePixelRatio();
+ } else {
+ ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
+ Q_ASSERT(ctx != nullptr);
+ cacheKey = ctx;
+ devicePixelRatio = qsg_device_pixel_ratio(ctx);
+ }
QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio);
if (!fontEngine->supportsTransformation(glyphCacheTransform))
glyphCacheTransform = QTransform();
- m_glyphCache = fontEngine->glyphCache(ctx, glyphFormat, glyphCacheTransform);
+ m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform);
+
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
- m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
- fontEngine->setGlyphCache(ctx, m_glyphCache.data());
- auto sg = QSGDefaultRenderContext::from(ctx);
- Q_ASSERT(sg);
- sg->registerFontengineForCleanup(fontEngine);
+ if (m_rhi)
+ m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform);
+ else
+ m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
+
+ fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
+ m_rc->registerFontengineForCleanup(fontEngine);
}
}
}
@@ -558,21 +864,45 @@ QSGMaterialType *QSGTextMaskMaterial::type() const
}
}
-QOpenGLTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
+QTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
+{
+ return static_cast<QTextureGlyphCache *>(m_glyphCache.data());
+}
+
+QOpenGLTextureGlyphCache *QSGTextMaskMaterial::openglGlyphCache() const
+{
+ return static_cast<QOpenGLTextureGlyphCache *>(glyphCache());
+}
+
+QSGRhiTextureGlyphCache *QSGTextMaskMaterial::rhiGlyphCache() const
{
- return static_cast<QOpenGLTextureGlyphCache*>(m_glyphCache.data());
+ return static_cast<QSGRhiTextureGlyphCache *>(glyphCache());
}
QSGMaterialShader *QSGTextMaskMaterial::createShader() const
{
- switch (QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat()) {
- case QFontEngine::Format_ARGB:
- return new QSG32BitColorTextShader(glyphFormat);
- case QFontEngine::Format_A32:
- return new QSG24BitTextMaskShader(glyphFormat);
- case QFontEngine::Format_A8:
- default:
- return new QSG8BitTextMaskShader(glyphFormat);
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ const QFontEngine::GlyphFormat glyphFormat = gc->glyphFormat();
+ switch (glyphFormat) {
+ case QFontEngine::Format_ARGB:
+ return new QSG32BitColorTextRhiShader(glyphFormat);
+ case QFontEngine::Format_A32:
+ return new QSG24BitTextMaskRhiShader(glyphFormat);
+ case QFontEngine::Format_A8:
+ default:
+ return new QSG8BitTextMaskRhiShader(glyphFormat, gc->eightBitFormatIsAlphaSwizzled());
+ }
+ } else {
+ switch (QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat()) {
+ case QFontEngine::Format_ARGB:
+ return new QSG32BitColorTextShader(glyphFormat);
+ case QFontEngine::Format_A32:
+ return new QSG24BitTextMaskShader(glyphFormat);
+ case QFontEngine::Format_A8:
+ default:
+ return new QSG8BitTextMaskShader(glyphFormat);
+ }
}
}
@@ -600,36 +930,40 @@ int QSGTextMaskMaterial::compare(const QSGMaterial *o) const
bool QSGTextMaskMaterial::ensureUpToDate()
{
- QSize glyphCacheSize(glyphCache()->width(), glyphCache()->height());
- if (glyphCacheSize != m_size) {
- if (m_texture)
- delete m_texture;
- m_texture = new QSGPlainTexture();
- m_texture->setTextureId(glyphCache()->texture());
- m_texture->setTextureSize(QSize(glyphCache()->width(), glyphCache()->height()));
- m_texture->setOwnsTexture(false);
-
- m_size = glyphCacheSize;
+ if (m_rhi) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ QSize glyphCacheSize(gc->width(), gc->height());
+ if (glyphCacheSize != m_size) {
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGPlainTexture;
+ m_texture->setTexture(gc->texture());
+ m_texture->setTextureSize(QSize(gc->width(), gc->height()));
+ m_texture->setOwnsTexture(false);
+ m_size = glyphCacheSize;
+ return true;
+ }
+ return false;
- return true;
} else {
+ QSize glyphCacheSize(openglGlyphCache()->width(), openglGlyphCache()->height());
+ if (glyphCacheSize != m_size) {
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGPlainTexture();
+ m_texture->setTextureId(openglGlyphCache()->texture());
+ m_texture->setTextureSize(QSize(openglGlyphCache()->width(), openglGlyphCache()->height()));
+ m_texture->setOwnsTexture(false);
+ m_size = glyphCacheSize;
+ return true;
+ }
return false;
}
}
-int QSGTextMaskMaterial::cacheTextureWidth() const
-{
- return glyphCache()->width();
-}
-int QSGTextMaskMaterial::cacheTextureHeight() const
-{
- return glyphCache()->height();
-}
-
-
-QSGStyledTextMaterial::QSGStyledTextMaterial(const QRawFont &font)
- : QSGTextMaskMaterial(font, QFontEngine::Format_A8)
+QSGStyledTextMaterial::QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font)
+ : QSGTextMaskMaterial(rc, font, QFontEngine::Format_A8)
{
}
@@ -641,7 +975,12 @@ QSGMaterialType *QSGStyledTextMaterial::type() const
QSGMaterialShader *QSGStyledTextMaterial::createShader() const
{
- return new QSGStyledTextShader(glyphCache()->glyphFormat());
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ return new QSGStyledTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ } else {
+ return new QSGStyledTextShader(glyphCache()->glyphFormat());
+ }
}
int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
@@ -658,8 +997,8 @@ int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
}
-QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(const QRawFont &font)
- : QSGStyledTextMaterial(font)
+QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(QSGRenderContext *rc, const QRawFont &font)
+ : QSGStyledTextMaterial(rc, font)
{
}
@@ -671,7 +1010,12 @@ QSGMaterialType *QSGOutlinedTextMaterial::type() const
QSGMaterialShader *QSGOutlinedTextMaterial::createShader() const
{
- return new QSGOutlinedTextShader(glyphCache()->glyphFormat());
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ return new QSGOutlinedTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ } else {
+ return new QSGOutlinedTextShader(glyphCache()->glyphFormat());
+ }
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index cfa1c1dad2..4cff2d3d24 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
- QSGDefaultGlyphNode();
+ QSGDefaultGlyphNode(QSGRenderContext *context);
~QSGDefaultGlyphNode();
void setMaterialColor(const QColor &color) override;
void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
@@ -76,8 +76,9 @@ private:
void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; }
+ QSGRenderContext *m_context;
DefaultGlyphNodeType m_glyphNodeType;
- QLinkedList<QSGNode *> m_nodesToDelete;
+ QVector<QSGNode *> m_nodesToDelete;
struct GlyphInfo {
QVector<quint32> indexes;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index b0a2788dd8..cd1b331278 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -57,7 +57,8 @@
#include <QtQuick/qsgtexture.h>
#include <QtQuick/qsggeometry.h>
#include <qshareddata.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
+#include <QtQuick/private/qsgrhitextureglyphcache_p.h>
#include <qrawfont.h>
#include <qmargins.h>
@@ -65,10 +66,13 @@ QT_BEGIN_NAMESPACE
class QFontEngine;
class Geometry;
+class QSGRenderContext;
+class QSGDefaultRenderContext;
+
class QSGTextMaskMaterial: public QSGMaterial
{
public:
- QSGTextMaskMaterial(const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+ QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
virtual ~QSGTextMaskMaterial();
QSGMaterialType *type() const override;
@@ -81,12 +85,12 @@ public:
QSGTexture *texture() const { return m_texture; }
- int cacheTextureWidth() const;
- int cacheTextureHeight() const;
-
bool ensureUpToDate();
- QOpenGLTextureGlyphCache *glyphCache() const;
+ QTextureGlyphCache *glyphCache() const;
+ QOpenGLTextureGlyphCache *openglGlyphCache() const;
+ QSGRhiTextureGlyphCache *rhiGlyphCache() const;
+
void populate(const QPointF &position,
const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions,
QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine,
@@ -95,9 +99,11 @@ public:
private:
void init(QFontEngine::GlyphFormat glyphFormat);
+ QSGDefaultRenderContext *m_rc;
QSGPlainTexture *m_texture;
QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache;
QRawFont m_font;
+ QRhi *m_rhi;
QVector4D m_color;
QSize m_size;
};
@@ -105,7 +111,7 @@ private:
class QSGStyledTextMaterial : public QSGTextMaskMaterial
{
public:
- QSGStyledTextMaterial(const QRawFont &font);
+ QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font);
virtual ~QSGStyledTextMaterial() { }
void setStyleShift(const QVector2D &shift) { m_styleShift = shift; }
@@ -117,7 +123,6 @@ public:
QSGMaterialType *type() const override;
QSGMaterialShader *createShader() const override;
-
int compare(const QSGMaterial *other) const override;
private:
@@ -128,7 +133,7 @@ private:
class QSGOutlinedTextMaterial : public QSGStyledTextMaterial
{
public:
- QSGOutlinedTextMaterial(const QRawFont &font);
+ QSGOutlinedTextMaterial(QSGRenderContext *rc, const QRawFont &font);
~QSGOutlinedTextMaterial() { }
QSGMaterialType *type() const override;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
index 5dd6eaa4ca..d1711f7524 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
@@ -38,10 +38,12 @@
****************************************************************************/
#include "qsgdefaultinternalimagenode_p.h"
+#include <private/qsgdefaultrendercontext_p.h>
#include <private/qsgmaterialshader_p.h>
#include <private/qsgtexturematerial_p.h>
#include <QtGui/qopenglfunctions.h>
#include <QtCore/qmath.h>
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
@@ -59,9 +61,18 @@ protected:
int m_pixelSizeLoc;
};
+class SmoothTextureMaterialRhiShader : public QSGTextureMaterialRhiShader
+{
+public:
+ SmoothTextureMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
QSGSmoothTextureMaterial::QSGSmoothTextureMaterial()
{
+ setFlag(SupportsRhiShader, true);
setFlag(RequiresFullMatrixExceptTranslate, true);
setFlag(Blending, true);
}
@@ -79,7 +90,10 @@ QSGMaterialType *QSGSmoothTextureMaterial::type() const
QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const
{
- return new SmoothTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SmoothTextureMaterialRhiShader;
+ else
+ return new SmoothTextureMaterialShader;
}
SmoothTextureMaterialShader::SmoothTextureMaterialShader()
@@ -116,7 +130,33 @@ void SmoothTextureMaterialShader::initialize()
QSGTextureMaterialShader::initialize();
}
-QSGDefaultInternalImageNode::QSGDefaultInternalImageNode()
+SmoothTextureMaterialRhiShader::SmoothTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.frag.qsb"));
+}
+
+bool SmoothTextureMaterialRhiShader::updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (!oldMaterial) {
+ // The viewport is constant, so set the pixel size uniform only once (per batches with the same material).
+ const QRect r = state.viewportRect();
+ const QVector2D v(2.0f / r.width(), 2.0f / r.height());
+ memcpy(buf->data() + 64 + 8, &v, 8);
+ changed = true;
+ }
+
+ changed |= QSGTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ return changed;
+}
+
+
+QSGDefaultInternalImageNode::QSGDefaultInternalImageNode(QSGDefaultRenderContext *rc)
+ : m_rc(rc)
{
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
@@ -209,14 +249,19 @@ bool QSGDefaultInternalImageNode::supportsWrap(const QSize &size) const
{
bool wrapSupported = true;
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (m_rc->rhi()) {
+ wrapSupported = m_rc->rhi()->isFeatureSupported(QRhi::NPOTTextureRepeat)
+ || (isPowerOfTwo(size.width()) && isPowerOfTwo(size.height()));
+ } else {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
#ifndef QT_OPENGL_ES_2
- if (ctx->isOpenGLES())
+ if (ctx->isOpenGLES())
#endif
- {
- bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
- wrapSupported = npotSupported || !isNpot;
+ {
+ bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ wrapSupported = npotSupported || !isNpot;
+ }
}
return wrapSupported;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
index 1fc7834bd1..b4cfbd9ece 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
@@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
+class QSGDefaultRenderContext;
+
class Q_QUICK_PRIVATE_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial
{
public:
@@ -73,7 +75,7 @@ protected:
class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode
{
public:
- QSGDefaultInternalImageNode();
+ QSGDefaultInternalImageNode(QSGDefaultRenderContext *rc);
void setMipmapFiltering(QSGTexture::Filtering filtering) override;
void setFiltering(QSGTexture::Filtering filtering) override;
@@ -87,6 +89,7 @@ public:
bool supportsWrap(const QSize &size) const override;
private:
+ QSGDefaultRenderContext *m_rc;
QSGOpaqueTextureMaterial m_material;
QSGTextureMaterial m_materialO;
QSGSmoothTextureMaterial m_smoothMaterial;
diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
index fd0dcebd57..417b63dee3 100644
--- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
@@ -105,14 +105,61 @@ void SmoothColorMaterialShader::initialize()
m_pixelSizeLoc = program()->uniformLocation("pixelSize");
}
+
+class SmoothColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ SmoothColorMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+SmoothColorMaterialRhiShader::SmoothColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.frag.qsb"));
+}
+
+bool SmoothColorMaterialRhiShader::updateUniformData(const RenderState &state, QSGMaterial *, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (oldMaterial == nullptr) {
+ // The viewport is constant, so set the pixel size uniform only once.
+ const QRect r = state.viewportRect();
+ const QVector2D v(2.0f / r.width(), 2.0f / r.height());
+ Q_ASSERT(sizeof(v) == 8);
+ memcpy(buf->data() + 64, &v, 8);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 72, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+
QSGSmoothColorMaterial::QSGSmoothColorMaterial()
{
setFlag(RequiresFullMatrixExceptTranslate, true);
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
int QSGSmoothColorMaterial::compare(const QSGMaterial *) const
{
+ // all state in vertex attributes -> all smoothcolor materials are equal
return 0;
}
@@ -124,7 +171,10 @@ QSGMaterialType *QSGSmoothColorMaterial::type() const
QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
{
- return new SmoothColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SmoothColorMaterialRhiShader;
+ else
+ return new SmoothColorMaterialShader;
}
QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode()
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 73b79c6300..60a525b877 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -44,9 +44,13 @@
#include <QtQuick/private/qsgbatchrenderer_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgrhiatlastexture_p.h>
+#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgmaterialrhishader_p.h>
+
+#include <QtQuick/private/qsgopenglatlastexture_p.h>
#include <QtQuick/private/qsgcompressedtexture_p.h>
-#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgopengldistancefieldglyphcache_p.h>
QT_BEGIN_NAMESPACE
@@ -54,68 +58,81 @@ QT_BEGIN_NAMESPACE
QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
: QSGRenderContext(context)
+ , m_rhi(nullptr)
, m_gl(nullptr)
, m_depthStencilManager(nullptr)
, m_maxTextureSize(0)
, m_brokenIBOs(false)
, m_serializedRender(false)
, m_attachToGLContext(true)
- , m_atlasManager(nullptr)
+ , m_glAtlasManager(nullptr)
+ , m_rhiAtlasManager(nullptr)
+ , m_currentFrameCommandBuffer(nullptr)
{
-
}
/*!
Initializes the scene graph render context with the GL context \a context. This also
emits the ready() signal so that the QML graph can start building scene graph nodes.
*/
-void QSGDefaultRenderContext::initialize(void *context)
+void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params)
{
if (!m_sg)
return;
- QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context);
-
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+ const InitParams *initParams = static_cast<const InitParams *>(params);
+ if (initParams->sType != INIT_PARAMS_MAGIC)
+ qFatal("QSGDefaultRenderContext: Invalid parameters passed to initialize()");
- // Sanity check the surface format, in case it was overridden by the application
- QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
- QSurfaceFormat actual = openglContext->format();
- if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
- qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
- if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
- qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+ m_initParams = *initParams;
- if (!m_atlasManager)
- m_atlasManager = new QSGAtlasTexture::Manager();
+ m_rhi = m_initParams.rhi;
+ if (m_rhi) {
+ m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ if (!m_rhiAtlasManager)
+ m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
+ } else {
+ QOpenGLFunctions *funcs = m_rhi ? nullptr : QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
- Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
- m_gl = openglContext;
- if (m_attachToGLContext) {
- Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
- openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
- }
- m_sg->renderContextInitialized(this);
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
+ QSurfaceFormat actual = m_initParams.openGLContext->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
#ifdef Q_OS_LINUX
- const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
- if (vendor && strstr(vendor, "nouveau"))
- m_brokenIBOs = true;
- const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (renderer && strstr(renderer, "llvmpipe"))
- m_serializedRender = true;
- if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
- m_brokenIBOs = true;
+ const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
+ if (vendor && strstr(vendor, "nouveau"))
+ m_brokenIBOs = true;
+ const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
+ if (renderer && strstr(renderer, "llvmpipe"))
+ m_serializedRender = true;
+ if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
+ m_brokenIBOs = true;
#endif
+ Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
+ m_gl = m_initParams.openGLContext;
+ if (m_attachToGLContext) {
+ Q_ASSERT(!m_gl->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+ }
+
+ if (!m_glAtlasManager)
+ m_glAtlasManager = new QSGOpenGLAtlasTexture::Manager(m_initParams.initialSurfacePixelSize);
+ }
+
+ m_sg->renderContextInitialized(this);
+
emit initialized();
}
-
void QSGDefaultRenderContext::invalidate()
{
- if (!m_gl)
+ if (!m_gl && !m_rhi)
return;
qDeleteAll(m_texturesToDelete);
@@ -138,11 +155,18 @@ void QSGDefaultRenderContext::invalidate()
deferred deleted last.
Another alternative would be to use a QPointer in
- QSGAtlasTexture::Texture, but this seemed simpler.
+ QSGOpenGLAtlasTexture::Texture, but this seemed simpler.
*/
- m_atlasManager->invalidate();
- m_atlasManager->deleteLater();
- m_atlasManager = nullptr;
+ if (m_glAtlasManager) {
+ m_glAtlasManager->invalidate();
+ m_glAtlasManager->deleteLater();
+ m_glAtlasManager = nullptr;
+ }
+ if (m_rhiAtlasManager) {
+ m_rhiAtlasManager->invalidate();
+ m_rhiAtlasManager->deleteLater();
+ m_rhiAtlasManager = nullptr;
+ }
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
@@ -151,7 +175,7 @@ void QSGDefaultRenderContext::invalidate()
// sequence. (see qsgdefaultglyphnode_p.cpp's init())
for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
- (*it)->clearGlyphCache(m_gl);
+ (*it)->clearGlyphCache(m_gl ? (void *) m_gl : (void *) m_rhi);
if (!(*it)->ref.deref())
delete *it;
}
@@ -163,17 +187,28 @@ void QSGDefaultRenderContext::invalidate()
qDeleteAll(m_glyphCaches);
m_glyphCaches.clear();
- if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
+ if (m_gl && m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+
m_gl = nullptr;
+ m_rhi = nullptr;
if (m_sg)
m_sg->renderContextInvalidated(this);
+
emit invalidated();
}
static QBasicMutex qsg_framerender_mutex;
+void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
+}
+
void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
{
if (m_serializedRender)
@@ -185,6 +220,38 @@ void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
qsg_framerender_mutex.unlock();
}
+void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp,
+ QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_ASSERT(!m_currentFrameCommandBuffer);
+
+ renderer->setRenderTarget(rt);
+ renderer->setRenderPassDescriptor(rp);
+ renderer->setCommandBuffer(cb);
+ renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
+
+ m_currentFrameCommandBuffer = cb;
+}
+
+void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
+{
+ renderer->renderScene();
+}
+
+void QSGDefaultRenderContext::endNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+ m_currentFrameCommandBuffer = nullptr;
+}
+
/*!
Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
*/
@@ -226,13 +293,21 @@ QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint fla
// The atlas implementation is only supported from the render thread and
// does not support mipmaps.
- if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
- QSGTexture *t = m_atlasManager->create(image, alpha);
- if (t)
- return t;
+ if (m_rhi) {
+ if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
+ QSGTexture *t = m_rhiAtlasManager->create(image, alpha);
+ if (t)
+ return t;
+ }
+ } else {
+ if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
+ QSGTexture *t = m_glAtlasManager->create(image, alpha);
+ if (t)
+ return t;
+ }
}
- QSGPlainTexture *texture = new QSGPlainTexture();
+ QSGPlainTexture *texture = new QSGPlainTexture;
texture->setImage(image);
if (texture->hasAlphaChannel() && !alpha)
texture->setHasAlphaChannel(false);
@@ -247,9 +322,15 @@ QSGRenderer *QSGDefaultRenderContext::createRenderer()
QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
{
- // The atlas implementation is only supported from the render thread
- if (openglContext() && QThread::currentThread() == openglContext()->thread())
- return m_atlasManager->create(factory);
+ // This is only used for atlasing compressed textures. Returning null implies no atlas.
+
+ if (m_rhi) {
+ // ###
+ } else if (openglContext() && QThread::currentThread() == openglContext()->thread()) {
+ // The atlas implementation is only supported from the render thread
+ return m_glAtlasManager->create(factory);
+ }
+
return nullptr;
}
@@ -275,7 +356,7 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri
p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
p->link();
if (!p->isLinked())
- qWarning() << "shader compilation failed:" << endl << p->log();
+ qWarning() << "shader compilation failed:" << Qt::endl << p->log();
} else {
shader->compile();
}
@@ -307,6 +388,11 @@ void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
shader->initialize();
}
+void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant)
+{
+ QSGMaterialRhiShaderPrivate::get(shader)->prepare(shaderVariant);
+}
+
void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach)
{
Q_ASSERT(!isValid());
@@ -320,6 +406,9 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
bool QSGDefaultRenderContext::separateIndexBuffer() const
{
+ if (m_rhi)
+ return true;
+
// WebGL: A given WebGLBuffer object may only be bound to one of
// the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its
// lifetime. An attempt to bind a buffer object to the other
@@ -335,7 +424,10 @@ QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(con
QString key = fontKey(font);
QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
if (!cache) {
- cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font);
+ if (m_rhi)
+ cache = new QSGRhiDistanceFieldGlyphCache(m_rhi, font);
+ else
+ cache = new QSGOpenGLDistanceFieldGlyphCache(openglContext(), font);
m_glyphCaches.insert(key, cache);
}
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 57aa4b4c90..79bc9dd76d 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -52,15 +52,28 @@
//
#include <QtQuick/private/qsgcontext_p.h>
+#include <QtGui/private/qshader_p.h>
+
+#if QT_CONFIG(opengl)
#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
+#endif
QT_BEGIN_NAMESPACE
+class QRhi;
+class QRhiCommandBuffer;
class QOpenGLContext;
class QSGMaterialShader;
+class QSGMaterialRhiShader;
class QOpenGLFramebufferObject;
+class QSGDepthStencilBufferManager;
+class QSGDepthStencilBuffer;
-namespace QSGAtlasTexture {
+namespace QSGOpenGLAtlasTexture {
+ class Manager;
+}
+
+namespace QSGRhiAtlasTexture {
class Manager;
}
@@ -70,12 +83,40 @@ class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderContext : public QSGRenderContext
public:
QSGDefaultRenderContext(QSGContext *context);
+ QRhi *rhi() const override { return m_rhi; }
QOpenGLContext *openglContext() const { return m_gl; }
- bool isValid() const override { return m_gl; }
-
- void initialize(void *context) override;
+ bool isValid() const override { return m_gl || m_rhi; }
+
+ static const int INIT_PARAMS_MAGIC = 0x50E;
+ struct InitParams : public QSGRenderContext::InitParams {
+ int sType = INIT_PARAMS_MAGIC; // help discovering broken code passing something else as 'context'
+ QRhi *rhi = nullptr;
+ int sampleCount = 1; // 1, 4, 8, ...
+ QOpenGLContext *openGLContext = nullptr; // ### Qt 6: remove
+ // only used as a hint f.ex. in the texture atlas init
+ QSize initialSurfacePixelSize;
+ // The first window that will be used with this rc, if available.
+ // Only a hint, to help picking better values for atlases.
+ QSurface *maybeSurface = nullptr;
+ };
+
+ void initialize(const QSGRenderContext::InitParams *params) override;
void invalidate() override;
+
+ void beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData) override;
void renderNextFrame(QSGRenderer *renderer, uint fboId) override;
+ void endNextFrame(QSGRenderer *renderer) override;
+
+ void beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData) override;
+ void renderNextRhiFrame(QSGRenderer *renderer) override;
+ void endNextRhiFrame(QSGRenderer *renderer) override;
QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font) override;
@@ -88,6 +129,7 @@ public:
virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = nullptr, const char *fragmentCode = nullptr);
virtual void initializeShader(QSGMaterialShader *shader);
+ virtual void initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant);
void setAttachToGraphicsContext(bool attach) override;
@@ -97,16 +139,27 @@ public:
int maxTextureSize() const override { return m_maxTextureSize; }
bool separateIndexBuffer() const;
+ int msaaSampleCount() const { return m_initParams.sampleCount; }
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() const {
+ // may be null if not in an active frame, but returning null is valid then
+ return m_currentFrameCommandBuffer;
+ }
+
protected:
static QString fontKey(const QRawFont &font);
+ InitParams m_initParams;
+ QRhi *m_rhi;
QOpenGLContext *m_gl;
QSGDepthStencilBufferManager *m_depthStencilManager;
int m_maxTextureSize;
bool m_brokenIBOs;
bool m_serializedRender;
bool m_attachToGLContext;
- QSGAtlasTexture::Manager *m_atlasManager;
+ QSGOpenGLAtlasTexture::Manager *m_glAtlasManager;
+ QSGRhiAtlasTexture::Manager *m_rhiAtlasManager;
+ QRhiCommandBuffer *m_currentFrameCommandBuffer;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp
index 8761d99c1f..4d4d9f6971 100644
--- a/src/quick/scenegraph/qsgdefaultspritenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp
@@ -84,6 +84,7 @@ public:
QQuickSpriteMaterial::QQuickSpriteMaterial()
{
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
QQuickSpriteMaterial::~QQuickSpriteMaterial()
@@ -91,10 +92,10 @@ QQuickSpriteMaterial::~QQuickSpriteMaterial()
delete texture;
}
-class SpriteMaterialData : public QSGMaterialShader
+class SpriteMaterialShader : public QSGMaterialShader
{
public:
- SpriteMaterialData()
+ SpriteMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.frag"));
@@ -135,9 +136,79 @@ public:
int m_animPos_id;
};
+class SpriteMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ SpriteMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+SpriteMaterialRhiShader::SpriteMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"));
+}
+
+bool SpriteMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 96);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ float animPosAndData[7] = { mat->animX1, mat->animY1, mat->animX2, mat->animY2,
+ mat->animW, mat->animH, mat->animT };
+ memcpy(buf->data() + 64, animPosAndData, 28);
+ changed = true;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 92, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void SpriteMaterialRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ if (binding != 1)
+ return;
+
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
+
+ QSGTexture *t = mat->texture;
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
QSGMaterialShader *QQuickSpriteMaterial::createShader() const
{
- return new SpriteMaterialData;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SpriteMaterialRhiShader;
+ else
+ return new SpriteMaterialShader;
}
static QSGGeometry::Attribute Sprite_Attributes[] = {
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index ae6336718e..0fa680a244 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -224,15 +224,19 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
const QPointF position = positions.at(i);
const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
- if (texture->textureId && !m_texture)
+ if ((!texture->rhiBased && texture->textureId && !m_texture)
+ || (texture->rhiBased && texture->texture && !m_texture))
+ {
m_texture = texture;
+ }
// As we use UNSIGNED_SHORT indexing in the geometry, we overload the
- // "glyphsInOtherTextures" concept as overflow for if there are more than
- // 65536 vertices to render which would otherwise exceed the maximum index
- // size. This will cause sub-nodes to be recursively created to handle any
- // number of glyphs.
- if (m_texture != texture || vp.size() >= 65536) {
+ // "glyphsInOtherTextures" concept as overflow for if there are more
+ // than 65535 vertices to render which would otherwise exceed the
+ // maximum index size. (leave 0xFFFF unused in order not to clash with
+ // primitive restart) This will cause sub-nodes to be recursively
+ // created to handle any number of glyphs.
+ if (m_texture != texture || vp.size() >= 65535) {
if (texture->textureId) {
GlyphInfo &glyphInfo = glyphsInOtherTextures[texture];
glyphInfo.indexes.append(glyphIndex);
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
index aa58218505..9121363159 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qsgdistancefieldglyphnode_p_p.h"
-#include <QtQuick/private/qsgtexture_p.h>
+#include "qsgrhidistancefieldglyphcache_p.h"
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qsurface.h>
#include <QtGui/qwindow.h>
@@ -46,6 +46,30 @@
QT_BEGIN_NAMESPACE
+static float qt_sg_envFloat(const char *name, float defaultValue)
+{
+ if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
+ return defaultValue;
+ bool ok = false;
+ const float value = qgetenv(name).toFloat(&ok);
+ return ok ? value : defaultValue;
+}
+
+static float thresholdFunc(float glyphScale)
+{
+ static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
+ static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
+ static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
+ static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
+ return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
+}
+
+static float spreadFunc(float glyphScale)
+{
+ static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
+ return range / glyphScale;
+}
+
class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader
{
public:
@@ -87,30 +111,6 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader()
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag"));
}
-static float qt_sg_envFloat(const char *name, float defaultValue)
-{
- if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
- return defaultValue;
- bool ok = false;
- const float value = qgetenv(name).toFloat(&ok);
- return ok ? value : defaultValue;
-}
-
-static float thresholdFunc(float glyphScale)
-{
- static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
- static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
- static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
- static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
- return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
-}
-
-static float spreadFunc(float glyphScale)
-{
- static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
- return range / glyphScale;
-}
-
void QSGDistanceFieldTextMaterialShader::updateAlphaRange()
{
float combinedScale = m_fontScale * m_matrixScale;
@@ -207,16 +207,115 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q
}
}
+class QSGDistanceFieldTextMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+ void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+protected:
+ float m_fontScale = 1.0;
+ float m_matrixScale = 1.0;
+};
+
+QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"));
+}
+
+bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
+ QSGDistanceFieldTextMaterial *oldMat = static_cast<QSGDistanceFieldTextMaterial *>(oldMaterial);
+
+ // updateUniformData() is called before updateSampledImage() by the
+ // renderer. Hence updating the glyph cache stuff here.
+ const bool textureUpdated = mat->updateTextureSizeAndWrapper();
+ Q_ASSERT(mat->wrapperTexture());
+ Q_ASSERT(oldMat == nullptr || oldMat->texture());
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 104);
+
+ bool updateRange = false;
+ if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
+ m_fontScale = mat->fontScale();
+ updateRange = true;
+ }
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
+ updateRange = true;
+ }
+ if (textureUpdated || !oldMat || oldMat->texture()->texture != mat->texture()->texture) {
+ const QVector2D ts(1.0f / mat->textureSize().width(), 1.0f / mat->textureSize().height());
+ Q_ASSERT(sizeof(ts) == 8);
+ memcpy(buf->data() + 64, &ts, 8);
+ changed = true;
+ }
+ if (!oldMat || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ const QVector4D color = mat->color() * state.opacity();
+ Q_ASSERT(sizeof(color) == 16);
+ memcpy(buf->data() + 80, &color, 16);
+ changed = true;
+ }
+ if (updateRange) { // deferred because depends on m_fontScale and m_matrixScale
+ const float combinedScale = m_fontScale * m_matrixScale;
+ const float base = thresholdFunc(combinedScale);
+ const float range = spreadFunc(combinedScale);
+ const QVector2D alphaMinMax(qMax(0.0f, base - range), qMin(base + range, 1.0f));
+ memcpy(buf->data() + 96, &alphaMinMax, 8);
+ changed = true;
+ }
+
+ // move texture uploads/copies onto the renderer's soon-to-be-committed list
+ static_cast<QSGRhiDistanceFieldGlyphCache *>(mat->glyphCache())->commitResourceUpdates(state.resourceUpdateBatch());
+
+ return changed;
+}
+
+void QSGDistanceFieldTextMaterialRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ Q_UNUSED(state);
+ if (binding != 1)
+ return;
+
+ QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
+ QSGTexture *t = mat->wrapperTexture();
+ t->setFiltering(QSGTexture::Linear);
+ *texture = t;
+}
+
QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
: m_glyph_cache(nullptr)
, m_texture(nullptr)
, m_fontScale(1.0)
+ , m_sgTexture(nullptr)
{
- setFlag(Blending | RequiresDeterminant, true);
+ setFlag(Blending | RequiresDeterminant | SupportsRhiShader, true);
}
QSGDistanceFieldTextMaterial::~QSGDistanceFieldTextMaterial()
{
+ delete m_sgTexture;
}
QSGMaterialType *QSGDistanceFieldTextMaterial::type() const
@@ -235,7 +334,10 @@ void QSGDistanceFieldTextMaterial::setColor(const QColor &color)
QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader() const
{
- return new QSGDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGDistanceFieldTextMaterialShader;
}
bool QSGDistanceFieldTextMaterial::updateTextureSize()
@@ -246,9 +348,26 @@ bool QSGDistanceFieldTextMaterial::updateTextureSize()
if (m_texture->size != m_size) {
m_size = m_texture->size;
return true;
- } else {
- return false;
}
+
+ return false;
+}
+
+// When using the RHI we need a QSGTexture wrapping the QRhiTexture, just
+// exposing a QRhiTexture * (which would be the equivalent of GLuint textureId)
+// is not sufficient to play nice with the material.
+bool QSGDistanceFieldTextMaterial::updateTextureSizeAndWrapper()
+{
+ bool updated = updateTextureSize();
+ if (updated) {
+ if (m_sgTexture)
+ delete m_sgTexture;
+ m_sgTexture = new QSGPlainTexture;
+ m_sgTexture->setTexture(m_texture->texture);
+ m_sgTexture->setTextureSize(m_size);
+ m_sgTexture->setOwnsTexture(false);
+ }
+ return updated;
}
int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
@@ -262,8 +381,8 @@ int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
}
if (m_color != other->m_color)
return &m_color < &other->m_color ? -1 : 1;
- int t0 = m_texture ? m_texture->textureId : 0;
- int t1 = other->m_texture ? other->m_texture->textureId : 0;
+ int t0 = m_texture ? (m_texture->rhiBased ? qintptr(m_texture->texture) : m_texture->textureId) : 0;
+ int t1 = other->m_texture ? (other->m_texture->rhiBased ? qintptr(other->m_texture->texture) : other->m_texture->textureId) : 0;
return t0 - t1;
}
@@ -308,6 +427,39 @@ void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state
}
}
+class DistanceFieldStyledTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
+{
+public:
+ DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+}
+
+bool DistanceFieldStyledTextMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldStyledTextMaterial *mat = static_cast<QSGDistanceFieldStyledTextMaterial *>(newMaterial);
+ QSGDistanceFieldStyledTextMaterial *oldMat = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 128);
+
+ if (!oldMat || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
+ QVector4D styleColor = mat->styleColor();
+ styleColor *= state.opacity();
+ memcpy(buf->data() + 112, &styleColor, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
QSGDistanceFieldStyledTextMaterial::QSGDistanceFieldStyledTextMaterial()
: QSGDistanceFieldTextMaterial()
{
@@ -391,6 +543,53 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat
updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius());
}
+class DistanceFieldOutlineTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
+{
+public:
+ DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"));
+}
+
+bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldOutlineTextMaterial *mat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newMaterial);
+ QSGDistanceFieldOutlineTextMaterial *oldMat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 136);
+
+ if (!oldMat || mat->fontScale() != oldMat->fontScale() || state.isMatrixDirty()) {
+ float dfRadius = mat->glyphCache()->distanceFieldRadius();
+ float combinedScale = m_fontScale * m_matrixScale;
+ float base = thresholdFunc(combinedScale);
+ float range = spreadFunc(combinedScale);
+ float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
+ float alphaMin = qMax(0.0f, base - range);
+ float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
+ float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
+ memcpy(buf->data() + 128, &styleAlphaMin0, 4);
+ memcpy(buf->data() + 132, &styleAlphaMin1, 4);
+ changed = true;
+ }
+
+ return changed;
+}
QSGDistanceFieldOutlineTextMaterial::QSGDistanceFieldOutlineTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
@@ -409,7 +608,10 @@ QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader() const
{
- return new DistanceFieldOutlineTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new DistanceFieldOutlineTextMaterialShader;
}
@@ -463,6 +665,49 @@ void DistanceFieldShiftedStyleTextMaterialShader::updateShift(qreal fontScale, c
program()->setUniformValue(m_shift_id, texel);
}
+class DistanceFieldShiftedStyleTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
+{
+public:
+ DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"));
+}
+
+bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldShiftedStyleTextMaterial *mat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newMaterial);
+ QSGDistanceFieldShiftedStyleTextMaterial *oldMat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 136);
+
+ if (!oldMat || oldMat->fontScale() != mat->fontScale() || oldMat->shift() != mat->shift()
+ || oldMat->textureSize() != mat->textureSize())
+ {
+ QPointF shift(1.0 / mat->fontScale() * mat->shift().x(),
+ 1.0 / mat->fontScale() * mat->shift().y());
+ memcpy(buf->data() + 128, &shift, 8);
+ changed = true;
+ }
+
+ return changed;
+}
+
QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
{
@@ -480,7 +725,10 @@ QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader() const
{
- return new DistanceFieldShiftedStyleTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new DistanceFieldShiftedStyleTextMaterialShader;
}
int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) const
@@ -491,6 +739,7 @@ int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) cons
return QSGDistanceFieldStyledTextMaterial::compare(o);
}
+
class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTextMaterialShader
{
public:
@@ -555,6 +804,75 @@ void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderStat
QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
}
+class QSGHiQSubPixelDistanceFieldTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
+{
+public:
+ QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+ setFlag(UpdatesGraphicsPipelineState, true);
+
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"));
+}
+
+bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *oldMat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 128);
+
+ if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
+ float fontScale = mat->fontScale();
+ memcpy(buf->data() + 104, &fontScale, 4);
+ changed = true;
+ }
+
+ if (!oldMat || state.isMatrixDirty()) {
+ int viewportWidth = state.viewportRect().width();
+ QMatrix4x4 mat = state.combinedMatrix().inverted();
+ QVector4D vecDelta = mat.column(0) * (qreal(2) / viewportWidth);
+ memcpy(buf->data() + 112, &vecDelta, 16);
+ }
+
+ return changed;
+}
+
+bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
+
+ ps->blendEnable = true;
+ ps->srcColor = GraphicsPipelineState::ConstantColor;
+ ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
+
+ const QVector4D color = mat->color();
+ // this is dynamic state but it's - magic! - taken care of by the renderer
+ ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), 1.0f);
+
+ return true;
+}
+
QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
@@ -563,7 +881,10 @@ QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader() const
{
- return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
}
@@ -580,6 +901,25 @@ QSGLoQSubPixelDistanceFieldTextMaterialShader::QSGLoQSubPixelDistanceFieldTextMa
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.frag"));
}
+class QSGLoQSubPixelDistanceFieldTextMaterialRhiShader : public QSGHiQSubPixelDistanceFieldTextMaterialRhiShader
+{
+public:
+ QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+};
+
+QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+ : QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"));
+}
+
QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
@@ -588,7 +928,10 @@ QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader() const
{
- return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index 7008f20925..7b6be29bcb 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -60,7 +60,8 @@ QT_BEGIN_NAMESPACE
class QSGRenderContext;
class QSGDistanceFieldTextMaterial;
-class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
+
+class QSGDistanceFieldGlyphNode : public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
{
public:
QSGDistanceFieldGlyphNode(QSGRenderContext *context);
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
index c13a0898eb..ee1ed8f337 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
@@ -52,11 +52,14 @@
//
#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include "qsgdistancefieldglyphnode_p.h"
#include "qsgadaptationlayer_p.h"
QT_BEGIN_NAMESPACE
+class QSGPlainTexture;
+
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldTextMaterial: public QSGMaterial
{
public:
@@ -82,6 +85,8 @@ public:
QSize textureSize() const { return m_size; }
bool updateTextureSize();
+ bool updateTextureSizeAndWrapper();
+ QSGTexture *wrapperTexture() const { return m_sgTexture; }
protected:
QSize m_size;
@@ -89,6 +94,7 @@ protected:
QSGDistanceFieldGlyphCache *m_glyph_cache;
const QSGDistanceFieldGlyphCache::Texture *m_texture;
qreal m_fontScale;
+ QSGPlainTexture *m_sgTexture;
};
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
index 9ad0a77318..0f9ca66ad7 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qsgdefaultdistancefieldglyphcache_p.h"
+#include "qsgopengldistancefieldglyphcache_p.h"
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qbuffer.h>
@@ -62,12 +62,12 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSIZE_GLYPHCACHE_TEXTURES)
-#if !defined(QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING)
-# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
+#if !defined(QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING)
+# define QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c,
- const QRawFont &font)
+QSGOpenGLDistanceFieldGlyphCache::QSGOpenGLDistanceFieldGlyphCache(QOpenGLContext *c,
+ const QRawFont &font)
: QSGDistanceFieldGlyphCache(font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
@@ -96,7 +96,7 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
loadPregeneratedCache(font);
}
-QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
+QSGOpenGLDistanceFieldGlyphCache::~QSGOpenGLDistanceFieldGlyphCache()
{
for (int i = 0; i < m_textures.count(); ++i)
m_funcs->glDeleteTextures(1, &m_textures[i].texture);
@@ -108,7 +108,7 @@ QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
delete m_areaAllocator;
}
-void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
{
QList<GlyphPosition> glyphPositions;
QVector<glyph_t> glyphsToRender;
@@ -119,7 +119,7 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
glyph_t glyphIndex = *it;
- int padding = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING;
+ int padding = QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING;
QRectF boundingRect = glyphData(glyphIndex).boundingRect;
int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
@@ -172,7 +172,7 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
markGlyphsToRender(glyphsToRender);
}
-void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
{
typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
typedef GlyphTextureHash::const_iterator GlyphTextureHashConstIt;
@@ -238,21 +238,22 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField>
Texture t;
t.textureId = i.key()->texture;
t.size = i.key()->size;
+ t.rhiBased = false;
setGlyphsTexture(i.value(), t);
}
}
-void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
{
m_unusedGlyphs -= glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
{
m_unusedGlyphs += glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+void QSGOpenGLDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
int width,
int height)
{
@@ -260,7 +261,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
createTexture(texInfo, width, height, zeroBuf.constData());
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+void QSGOpenGLDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
int width,
int height,
const void *pixels)
@@ -307,7 +308,7 @@ static void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
funcs->glDeleteFramebuffers(1, &id);
}
-void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
+void QSGOpenGLDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
@@ -510,7 +511,7 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int
m_vao.release();
}
-bool QSGDefaultDistanceFieldGlyphCache::useTextureResizeWorkaround() const
+bool QSGOpenGLDistanceFieldGlyphCache::useTextureResizeWorkaround() const
{
static bool set = false;
static bool useWorkaround = false;
@@ -523,7 +524,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureResizeWorkaround() const
return useWorkaround;
}
-bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const
+bool QSGOpenGLDistanceFieldGlyphCache::useTextureUploadWorkaround() const
{
static bool set = false;
static bool useWorkaround = false;
@@ -535,12 +536,12 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const
return useWorkaround;
}
-bool QSGDefaultDistanceFieldGlyphCache::createFullSizeTextures() const
+bool QSGOpenGLDistanceFieldGlyphCache::createFullSizeTextures() const
{
return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
}
-int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
+int QSGOpenGLDistanceFieldGlyphCache::maxTextureSize() const
{
if (!m_maxTextureSize)
m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
@@ -599,7 +600,7 @@ namespace {
};
}
-bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
{
// The pregenerated data must be loaded first, otherwise the area allocator
// will be wrong
@@ -669,11 +670,11 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo
m_maxTextureSize);
}
- if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ if (padding != QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
qPrintable(font.familyName()),
padding,
- QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING);
}
m_referenceFont.setPixelSize(pixelSize);
@@ -784,6 +785,7 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo
Texture t;
t.textureId = texInfo->texture;
t.size = texInfo->size;
+ t.rhiBased = false;
setGlyphsTexture(glyphs, t);
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h
index c64adddd91..99cfe27b9c 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
-#define QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+#ifndef QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
+#define QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
//
// W A R N I N G
@@ -66,11 +66,11 @@ class QOpenGLSharedResourceGuard;
class QOpenGLFunctions_3_2_Core;
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+class Q_QUICK_PRIVATE_EXPORT QSGOpenGLDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
- QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
- virtual ~QSGDefaultDistanceFieldGlyphCache();
+ QSGOpenGLDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
+ virtual ~QSGOpenGLDistanceFieldGlyphCache();
void requestGlyphs(const QSet<glyph_t> &glyphs) override;
void storeGlyphs(const QList<QDistanceField> &glyphs) override;
@@ -85,6 +85,7 @@ public:
void setMaxTextureCount(int max) { m_maxTextureCount = max; }
int maxTextureCount() const { return m_maxTextureCount; }
+ bool eightBitFormatIsAlphaSwizzled() const override { return !m_coreProfile; }
private:
bool loadPregeneratedCache(const QRawFont &font);
@@ -159,4 +160,4 @@ private:
QT_END_NAMESPACE
-#endif // QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+#endif // QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgopengllayer.cpp
index b2b123912f..8db4cba58a 100644
--- a/src/quick/scenegraph/qsgdefaultlayer.cpp
+++ b/src/quick/scenegraph/qsgopengllayer.cpp
@@ -36,7 +36,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include "qsgdefaultlayer_p.h"
+#include "qsgopengllayer_p.h"
#include <private/qqmlglobal_p.h>
#include <private/qsgrenderer_p.h>
@@ -88,8 +88,8 @@ namespace
}
}
-QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
- : QSGLayer()
+QSGOpenGLLayer::QSGOpenGLLayer(QSGRenderContext *context)
+ : QSGLayer(*(new QSGOpenGLLayerPrivate))
, m_item(nullptr)
, m_device_pixel_ratio(1)
, m_format(GL_RGBA)
@@ -114,12 +114,12 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
m_context = static_cast<QSGDefaultRenderContext *>(context);
}
-QSGDefaultLayer::~QSGDefaultLayer()
+QSGOpenGLLayer::~QSGOpenGLLayer()
{
invalidated();
}
-void QSGDefaultLayer::invalidated()
+void QSGOpenGLLayer::invalidated()
{
delete m_renderer;
m_renderer = nullptr;
@@ -136,23 +136,29 @@ void QSGDefaultLayer::invalidated()
}
}
-int QSGDefaultLayer::textureId() const
+int QSGOpenGLLayer::textureId() const
{
return m_fbo ? m_fbo->texture() : 0;
}
-bool QSGDefaultLayer::hasAlphaChannel() const
+int QSGOpenGLLayerPrivate::comparisonKey() const
+{
+ Q_Q(const QSGOpenGLLayer);
+ return q->m_fbo ? q->m_fbo->texture() : 0;
+}
+
+bool QSGOpenGLLayer::hasAlphaChannel() const
{
return m_format != GL_RGB;
}
-bool QSGDefaultLayer::hasMipmaps() const
+bool QSGOpenGLLayer::hasMipmaps() const
{
return m_mipmap;
}
-void QSGDefaultLayer::bind()
+void QSGOpenGLLayer::bind()
{
#ifndef QT_NO_DEBUG
if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
@@ -174,7 +180,7 @@ void QSGDefaultLayer::bind()
}
}
-bool QSGDefaultLayer::updateTexture()
+bool QSGOpenGLLayer::updateTexture()
{
bool doGrab = (m_live || m_grab) && m_dirtyTexture;
if (doGrab)
@@ -185,7 +191,7 @@ bool QSGDefaultLayer::updateTexture()
return doGrab;
}
-void QSGDefaultLayer::setHasMipmaps(bool mipmap)
+void QSGOpenGLLayer::setHasMipmaps(bool mipmap)
{
if (mipmap == m_mipmap)
return;
@@ -195,7 +201,7 @@ void QSGDefaultLayer::setHasMipmaps(bool mipmap)
}
-void QSGDefaultLayer::setItem(QSGNode *item)
+void QSGOpenGLLayer::setItem(QSGNode *item)
{
if (item == m_item)
return;
@@ -211,7 +217,7 @@ void QSGDefaultLayer::setItem(QSGNode *item)
markDirtyTexture();
}
-void QSGDefaultLayer::setRect(const QRectF &rect)
+void QSGOpenGLLayer::setRect(const QRectF &rect)
{
if (rect == m_rect)
return;
@@ -219,7 +225,7 @@ void QSGDefaultLayer::setRect(const QRectF &rect)
markDirtyTexture();
}
-void QSGDefaultLayer::setSize(const QSize &size)
+void QSGOpenGLLayer::setSize(const QSize &size)
{
if (size == m_size)
return;
@@ -235,7 +241,7 @@ void QSGDefaultLayer::setSize(const QSize &size)
markDirtyTexture();
}
-void QSGDefaultLayer::setFormat(GLenum format)
+void QSGOpenGLLayer::setFormat(GLenum format)
{
if (format == m_format)
return;
@@ -243,7 +249,7 @@ void QSGDefaultLayer::setFormat(GLenum format)
markDirtyTexture();
}
-void QSGDefaultLayer::setLive(bool live)
+void QSGOpenGLLayer::setLive(bool live)
{
if (live == m_live)
return;
@@ -259,7 +265,7 @@ void QSGDefaultLayer::setLive(bool live)
markDirtyTexture();
}
-void QSGDefaultLayer::scheduleUpdate()
+void QSGOpenGLLayer::scheduleUpdate()
{
if (m_grab)
return;
@@ -268,29 +274,29 @@ void QSGDefaultLayer::scheduleUpdate()
emit updateRequested();
}
-void QSGDefaultLayer::setRecursive(bool recursive)
+void QSGOpenGLLayer::setRecursive(bool recursive)
{
m_recursive = recursive;
}
-void QSGDefaultLayer::setMirrorHorizontal(bool mirror)
+void QSGOpenGLLayer::setMirrorHorizontal(bool mirror)
{
m_mirrorHorizontal = mirror;
}
-void QSGDefaultLayer::setMirrorVertical(bool mirror)
+void QSGOpenGLLayer::setMirrorVertical(bool mirror)
{
m_mirrorVertical = mirror;
}
-void QSGDefaultLayer::markDirtyTexture()
+void QSGOpenGLLayer::markDirtyTexture()
{
m_dirtyTexture = true;
if (m_live || m_grab)
emit updateRequested();
}
-void QSGDefaultLayer::grab()
+void QSGOpenGLLayer::grab()
{
if (!m_item || m_size.isNull()) {
delete m_fbo;
@@ -402,7 +408,7 @@ void QSGDefaultLayer::grab()
m_mirrorVertical ? m_rect.bottom() : m_rect.top(),
m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
m_mirrorVertical ? -m_rect.height() : m_rect.height());
- m_renderer->setProjectionMatrixToRect(mirrored);
+ m_renderer->setProjectionMatrixToRect(mirrored, false);
m_renderer->setClearColor(Qt::transparent);
if (m_multisampling) {
@@ -457,7 +463,7 @@ void QSGDefaultLayer::grab()
markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
}
-QImage QSGDefaultLayer::toImage() const
+QImage QSGOpenGLLayer::toImage() const
{
if (m_fbo)
return m_fbo->toImage();
@@ -465,7 +471,7 @@ QImage QSGDefaultLayer::toImage() const
return QImage();
}
-QRectF QSGDefaultLayer::normalizedTextureSubRect() const
+QRectF QSGOpenGLLayer::normalizedTextureSubRect() const
{
return QRectF(m_mirrorHorizontal ? 1 : 0,
m_mirrorVertical ? 0 : 1,
@@ -473,4 +479,4 @@ QRectF QSGDefaultLayer::normalizedTextureSubRect() const
m_mirrorVertical ? 1 : -1);
}
-#include "moc_qsgdefaultlayer_p.cpp"
+#include "moc_qsgopengllayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgopengllayer_p.h
index 06355e0c21..c6246843e2 100644
--- a/src/quick/scenegraph/qsgdefaultlayer_p.h
+++ b/src/quick/scenegraph/qsgopengllayer_p.h
@@ -36,8 +36,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QSGDEFAULTLAYER_P_H
-#define QSGDEFAULTLAYER_P_H
+#ifndef QSGOPENGLLAYER_P_H
+#define QSGOPENGLLAYER_P_H
//
// W A R N I N G
@@ -52,6 +52,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include <qsgsimplerectnode.h>
QT_BEGIN_NAMESPACE
@@ -61,13 +62,15 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QSGDepthStencilBuffer;
class QSGDefaultRenderContext;
+class QSGOpenGLLayerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultLayer : public QSGLayer
+class Q_QUICK_PRIVATE_EXPORT QSGOpenGLLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGOpenGLLayer)
Q_OBJECT
public:
- QSGDefaultLayer(QSGRenderContext *context);
- ~QSGDefaultLayer();
+ QSGOpenGLLayer(QSGRenderContext *context);
+ ~QSGOpenGLLayer();
bool updateTexture() override;
@@ -154,6 +157,13 @@ private:
uint m_mirrorVertical : 1;
};
+class QSGOpenGLLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGOpenGLLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
-#endif // QSGDEFAULTLAYER_P_H
+#endif // QSGOPENGLLAYER_P_H
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 2e91bafa7c..d38b5649c7 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -40,6 +40,7 @@
#include "qsgrenderloop_p.h"
#include "qsgthreadedrenderloop_p.h"
#include "qsgwindowsrenderloop_p.h"
+#include "qsgrhisupport_p.h"
#include <private/qquickanimatorcontroller_p.h>
#include <QtCore/QCoreApplication>
@@ -50,6 +51,7 @@
#include <QtGui/QOffscreenSurface>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#include <QPlatformSurfaceEvent>
#include <QtQml/private/qqmlglobal_p.h>
@@ -59,23 +61,35 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <private/qquickprofiler_p.h>
+#include <private/qsgrhishadereffectnode_p.h>
+
#if QT_CONFIG(opengl)
-# include <QtGui/QOpenGLContext>
-# include <private/qsgdefaultrendercontext_p.h>
+#include <QtGui/QOpenGLContext>
#if QT_CONFIG(quick_shadereffect)
-# include <private/qquickopenglshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
#endif
+#include <private/qsgdefaultrendercontext_p.h>
#endif
#ifdef Q_OS_WIN
-# include <QtCore/qt_windows.h>
+#include <QtCore/qt_windows.h>
#endif
QT_BEGIN_NAMESPACE
extern bool qsg_useConsistentTiming();
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-#if QT_CONFIG(opengl)
+
+// ### We do not yet support using Qt Quick with QRhi (and Vulkan, D3D
+// or Metal) in -no-opengl builds as of Qt 5.14. This is due to to the
+// widespread direct OpenGL usage all over the place in qsgdefault*
+// and the related classes. To be cleaned up in Qt 6 when the direct
+// GL code path is removed.
+
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+
+#define ENABLE_DEFAULT_BACKEND
+
/*
expectations for this manager to work:
- one opengl context to render multiple windows
@@ -88,17 +102,13 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
#endif
+
QSGRenderLoop *QSGRenderLoop::s_instance = nullptr;
QSGRenderLoop::~QSGRenderLoop()
{
}
-QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
-{
- return QSurface::OpenGLSurface;
-}
-
void QSGRenderLoop::cleanup()
{
if (!s_instance)
@@ -112,18 +122,36 @@ void QSGRenderLoop::cleanup()
}
delete s_instance;
s_instance = nullptr;
+
+#ifdef ENABLE_DEFAULT_BACKEND
+ QSGRhiSupport::instance()->cleanup();
+ QSGRhiProfileConnection::instance()->cleanup();
+#endif
+}
+
+QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
+{
+#ifdef ENABLE_DEFAULT_BACKEND
+ return QSGRhiSupport::instance()->windowSurfaceType();
+#else
+ return QSurface::RasterSurface;
+#endif
}
-/*!
- * Non-threaded render loops immediately run the job if there is a context.
- */
void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
Q_ASSERT(job);
-#if QT_CONFIG(opengl)
+#ifdef ENABLE_DEFAULT_BACKEND
Q_ASSERT(window);
- if (window->openglContext()) {
- window->openglContext()->makeCurrent(window);
+ if (!QSGRhiSupport::instance()->isRhiEnabled()) {
+ if (window->openglContext()) {
+ window->openglContext()->makeCurrent(window);
+ job->run();
+ }
+ } else {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (cd->rhi)
+ cd->rhi->makeThreadLocalNativeContextCurrent();
job->run();
}
#else
@@ -132,7 +160,8 @@ void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
#endif
delete job;
}
-#if QT_CONFIG(opengl)
+
+#ifdef ENABLE_DEFAULT_BACKEND
class QSGGuiThreadRenderLoop : public QSGRenderLoop
{
Q_OBJECT
@@ -160,6 +189,10 @@ public:
QSGContext *sceneGraphContext() const override;
QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; }
+ void releaseSwapchain(QQuickWindow *window);
+
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
struct WindowData {
bool updatePending : 1;
bool grabOnly : 1;
@@ -167,13 +200,16 @@ public:
QHash<QQuickWindow *, WindowData> m_windows;
- QOpenGLContext *gl;
+ QOpenGLContext *gl = nullptr;
+ QOffscreenSurface *offscreenSurface = nullptr;
+ QRhi *rhi = nullptr;
QSGContext *sg;
QSGRenderContext *rc;
QImage grabContent;
};
#endif
+
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
@@ -183,28 +219,64 @@ QSGRenderLoop *QSGRenderLoop::instance()
const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
s_instance = QSGContext::createWindowManager();
-#if QT_CONFIG(opengl)
+#ifdef ENABLE_DEFAULT_BACKEND
if (!s_instance) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
- enum RenderLoopType {
- BasicRenderLoop,
- ThreadedRenderLoop,
- WindowsRenderLoop
- };
-
- RenderLoopType loopType = BasicRenderLoop;
-
-#ifdef Q_OS_WIN
- // With desktop OpenGL (opengl32.dll), use threaded. Otherwise (ANGLE) use windows.
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
- && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ QSGRenderLoopType loopType;
+ if (rhiSupport->isRhiEnabled() && rhiSupport->rhiBackend() != QRhi::OpenGLES2) {
loopType = ThreadedRenderLoop;
- else
- loopType = WindowsRenderLoop;
+ } else {
+ loopType = BasicRenderLoop;
+#ifdef Q_OS_WIN
+ // With desktop OpenGL (opengl32.dll), use threaded. Otherwise (ANGLE) use windows.
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
+ && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ {
+ loopType = ThreadedRenderLoop;
+ } else {
+ loopType = WindowsRenderLoop;
+ }
#else
- if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
- loopType = ThreadedRenderLoop;
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ loopType = ThreadedRenderLoop;
+#endif
+ }
+
+ if (rhiSupport->isRhiEnabled()) {
+ // no 'windows' because that's not yet ported to the rhi
+ if (loopType == WindowsRenderLoop)
+ loopType = BasicRenderLoop;
+
+ switch (rhiSupport->rhiBackend()) {
+ case QRhi::Vulkan:
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
+ // ### to be investigated (Mesa/Gnome deadlocks on
+ // resize with threaded+Vulkan (but not threaded+GL))
+ loopType = BasicRenderLoop;
#endif
+ break;
+
+ case QRhi::D3D11:
+ // D3D11 is forced to 'basic' always for now. The threaded loop's model may
+ // not be suitable for DXGI due to the possibility of having the main
+ // thread blocked while issuing a Present. To be investigated.
+ loopType = BasicRenderLoop;
+ break;
+
+ case QRhi::Null:
+ loopType = BasicRenderLoop;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // The environment variables can always override. This is good
+ // because in some situations it makes perfect sense to try out a
+ // render loop that is otherwise disabled by default.
+
if (qmlNoThreadedRenderer())
loopType = BasicRenderLoop;
else if (qmlForceThreadedRenderer())
@@ -275,14 +347,15 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window,
if (!signalEmitted)
qFatal("%s", qPrintable(untranslatedMessage));
}
-#if QT_CONFIG(opengl)
+
+#ifdef ENABLE_DEFAULT_BACKEND
QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
- : gl(nullptr)
{
if (qsg_useConsistentTiming()) {
QUnifiedTimer::instance(true)->setConsistentTiming(true);
qCDebug(QSG_LOG_INFO, "using fixed animation steps");
}
+
sg = QSGContext::createDefaultContext();
rc = sg->createRenderContext();
}
@@ -318,38 +391,88 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool current = false;
- QScopedPointer<QOffscreenSurface> offscreenSurface;
if (gl) {
QSurface *surface = window;
// There may be no platform window if the window got closed.
- if (!window->handle()) {
- offscreenSurface.reset(new QOffscreenSurface);
- offscreenSurface->setFormat(gl->format());
- offscreenSurface->create();
- surface = offscreenSurface.data();
+ if (!window->handle())
+ surface = offscreenSurface;
+ if (rhi) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ current = true;
+ } else {
+ current = gl->makeCurrent(surface);
}
- current = gl->makeCurrent(surface);
}
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context");
-#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
- if (current)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
#endif
+ if (d->swapchain) {
+ if (window->handle()) {
+ // We get here when exiting via QCoreApplication::quit() instead of
+ // through QWindow::close().
+ releaseSwapchain(window);
+ } else {
+ qWarning("QSGGuiThreadRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
+ window, d->swapchain);
+ }
+ }
+
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
rc->invalidate();
+ d->rhi = nullptr;
+ delete rhi;
+ rhi = nullptr;
delete gl;
gl = nullptr;
+ delete offscreenSurface;
+ offscreenSurface = nullptr;
} else if (gl && window == gl->surface() && current) {
- gl->doneCurrent();
+ if (!rhi)
+ gl->doneCurrent();
}
delete d->animationController;
}
+void QSGGuiThreadRenderLoop::releaseSwapchain(QQuickWindow *window)
+{
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ delete wd->rpDescForSwapchain;
+ wd->rpDescForSwapchain = nullptr;
+ delete wd->swapchain;
+ wd->swapchain = nullptr;
+ delete wd->depthStencilForSwapchain;
+ wd->depthStencilForSwapchain = nullptr;
+ wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable = false;
+}
+
+bool QSGGuiThreadRenderLoop::eventFilter(QObject *watched, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ QQuickWindow *w = qobject_cast<QQuickWindow *>(watched);
+ if (w) {
+ releaseSwapchain(w);
+ w->removeEventFilter(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(watched, event);
+}
+
void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
{
if (!m_windows.contains(window))
@@ -364,8 +487,36 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
return;
bool current = false;
-
- if (!gl) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ int rhiSampleCount = 1;
+ const bool enableRhi = rhiSupport->isRhiEnabled();
+
+ if (enableRhi && !rhi) {
+ offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+ if (rhi) {
+ if (rhiSupport->isProfilingRequested())
+ QSGRhiProfileConnection::instance()->initialize(rhi);
+
+ current = true;
+ rhi->makeThreadLocalNativeContextCurrent();
+
+ // The sample count cannot vary between windows as we use the same
+ // rendercontext for all of them. Decide it here and now.
+ rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
+
+ cd->rhi = rhi; // set this early in case something hooked up to rc initialized() accesses it
+
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.rhi = rhi;
+ rcParams.sampleCount = rhiSampleCount;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ cd->context->initialize(&rcParams);
+ } else {
+ handleContextCreationFailure(window, false);
+ }
+ } else if (!enableRhi && !gl) {
gl = new QOpenGLContext();
gl->setFormat(window->requestedFormat());
gl->setScreen(window->screen());
@@ -377,15 +528,57 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
gl = nullptr;
handleContextCreationFailure(window, isEs);
} else {
+ if (!offscreenSurface) {
+ offscreenSurface = new QOffscreenSurface;
+ offscreenSurface->setFormat(gl->format());
+ offscreenSurface->create();
+ }
cd->fireOpenGLContextCreated(gl);
current = gl->makeCurrent(window);
}
if (current) {
- auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(cd->context);
- openglRenderContext->initialize(gl);
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ cd->context->initialize(&rcParams);
}
} else {
- current = gl->makeCurrent(window);
+ if (rhi) {
+ current = true;
+ // With the rhi making the (OpenGL) context current serves only one
+ // purpose: to enable external OpenGL rendering connected to one of
+ // the QQuickWindow signals (beforeSynchronizing, beforeRendering,
+ // etc.) to function like it did on the direct OpenGL path. For our
+ // own rendering this call would not be necessary.
+ rhi->makeThreadLocalNativeContextCurrent();
+ } else {
+ current = gl->makeCurrent(window);
+ }
+ }
+
+ if (enableRhi && rhi && !cd->swapchain) {
+ // if it's not the first window then the rhi is not yet stored to
+ // QQuickWindowPrivate, do it now
+ cd->rhi = rhi;
+
+ QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
+
+ cd->swapchain = rhi->newSwapChain();
+ cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(),
+ rhiSampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ cd->swapchain->setWindow(window);
+ cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
+ qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ cd->swapchain->setSampleCount(rhiSampleCount);
+ cd->swapchain->setFlags(flags);
+ cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
+ cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
+
+ window->installEventFilter(this);
}
bool lastDirtyWindow = true;
@@ -407,6 +600,17 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!m_windows.contains(window))
return;
}
+
+ QSize effectiveOutputSize; // always prefer what the surface tells us, not the QWindow
+ if (cd->swapchain) {
+ effectiveOutputSize = cd->swapchain->surfacePixelSize();
+ // An update request could still be delivered right before we get an
+ // unexpose. With Vulkan on Windows for example attempting to render
+ // leads to failures at this stage since the surface size is already 0.
+ if (effectiveOutputSize.isEmpty())
+ return;
+ }
+
QElapsedTimer renderTimer;
qint64 renderTime = 0, syncTime = 0, polishTime = 0;
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
@@ -424,6 +628,41 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
emit window->afterAnimating();
+ // Begin the frame before syncing -> sync is where we may invoke
+ // updatePaintNode() on the items and they may want to do resource updates.
+ // Also relevant for applications that connect to the before/afterSynchronizing
+ // signals and want to do graphics stuff already there.
+ if (cd->swapchain) {
+ Q_ASSERT(!effectiveOutputSize.isEmpty());
+ const QSize previousOutputSize = cd->swapchain->currentPixelSize();
+ if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
+ if (cd->swapchainJustBecameRenderable)
+ qDebug("just became exposed");
+ cd->swapchainJustBecameRenderable = false;
+ cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
+
+ cd->depthStencilForSwapchain->build();
+ cd->hasActiveSwapchain = cd->swapchain->buildOrResize();
+
+ cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
+ if (!cd->hasActiveSwapchain)
+ qWarning("Failed to build or resize swapchain");
+ else
+ qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ }
+
+ Q_ASSERT(rhi == cd->rhi);
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ qWarning("Device lost");
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to start frame");
+ // out of date is not worth warning about - it may happen even during resizing on some platforms
+ return;
+ }
+ }
+
cd->syncSceneGraph();
if (lastDirtyWindow)
rc->endSync();
@@ -433,7 +672,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
- cd->renderSceneGraph(window->size());
+ cd->renderSceneGraph(window->size(), effectiveOutputSize);
if (profileFrames)
renderTime = renderTimer.nsecsElapsed();
@@ -441,17 +680,27 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
QQuickProfiler::SceneGraphRenderLoopRender);
if (data.grabOnly) {
- bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255;
- grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha);
+ const bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255;
+ if (cd->swapchain)
+ grabContent = rhiSupport->grabAndBlockInCurrentFrame(rhi, cd->swapchain);
+ else
+ grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha);
grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio());
data.grabOnly = false;
}
- if (alsoSwap && window->isVisible()) {
+ const bool needsPresent = alsoSwap && window->isVisible();
+ if (cd->swapchain) {
+ QRhi::EndFrameFlags flags = 0;
+ if (!needsPresent)
+ flags |= QRhi::SkipPresent;
+ rhi->endFrame(cd->swapchain, flags);
+ } else if (needsPresent) {
if (!cd->customRenderStage || !cd->customRenderStage->swap())
gl->swapBuffers(window);
- cd->fireFrameSwapped();
}
+ if (needsPresent)
+ cd->fireFrameSwapped();
qint64 swapTime = 0;
if (profileFrames)
@@ -472,6 +721,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
lastFrameTime = QTime::currentTime();
}
+ QSGRhiProfileConnection::instance()->send(rhi);
+
// Might have been set during syncSceneGraph()
if (data.updatePending)
maybeUpdate(window);
@@ -479,7 +730,26 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window)
{
- if (window->isExposed()) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+
+ // This is tricker than used to be. We want to detect having an empty
+ // surface size (which may be the case even when window->size() is
+ // non-empty, on some platforms with some graphics APIs!) as well as the
+ // case when the window just became "newly exposed" (e.g. after a
+ // minimize-restore on Windows, or when switching between fully obscured -
+ // not fully obscured on macOS)
+
+ if (!window->isExposed() || (wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()))
+ wd->hasRenderableSwapchain = false;
+
+ if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
+ && !wd->swapchain->surfacePixelSize().isEmpty())
+ {
+ wd->hasRenderableSwapchain = true;
+ wd->swapchainJustBecameRenderable = true;
+ }
+
+ if (window->isExposed() && (!rhi || !wd->hasActiveSwapchain || wd->hasRenderableSwapchain)) {
m_windows[window].updatePending = true;
renderWindow(window);
}
@@ -527,7 +797,7 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window)
renderWindow(window);
}
-#endif
+#endif // ENABLE_DEFAULT_BACKEND
#include "qsgrenderloop.moc"
#include "moc_qsgrenderloop_p.cpp"
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 7bf40ecac4..02d0b84de1 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGRenderLoop_P_H
-#define QSGRenderLoop_P_H
+#ifndef QSGRENDERLOOP_P_H
+#define QSGRENDERLOOP_P_H
//
// W A R N I N G
@@ -124,6 +124,13 @@ private:
QSet<QQuickWindow *> m_windows;
};
+enum QSGRenderLoopType
+{
+ BasicRenderLoop,
+ ThreadedRenderLoop,
+ WindowsRenderLoop
+};
+
QT_END_NAMESPACE
-#endif // QSGRenderLoop_P_H
+#endif // QSGRENDERLOOP_P_H
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
new file mode 100644
index 0000000000..28fc10e413
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -0,0 +1,568 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhidistancefieldglyphcache_p.h"
+#include "qsgcontext_p.h"
+#include <QtGui/private/qdistancefield_p.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtQml/private/qqmlglobal_p.h>
+#include <qmath.h>
+#include <qendian.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
+DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSIZE_GLYPHCACHE_TEXTURES)
+
+#if !defined(QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING)
+# define QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
+#endif
+
+QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QRhi *rhi, const QRawFont &font)
+ : QSGDistanceFieldGlyphCache(font)
+ , m_rhi(rhi)
+{
+ // Load a pregenerated cache if the font contains one
+ loadPregeneratedCache(font);
+}
+
+QSGRhiDistanceFieldGlyphCache::~QSGRhiDistanceFieldGlyphCache()
+{
+ for (int i = 0; i < m_textures.count(); ++i)
+ delete m_textures[i].texture;
+
+ delete m_areaAllocator;
+
+ // should be empty, but just in case
+ qDeleteAll(m_pendingDispose);
+}
+
+void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+{
+ QList<GlyphPosition> glyphPositions;
+ QVector<glyph_t> glyphsToRender;
+
+ if (m_areaAllocator == nullptr)
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+
+ for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
+ glyph_t glyphIndex = *it;
+
+ int padding = QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING;
+ QRectF boundingRect = glyphData(glyphIndex).boundingRect;
+ int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
+ int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
+ QRect alloc = m_areaAllocator->allocate(glyphSize);
+
+ if (alloc.isNull()) {
+ // Unallocate unused glyphs until we can allocated the new glyph
+ while (alloc.isNull() && !m_unusedGlyphs.isEmpty()) {
+ glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
+
+ TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
+ QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2;
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2;
+ m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
+ unusedCoord.y - padding,
+ padding * 2 + unusedGlyphWidth,
+ padding * 2 + unusedGlyphHeight));
+
+ m_unusedGlyphs.remove(unusedGlyph);
+ m_glyphsTexture.remove(unusedGlyph);
+ removeGlyph(unusedGlyph);
+
+ alloc = m_areaAllocator->allocate(glyphSize);
+ }
+
+ // Not enough space left for this glyph... skip to the next one
+ if (alloc.isNull())
+ continue;
+ }
+
+ TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
+ alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
+
+ tex->allocatedArea |= alloc;
+ Q_ASSERT(tex->padding == padding || tex->padding < 0);
+ tex->padding = padding;
+
+ GlyphPosition p;
+ p.glyph = glyphIndex;
+ p.position = alloc.topLeft() + QPoint(padding, padding);
+
+ glyphPositions.append(p);
+ glyphsToRender.append(glyphIndex);
+ m_glyphsTexture.insert(glyphIndex, tex);
+ }
+
+ setGlyphsPosition(glyphPositions);
+ markGlyphsToRender(glyphsToRender);
+}
+
+void QSGRhiDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
+{
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+ typedef GlyphTextureHash::const_iterator GlyphTextureHashConstIt;
+
+ GlyphTextureHash glyphTextures;
+
+ QVarLengthArray<QRhiTextureUploadEntry, 32> uploads;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ QDistanceField glyph = glyphs.at(i);
+ glyph_t glyphIndex = glyph.glyph();
+ TexCoord c = glyphTexCoord(glyphIndex);
+ TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex);
+
+ resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
+
+ glyphTextures[texInfo].append(glyphIndex);
+
+ int padding = texInfo->padding;
+ int expectedWidth = qCeil(c.width + c.xMargin * 2);
+ glyph = glyph.copy(-padding, -padding,
+ expectedWidth + padding * 2, glyph.height() + padding * 2);
+
+ if (useTextureResizeWorkaround()) {
+ uchar *inBits = glyph.scanLine(0);
+ uchar *outBits = texInfo->image.scanLine(int(c.y) - padding) + int(c.x) - padding;
+ for (int y = 0; y < glyph.height(); ++y) {
+ memcpy(outBits, inBits, glyph.width());
+ inBits += glyph.width();
+ outBits += texInfo->image.width();
+ }
+ }
+
+ QRhiTextureSubresourceUploadDescription subresDesc(glyph.constBits(), glyph.width() * glyph.height());
+ subresDesc.setSourceSize(QSize(glyph.width(), glyph.height()));
+ subresDesc.setDestinationTopLeft(QPoint(c.x - padding, c.y - padding));
+ texInfo->uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ for (int i = 0; i < glyphs.size(); ++i) {
+ TextureInfo *texInfo = m_glyphsTexture.value(glyphs.at(i).glyph());
+ if (!texInfo->uploads.isEmpty()) {
+ m_resourceUpdates->uploadTexture(texInfo->texture, texInfo->uploads);
+ texInfo->uploads.clear();
+ }
+ }
+
+ for (GlyphTextureHashConstIt i = glyphTextures.constBegin(), cend = glyphTextures.constEnd(); i != cend; ++i) {
+ Texture t;
+ t.texture = i.key()->texture;
+ t.size = i.key()->size;
+ t.rhiBased = true;
+ setGlyphsTexture(i.value(), t);
+ }
+}
+
+void QSGRhiDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs -= glyphs;
+}
+
+void QSGRhiDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs += glyphs;
+}
+
+void QSGRhiDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height)
+{
+ QByteArray zeroBuf(width * height, 0);
+ createTexture(texInfo, width, height, zeroBuf.constData());
+}
+
+void QSGRhiDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height,
+ const void *pixels)
+{
+ if (useTextureResizeWorkaround() && texInfo->image.isNull()) {
+ texInfo->image = QDistanceField(width, height);
+ memcpy(texInfo->image.bits(), pixels, width * height);
+ }
+
+ texInfo->texture = m_rhi->newTexture(QRhiTexture::RED_OR_ALPHA8, QSize(width, height), 1, QRhiTexture::UsedAsTransferSource);
+ if (texInfo->texture->build()) {
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ QRhiTextureSubresourceUploadDescription subresDesc(pixels, width * height);
+ subresDesc.setSourceSize(QSize(width, height));
+ m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ } else {
+ qWarning("Failed to create distance field glyph cache");
+ }
+
+ texInfo->size = QSize(width, height);
+}
+
+void QSGRhiDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
+{
+ int oldWidth = texInfo->size.width();
+ int oldHeight = texInfo->size.height();
+ if (width == oldWidth && height == oldHeight)
+ return;
+
+ QRhiTexture *oldTexture = texInfo->texture;
+ createTexture(texInfo, width, height);
+
+ if (!oldTexture)
+ return;
+
+ updateRhiTexture(oldTexture, texInfo->texture, texInfo->size);
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ if (useTextureResizeWorkaround()) {
+ QRhiTextureSubresourceUploadDescription subresDesc(texInfo->image.constBits(),
+ oldWidth * oldHeight);
+ subresDesc.setSourceSize(QSize(oldWidth, oldHeight));
+ m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ texInfo->image = texInfo->image.copy(0, 0, width, height);
+ } else {
+ m_resourceUpdates->copyTexture(texInfo->texture, oldTexture);
+ }
+
+ m_pendingDispose.insert(oldTexture);
+}
+
+bool QSGRhiDistanceFieldGlyphCache::useTextureResizeWorkaround() const
+{
+ static bool set = false;
+ static bool useWorkaround = false;
+ if (!set) {
+ useWorkaround = m_rhi->backend() == QRhi::OpenGLES2 || qmlUseGlyphCacheWorkaround();
+ set = true;
+ }
+ return useWorkaround;
+}
+
+bool QSGRhiDistanceFieldGlyphCache::createFullSizeTextures() const
+{
+ return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
+}
+
+int QSGRhiDistanceFieldGlyphCache::maxTextureSize() const
+{
+ if (!m_maxTextureSize)
+ m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ return m_maxTextureSize;
+}
+
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ static QElapsedTimer timer;
+
+ bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled();
+ if (profile)
+ timer.start();
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize);
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ int systemMaxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+
+ if (m_maxTextureSize > systemMaxTextureSize) {
+ qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d",
+ systemMaxTextureSize,
+ qPrintable(font.familyName()),
+ m_maxTextureSize);
+ }
+
+ if (padding != QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(glyphCount);
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX));
+ tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY));
+ tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth));
+ tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(((qreal)value)/(qreal)65536)
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ createTexture(texInfo, width, height, textureData);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.texture = texInfo->texture;
+ t.size = texInfo->size;
+ t.rhiBased = true;
+
+ setGlyphsTexture(glyphs, t);
+
+ textureData += size;
+ }
+ }
+
+ if (profile) {
+ quint64 now = timer.elapsed();
+ qCDebug(QSG_LOG_TIME_GLYPH,
+ "distancefield: %d pre-generated glyphs loaded in %dms",
+ m_unusedGlyphs.size(),
+ (int) now);
+ }
+
+ return true;
+}
+
+void QSGRhiDistanceFieldGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
+{
+ if (m_resourceUpdates) {
+ mergeInto->merge(m_resourceUpdates);
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+
+ // now let's assume the resource updates will be committed in this frame
+ for (QRhiTexture *t : m_pendingDispose)
+ t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe
+
+ m_pendingDispose.clear();
+}
+
+bool QSGRhiDistanceFieldGlyphCache::eightBitFormatIsAlphaSwizzled() const
+{
+ // return true when the shaders for 8-bit formats need .a instead of .r
+ // when sampling the texture
+ return !m_rhi->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
new file mode 100644
index 0000000000..ddd3d92ba7
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRHIDISTANCEFIELDGLYPHCACHE_H
+#define QSGRHIDISTANCEFIELDGLYPHCACHE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgadaptationlayer_p.h"
+#include <private/qsgareaallocator_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGRhiDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+{
+public:
+ QSGRhiDistanceFieldGlyphCache(QRhi *rhi, const QRawFont &font);
+ virtual ~QSGRhiDistanceFieldGlyphCache();
+
+ void requestGlyphs(const QSet<glyph_t> &glyphs) override;
+ void storeGlyphs(const QList<QDistanceField> &glyphs) override;
+ void referenceGlyphs(const QSet<glyph_t> &glyphs) override;
+ void releaseGlyphs(const QSet<glyph_t> &glyphs) override;
+
+ bool useTextureResizeWorkaround() const;
+ bool createFullSizeTextures() const;
+ int maxTextureSize() const;
+
+ void setMaxTextureCount(int max) { m_maxTextureCount = max; }
+ int maxTextureCount() const { return m_maxTextureCount; }
+
+ void commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto);
+
+ bool eightBitFormatIsAlphaSwizzled() const override;
+
+private:
+ bool loadPregeneratedCache(const QRawFont &font);
+
+ struct TextureInfo {
+ QRhiTexture *texture;
+ QSize size;
+ QRect allocatedArea;
+ QDistanceField image;
+ int padding = -1;
+ QVector<QRhiTextureUploadEntry> uploads;
+
+ TextureInfo(const QRect &preallocRect = QRect()) : texture(nullptr), allocatedArea(preallocRect) { }
+ };
+
+ void createTexture(TextureInfo *texInfo, int width, int height, const void *pixels);
+ void createTexture(TextureInfo *texInfo, int width, int height);
+ void resizeTexture(TextureInfo *texInfo, int width, int height);
+
+ TextureInfo *textureInfo(int index)
+ {
+ for (int i = m_textures.count(); i <= index; ++i) {
+ if (createFullSizeTextures())
+ m_textures.append(QRect(0, 0, maxTextureSize(), maxTextureSize()));
+ else
+ m_textures.append(TextureInfo());
+ }
+
+ return &m_textures[index];
+ }
+
+ QRhi *m_rhi;
+ mutable int m_maxTextureSize = 0;
+ int m_maxTextureCount = 3;
+ QSGAreaAllocator *m_areaAllocator = nullptr;
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ QList<TextureInfo> m_textures;
+ QHash<glyph_t, TextureInfo *> m_glyphsTexture;
+ QSet<glyph_t> m_unusedGlyphs;
+ QSet<QRhiTexture *> m_pendingDispose;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIDISTANCEFIELDGLYPHCACHE_H
diff --git a/src/quick/scenegraph/qsgrhilayer.cpp b/src/quick/scenegraph/qsgrhilayer.cpp
new file mode 100644
index 0000000000..80ca40d50a
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhilayer.cpp
@@ -0,0 +1,471 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhilayer_p.h"
+
+#include <private/qqmlglobal_p.h>
+#include <private/qsgrenderer_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
+
+QSGRhiLayer::QSGRhiLayer(QSGRenderContext *context)
+ : QSGLayer(*(new QSGRhiLayerPrivate))
+ , m_mipmap(false)
+ , m_live(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+ , m_multisampling(false)
+ , m_grab(false)
+ , m_mirrorHorizontal(false)
+ , m_mirrorVertical(true)
+{
+ m_context = static_cast<QSGDefaultRenderContext *>(context);
+ m_rhi = m_context->rhi();
+ Q_ASSERT(m_rhi);
+}
+
+QSGRhiLayer::~QSGRhiLayer()
+{
+ invalidated();
+}
+
+void QSGRhiLayer::invalidated()
+{
+ releaseResources();
+
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+int QSGRhiLayerPrivate::comparisonKey() const
+{
+ Q_Q(const QSGRhiLayer);
+ return int(qintptr(q->m_texture));
+}
+
+bool QSGRhiLayer::hasAlphaChannel() const
+{
+ return true;
+}
+
+bool QSGRhiLayer::hasMipmaps() const
+{
+ return m_mipmap;
+}
+
+int QSGRhiLayer::textureId() const
+{
+ Q_ASSERT_X(false, "QSGRhiLayer::textureId()", "Not implemented for RHI");
+ return 0;
+}
+
+void QSGRhiLayer::bind()
+{
+ Q_ASSERT_X(false, "QSGRhiLayer::bind()", "Not implemented for RHI");
+}
+
+QRhiTexture *QSGRhiLayerPrivate::rhiTexture() const
+{
+ Q_Q(const QSGRhiLayer);
+ return q->m_texture;
+}
+
+void QSGRhiLayerPrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(resourceUpdates);
+}
+
+bool QSGRhiLayer::updateTexture()
+{
+ // called during the preprocess phase, outside of frame rendering -> good
+
+ bool doGrab = (m_live || m_grab) && m_dirtyTexture;
+ if (doGrab)
+ grab();
+
+ if (m_grab)
+ emit scheduledUpdateCompleted();
+
+ m_grab = false;
+ return doGrab;
+}
+
+void QSGRhiLayer::setHasMipmaps(bool mipmap)
+{
+ if (mipmap == m_mipmap)
+ return;
+
+ m_mipmap = mipmap;
+ if (m_mipmap && m_texture)
+ markDirtyTexture();
+}
+
+
+void QSGRhiLayer::setItem(QSGNode *item)
+{
+ if (item == m_item)
+ return;
+
+ m_item = item;
+
+ if (m_live && !m_item)
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+
+ m_size = size;
+
+ if (m_live && m_size.isNull())
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setFormat(uint format)
+{
+ Q_UNUSED(format);
+}
+
+void QSGRhiLayer::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+
+ m_live = live;
+
+ if (m_live && (!m_item || m_size.isNull()))
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+
+ m_grab = true;
+ if (m_dirtyTexture)
+ emit updateRequested();
+}
+
+void QSGRhiLayer::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGRhiLayer::setMirrorHorizontal(bool mirror)
+{
+ m_mirrorHorizontal = mirror;
+}
+
+void QSGRhiLayer::setMirrorVertical(bool mirror)
+{
+ m_mirrorVertical = mirror;
+}
+
+void QSGRhiLayer::markDirtyTexture()
+{
+ m_dirtyTexture = true;
+ if (m_live || m_grab)
+ emit updateRequested();
+}
+
+void QSGRhiLayer::releaseResources()
+{
+ delete m_rt;
+ m_rt = nullptr;
+
+ delete m_rtRp;
+ m_rtRp = nullptr;
+
+ delete m_ds;
+ m_ds = nullptr;
+
+ delete m_msaaColorBuffer;
+ m_msaaColorBuffer = nullptr;
+
+ delete m_texture;
+ m_texture = nullptr;
+
+ delete m_secondaryTexture;
+ m_secondaryTexture = nullptr;
+}
+
+void QSGRhiLayer::grab()
+{
+ if (!m_item || m_size.isNull()) {
+ releaseResources();
+ m_dirtyTexture = false;
+ return;
+ }
+
+ int effectiveSamples = m_samples;
+ // if no layer.samples was provided use the window's msaa setting
+ if (effectiveSamples <= 1)
+ effectiveSamples = m_context->msaaSampleCount();
+
+ const bool needsNewRt = !m_rt || m_rt->pixelSize() != m_size || (m_recursive && !m_secondaryTexture);
+ const bool mipmapSettingChanged = m_texture && m_texture->flags().testFlag(QRhiTexture::MipMapped) != m_mipmap;
+ const bool msaaSettingChanged = (effectiveSamples > 1 && !m_msaaColorBuffer) || (effectiveSamples <= 1 && m_msaaColorBuffer);
+
+ if (needsNewRt ||mipmapSettingChanged || msaaSettingChanged) {
+ if (effectiveSamples <= 1) {
+ m_multisampling = false;
+ } else {
+ m_multisampling = m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
+ if (!m_multisampling)
+ qWarning("Layer requested %d samples but multisample renderbuffers are not supported", effectiveSamples);
+ }
+
+ QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
+ if (m_mipmap)
+ textureFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ if (m_multisampling) {
+ releaseResources();
+ m_msaaColorBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_size, effectiveSamples);
+ if (!m_msaaColorBuffer->build()) {
+ qWarning("Failed to build multisample color buffer for layer of size %dx%d, sample count %d",
+ m_size.width(), m_size.height(), effectiveSamples);
+ releaseResources();
+ return;
+ }
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_texture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_size, effectiveSamples);
+ if (!m_ds->build()) {
+ qWarning("Failed to build depth-stencil buffer for layer");
+ releaseResources();
+ return;
+ }
+ QRhiTextureRenderTargetDescription desc;
+ QRhiColorAttachment color0(m_msaaColorBuffer);
+ color0.setResolveTexture(m_texture);
+ desc.setColorAttachments({ color0 });
+ desc.setDepthStencilBuffer(m_ds);
+ m_rt = m_rhi->newTextureRenderTarget(desc);
+ m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
+ if (!m_rtRp) {
+ qWarning("Failed to build render pass descriptor for layer");
+ releaseResources();
+ return;
+ }
+ m_rt->setRenderPassDescriptor(m_rtRp);
+ if (!m_rt->build()) {
+ qWarning("Failed to build texture render target for layer");
+ releaseResources();
+ return;
+ }
+ } else {
+ releaseResources();
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_texture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_size);
+ if (!m_ds->build()) {
+ qWarning("Failed to build depth-stencil buffer for layer");
+ releaseResources();
+ return;
+ }
+ QRhiColorAttachment color0(m_texture);
+ if (m_recursive) {
+ // Here rt is associated with m_secondaryTexture instead of m_texture.
+ // We will issue a copy to m_texture afterwards.
+ m_secondaryTexture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_secondaryTexture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ color0.setTexture(m_secondaryTexture);
+ }
+ m_rt = m_rhi->newTextureRenderTarget({ color0, m_ds });
+ m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
+ if (!m_rtRp) {
+ qWarning("Failed to build render pass descriptor for layer");
+ releaseResources();
+ return;
+ }
+ m_rt->setRenderPassDescriptor(m_rtRp);
+ if (!m_rt->build()) {
+ qWarning("Failed to build texture render target for layer");
+ releaseResources();
+ return;
+ }
+ }
+ }
+
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = m_context->createRenderer();
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
+ }
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+ // This must not be moved. The flag must be reset only after the
+ // nodeChanged otherwise we end up with constantly updating even when the
+ // layer contents do not change.
+ m_dirtyTexture = false;
+
+ m_renderer->setDevicePixelRatio(m_dpr);
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRectF mirrored;
+ if (m_rhi->isYUpInFramebuffer()) {
+ mirrored = QRectF(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.bottom() : m_rect.top(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? -m_rect.height() : m_rect.height());
+ } else {
+ mirrored = QRectF(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.top() : m_rect.bottom(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? m_rect.height() : -m_rect.height());
+ }
+ m_renderer->setProjectionMatrixToRect(mirrored, !m_rhi->isYUpInNDC());
+ m_renderer->setClearColor(Qt::transparent);
+ m_renderer->setRenderTarget(m_rt);
+ m_renderer->setCommandBuffer(m_context->currentFrameCommandBuffer());
+ m_renderer->setRenderPassDescriptor(m_rtRp);
+
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr;
+
+ // render with our own "sub-renderer" (this will just add a render pass to the command buffer)
+ if (m_multisampling) {
+ m_context->renderNextRhiFrame(m_renderer);
+ } else {
+ if (m_recursive) {
+ m_context->renderNextRhiFrame(m_renderer);
+ if (!resourceUpdates)
+ resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ resourceUpdates->copyTexture(m_texture, m_secondaryTexture);
+ } else {
+ m_context->renderNextRhiFrame(m_renderer);
+ }
+ }
+
+ if (m_mipmap) {
+ if (!resourceUpdates)
+ resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ // going to be expensive - if done every frame - but the user asked for it...
+ resourceUpdates->generateMips(m_texture);
+ }
+
+ // Do not defer committing the resource updates to the main pass - with
+ // multiple layers there can be dependencies, so the texture should be
+ // usable once we return.
+ m_context->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+QImage QSGRhiLayer::toImage() const
+{
+ if (!m_texture)
+ return QImage();
+
+ QRhiCommandBuffer *cb = m_context->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult result;
+ QRhiReadbackDescription readbackDesc(m_texture);
+ resourceUpdates->readBackTexture(readbackDesc, &result);
+
+ cb->resourceUpdate(resourceUpdates);
+
+ // Inefficient but what can you do. We need the results right away. This is
+ // not something that occurs in a normal rendering process anyway. (used by
+ // QQuickItem's grabToImage).
+ m_rhi->finish();
+
+ if (result.data.isEmpty()) {
+ qWarning("Layer grab failed");
+ return QImage();
+ }
+
+ // There is no room for negotiation here, the texture is RGBA8, and the
+ // readback happens with GL_RGBA on GL, so RGBA8888 is the only option.
+ // Also, Quick is always premultiplied alpha.
+ const QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
+
+ const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
+ return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).mirrored();
+}
+
+QRectF QSGRhiLayer::normalizedTextureSubRect() const
+{
+ return QRectF(m_mirrorHorizontal ? 1 : 0,
+ m_mirrorVertical ? 0 : 1,
+ m_mirrorHorizontal ? -1 : 1,
+ m_mirrorVertical ? 1 : -1);
+}
+
+#include "moc_qsgrhilayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgrhilayer_p.h b/src/quick/scenegraph/qsgrhilayer_p.h
new file mode 100644
index 0000000000..6c4953ce17
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhilayer_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QSGRHILAYER_P_H
+#define QSGRHILAYER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QSGRhiLayerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QSGRhiLayer : public QSGLayer
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGRhiLayer)
+public:
+ QSGRhiLayer(QSGRenderContext *context);
+ ~QSGRhiLayer();
+
+ bool updateTexture() override;
+
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ QSize textureSize() const override { return m_size; }
+
+ void bind() override;
+ int textureId() const override;
+
+ void setItem(QSGNode *item) override;
+ void setRect(const QRectF &rect) override;
+ void setSize(const QSize &size) override;
+ void setHasMipmaps(bool mipmap) override;
+ void setFormat(uint format) override;
+ void setLive(bool live) override;
+ void setRecursive(bool recursive) override;
+ void setDevicePixelRatio(qreal ratio) override { m_dpr = ratio; }
+ void setMirrorHorizontal(bool mirror) override;
+ void setMirrorVertical(bool mirror) override;
+ QRectF normalizedTextureSubRect() const override;
+ void setSamples(int samples) override { m_samples = samples; }
+
+ void scheduleUpdate() override;
+ QImage toImage() const override;
+
+public Q_SLOTS:
+ void markDirtyTexture() override;
+ void invalidated() override;
+
+private:
+ void grab();
+ void releaseResources();
+
+ QSGNode *m_item = nullptr;
+ QRectF m_rect;
+ QSize m_size;
+ qreal m_dpr = 1;
+ QRhiTexture::Format m_format = QRhiTexture::RGBA8;
+
+ QSGRenderer *m_renderer = nullptr;
+ QRhiTexture *m_texture = nullptr;
+ QRhiRenderBuffer *m_ds = nullptr;
+ QRhiRenderBuffer *m_msaaColorBuffer = nullptr;
+ QRhiTexture *m_secondaryTexture = nullptr;
+ QRhiTextureRenderTarget *m_rt = nullptr;
+ QRhiRenderPassDescriptor *m_rtRp = nullptr;
+
+ QSGDefaultRenderContext *m_context = nullptr;
+ QRhi *m_rhi = nullptr;
+ int m_samples = 0;
+
+ uint m_mipmap : 1;
+ uint m_live : 1;
+ uint m_recursive : 1;
+ uint m_dirtyTexture : 1;
+ uint m_multisampling : 1;
+ uint m_grab : 1;
+ uint m_mirrorHorizontal : 1;
+ uint m_mirrorVertical : 1;
+};
+
+class QSGRhiLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGRhiLayer)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHILAYER_P_H
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
new file mode 100644
index 0000000000..fe9cecb51b
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -0,0 +1,886 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhishadereffectnode_p.h"
+#include "qsgdefaultrendercontext_p.h"
+#include "qsgrhisupport_p.h"
+#include <qsgmaterialrhishader.h>
+#include <qsgtextureprovider.h>
+#include <private/qsgplaintexture_p.h>
+#include <QtGui/private/qshaderdescription_p.h>
+#include <QQmlFile>
+#include <QFile>
+#include <QFileSelector>
+
+QT_BEGIN_NAMESPACE
+
+void QSGRhiShaderLinker::reset(const QShader &vs, const QShader &fs)
+{
+ Q_ASSERT(vs.isValid() && fs.isValid());
+ m_vs = vs;
+ m_fs = fs;
+
+ m_error = false;
+
+ m_constantBufferSize = 0;
+ m_constants.clear();
+ m_samplers.clear();
+ m_samplerNameMap.clear();
+}
+
+void QSGRhiShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count());
+ if (!dirtyIndices) {
+ m_constantBufferSize = qMax(m_constantBufferSize, shader.shaderInfo.constantDataSize);
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) {
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
+ Constant c;
+ c.size = var.size;
+ c.specialType = vd.specialType;
+ if (c.specialType != QSGShaderEffectNode::VariableData::SubRect) {
+ c.value = vd.value;
+ if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
+ if (c.specialType == QSGShaderEffectNode::VariableData::None) {
+ qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
+ << "offset" << var.offset << "value" << c.value;
+ } else {
+ qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
+ << "offset" << var.offset << "special" << c.specialType;
+ }
+ }
+ } else {
+ Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
+ c.value = var.name.mid(11);
+ }
+ m_constants[var.offset] = c;
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices) {
+ const int offset = shader.shaderInfo.variables.at(idx).offset;
+ const QVariant value = shader.varData.at(idx).value;
+ m_constants[offset].value = value;
+ if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
+ qDebug() << "cbuf update" << shader.shaderInfo.name
+ << "offset" << offset << "value" << value;
+ }
+ }
+ }
+}
+
+void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ if (!dirtyIndices) {
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+ m_samplers.insert(var.bindPoint, vd.value);
+ m_samplerNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
+ m_samplers.insert(var.bindPoint, vd.value);
+ m_samplerNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+}
+
+void QSGRhiShaderLinker::linkTextureSubRects()
+{
+ // feedConstants stores <name> in Constant::value for subrect entries. Now
+ // that both constants and textures are known, replace the name with the
+ // texture binding point.
+ for (Constant &c : m_constants) {
+ if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ if (c.value.type() == QVariant::ByteArray) {
+ const QByteArray name = c.value.toByteArray();
+ if (!m_samplerNameMap.contains(name))
+ qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", name.constData());
+ c.value = m_samplerNameMap[name];
+ }
+ }
+ }
+}
+
+void QSGRhiShaderLinker::dump()
+{
+ if (m_error) {
+ qDebug() << "Failed to generate program data";
+ return;
+ }
+ qDebug() << "Combined shader data" << m_vs << m_fs << "cbuffer size" << m_constantBufferSize;
+ qDebug() << " - constants" << m_constants;
+ qDebug() << " - samplers" << m_samplers;
+}
+
+QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << "size" << c.size;
+ if (c.specialType != QSGShaderEffectNode::VariableData::None)
+ debug << "special" << c.specialType;
+ else
+ debug << "value" << c.value;
+ return debug;
+}
+
+struct QSGRhiShaderMaterialTypeCache
+{
+ QSGMaterialType *get(const QShader &vs, const QShader &fs);
+ void reset() { qDeleteAll(m_types); m_types.clear(); }
+
+ struct Key {
+ QShader blob[2];
+ Key() { }
+ Key(const QShader &vs, const QShader &fs) { blob[0] = vs; blob[1] = fs; }
+ bool operator==(const Key &other) const {
+ return blob[0] == other.blob[0] && blob[1] == other.blob[1];
+ }
+ };
+ QHash<Key, QSGMaterialType *> m_types;
+};
+
+uint qHash(const QSGRhiShaderMaterialTypeCache::Key &key, uint seed = 0)
+{
+ uint hash = seed;
+ for (int i = 0; i < 2; ++i)
+ hash = hash * 31337 + qHash(key.blob[i]);
+ return hash;
+}
+
+QSGMaterialType *QSGRhiShaderMaterialTypeCache::get(const QShader &vs, const QShader &fs)
+{
+ const Key k(vs, fs);
+ if (m_types.contains(k))
+ return m_types.value(k);
+
+ QSGMaterialType *t = new QSGMaterialType;
+ m_types.insert(k, t);
+ return t;
+}
+
+static QSGRhiShaderMaterialTypeCache shaderMaterialTypeCache;
+
+class QSGRhiShaderEffectMaterialShader : public QSGMaterialRhiShader
+{
+public:
+ QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material);
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+QSGRhiShaderEffectMaterialShader::QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material)
+{
+ setFlag(UpdatesGraphicsPipelineState, true);
+ setShader(VertexStage, material->m_vertexShader);
+ setShader(FragmentStage, material->m_fragmentShader);
+}
+
+static inline QColor qsg_premultiply_color(const QColor &c)
+{
+ return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
+}
+
+bool QSGRhiShaderEffectMaterialShader::updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ for (auto it = mat->m_linker.m_constants.constBegin(), itEnd = mat->m_linker.m_constants.constEnd(); it != itEnd; ++it) {
+ const int offset = it.key();
+ char *dst = buf->data() + offset;
+ const QSGRhiShaderLinker::Constant &c(it.value());
+ if (c.specialType == QSGShaderEffectNode::VariableData::Opacity) {
+ if (state.isOpacityDirty()) {
+ const float f = state.opacity();
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ changed = true;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
+ if (state.isMatrixDirty()) {
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, state.combinedMatrix().constData(), sz);
+ changed = true;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ // vec4
+ QRectF subRect(0, 0, 1, 1);
+ const int binding = c.value.toInt(); // filled in by linkTextureSubRects
+ if (binding < QSGRhiShaderEffectMaterial::MAX_BINDINGS) {
+ if (QSGTextureProvider *tp = mat->m_textureProviders.at(binding)) {
+ if (QSGTexture *t = tp->texture())
+ subRect = t->normalizedTextureSubRect();
+ }
+ }
+ const float f[4] = { float(subRect.x()), float(subRect.y()),
+ float(subRect.width()), float(subRect.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
+ changed = true;
+ switch (int(c.value.type())) {
+ case QMetaType::QColor: {
+ const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value));
+ const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::Float: {
+ const float f = qvariant_cast<float>(c.value);
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Double: {
+ const float f = float(qvariant_cast<double>(c.value));
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Int: {
+ const int i = c.value.toInt();
+ Q_ASSERT(sizeof(i) == c.size);
+ memcpy(dst, &i, sizeof(i));
+ break;
+ }
+ case QMetaType::Bool: {
+ const bool b = c.value.toBool();
+ Q_ASSERT(sizeof(b) == c.size);
+ memcpy(dst, &b, sizeof(b));
+ break;
+ }
+ case QMetaType::QTransform: { // mat3
+ const QTransform v = qvariant_cast<QTransform>(c.value);
+ const float m[3][3] = {
+ { float(v.m11()), float(v.m12()), float(v.m13()) },
+ { float(v.m21()), float(v.m22()), float(v.m23()) },
+ { float(v.m31()), float(v.m32()), float(v.m33()) }
+ };
+ Q_ASSERT(sizeof(m) == c.size);
+ memcpy(dst, m[0], sizeof(m));
+ break;
+ }
+ case QMetaType::QSize:
+ case QMetaType::QSizeF: { // vec2
+ const QSizeF v = c.value.toSizeF();
+ const float f[2] = { float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QPoint:
+ case QMetaType::QPointF: { // vec2
+ const QPointF v = c.value.toPointF();
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QRect:
+ case QMetaType::QRectF: { // vec4
+ const QRectF v = c.value.toRectF();
+ const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector2D: { // vec2
+ const QVector2D v = qvariant_cast<QVector2D>(c.value);
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector3D: { // vec3
+ const QVector3D v = qvariant_cast<QVector3D>(c.value);
+ const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector4D: { // vec4
+ const QVector4D v = qvariant_cast<QVector4D>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QQuaternion: { // vec4
+ const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QMatrix4x4: { // mat4
+ const QMatrix4x4 v = qvariant_cast<QMatrix4x4>(c.value);
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, v.constData(), sz);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return changed;
+}
+
+void QSGRhiShaderEffectMaterialShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ if (binding >= QSGRhiShaderEffectMaterial::MAX_BINDINGS)
+ return;
+
+ QSGTextureProvider *tp = mat->m_textureProviders.at(binding);
+ if (tp) {
+ if (QSGTexture *t = tp->texture()) {
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ if (t->isAtlasTexture() && !mat->m_geometryUsesTextureSubRect) {
+ // Why the hassle with the batch: while removedFromAtlas() is
+ // able to operate with its own resource update batch (which is
+ // then committed immediately), that approach is wrong when the
+ // atlas enqueued (in the updateRhiTexture() above) not yet
+ // committed operations to state.resourceUpdateBatch()... The
+ // only safe way then is to use the same batch the atlas'
+ // updateRhiTexture() used.
+ t->setWorkResourceUpdateBatch(state.resourceUpdateBatch());
+ QSGTexture *newTexture = t->removedFromAtlas();
+ t->setWorkResourceUpdateBatch(nullptr);
+ if (newTexture)
+ t = newTexture;
+ }
+ *texture = t;
+ return;
+ }
+ }
+
+ if (!mat->m_dummyTexture) {
+ mat->m_dummyTexture = new QSGPlainTexture;
+ mat->m_dummyTexture->setFiltering(QSGTexture::Nearest);
+ mat->m_dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
+ mat->m_dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
+ QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ mat->m_dummyTexture->setImage(img);
+ mat->m_dummyTexture->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ }
+ *texture = mat->m_dummyTexture;
+}
+
+bool QSGRhiShaderEffectMaterialShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ switch (mat->m_cullMode) {
+ case QSGShaderEffectNode::FrontFaceCulling:
+ ps->cullMode = GraphicsPipelineState::CullFront;
+ return true;
+ case QSGShaderEffectNode::BackFaceCulling:
+ ps->cullMode = GraphicsPipelineState::CullBack;
+ return true;
+ default:
+ return false;
+ }
+}
+
+QSGRhiShaderEffectMaterial::QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node)
+ : m_node(node)
+{
+ setFlag(SupportsRhiShader | Blending | RequiresFullMatrix, true); // may be changed in syncMaterial()
+}
+
+QSGRhiShaderEffectMaterial::~QSGRhiShaderEffectMaterial()
+{
+ delete m_dummyTexture;
+}
+
+static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
+{
+ for (QSGTextureProvider *tp : textureProviders) {
+ if (tp && tp->texture() && tp->texture()->isAtlasTexture())
+ return true;
+ }
+ return false;
+}
+
+int QSGRhiShaderEffectMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGRhiShaderEffectMaterial *o = static_cast<const QSGRhiShaderEffectMaterial *>(other);
+
+ if (int diff = m_cullMode - o->m_cullMode)
+ return diff;
+
+ if (int diff = m_textureProviders.count() - o->m_textureProviders.count())
+ return diff;
+
+ if (m_linker.m_constants != o->m_linker.m_constants)
+ return 1;
+
+ if (hasAtlasTexture(m_textureProviders) && !m_geometryUsesTextureSubRect)
+ return -1;
+
+ if (hasAtlasTexture(o->m_textureProviders) && !o->m_geometryUsesTextureSubRect)
+ return 1;
+
+ for (int binding = 0, count = m_textureProviders.count(); binding != count; ++binding) {
+ QSGTextureProvider *tp1 = m_textureProviders.at(binding);
+ QSGTextureProvider *tp2 = o->m_textureProviders.at(binding);
+ if (tp1 && tp2) {
+ QSGTexture *t1 = tp1->texture();
+ QSGTexture *t2 = tp2->texture();
+ if (t1 && t2) {
+ if (int diff = t1->comparisonKey() - t2->comparisonKey())
+ return diff;
+ } else {
+ if (!t1 && t2)
+ return -1;
+ if (t1 && !t2)
+ return 1;
+ }
+ } else {
+ if (!tp1 && tp2)
+ return -1;
+ if (tp1 && !tp2)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+QSGMaterialType *QSGRhiShaderEffectMaterial::type() const
+{
+ return m_materialType;
+}
+
+QSGMaterialShader *QSGRhiShaderEffectMaterial::createShader() const
+{
+ Q_ASSERT(flags().testFlag(RhiShaderWanted));
+ return new QSGRhiShaderEffectMaterialShader(this);
+}
+
+void QSGRhiShaderEffectMaterial::updateTextureProviders(bool layoutChange)
+{
+ if (layoutChange) {
+ for (QSGTextureProvider *tp : m_textureProviders) {
+ if (tp) {
+ QObject::disconnect(tp, SIGNAL(textureChanged()), m_node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ }
+ m_textureProviders.fill(nullptr, MAX_BINDINGS);
+ }
+
+ for (auto it = m_linker.m_samplers.constBegin(), itEnd = m_linker.m_samplers.constEnd(); it != itEnd; ++it) {
+ const int binding = it.key();
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
+ QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
+ if (binding >= MAX_BINDINGS) {
+ qWarning("Sampler at binding %d exceeds the available ShaderEffect binding slots; ignored",
+ binding);
+ continue;
+ }
+ QSGTextureProvider *&activeProvider(m_textureProviders[binding]);
+ if (newProvider != activeProvider) {
+ if (activeProvider) {
+ QObject::disconnect(activeProvider, SIGNAL(textureChanged()), m_node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ if (newProvider) {
+ Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
+ "QSGRhiShaderEffectMaterial::updateTextureProviders",
+ "Texture provider must belong to the rendering thread");
+ QObject::connect(newProvider, SIGNAL(textureChanged()), m_node, SLOT(handleTextureChange()));
+ QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ } else {
+ const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
+ qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
+ binding, typeName);
+ }
+ activeProvider = newProvider;
+ }
+ }
+}
+
+QSGRhiShaderEffectNode::QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr)
+ : QSGShaderEffectNode(mgr),
+ m_rc(rc),
+ m_mgr(mgr),
+ m_material(this)
+{
+ setFlag(UsePreprocess, true);
+ setMaterial(&m_material);
+}
+
+QRectF QSGRhiShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtlasTextures)
+{
+ QRectF srcRect(0, 0, 1, 1);
+ bool geometryUsesTextureSubRect = false;
+ if (supportsAtlasTextures) {
+ QSGTextureProvider *tp = nullptr;
+ for (int binding = 0, count = m_material.m_textureProviders.count(); binding != count; ++binding) {
+ if (QSGTextureProvider *candidate = m_material.m_textureProviders.at(binding)) {
+ if (!tp) {
+ tp = candidate;
+ } else { // there can only be one...
+ tp = nullptr;
+ break;
+ }
+ }
+ }
+ if (tp && tp->texture()) {
+ srcRect = tp->texture()->normalizedTextureSubRect();
+ geometryUsesTextureSubRect = true;
+ }
+ }
+
+ if (m_material.m_geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
+ m_material.m_geometryUsesTextureSubRect = geometryUsesTextureSubRect;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ return srcRect;
+}
+
+static QShader loadShader(const QString &filename)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to find shader" << filename;
+ return QShader();
+ }
+ return QShader::fromSerialized(f.readAll());
+}
+
+void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
+{
+ static QShader defaultVertexShader;
+ static QShader defaultFragmentShader;
+
+ if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
+ m_material.setFlag(QSGMaterial::Blending, syncData->blending);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_material.m_cullMode != syncData->cullMode) {
+ m_material.m_cullMode = syncData->cullMode;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) {
+ m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
+ if (m_material.m_hasCustomVertexShader) {
+ m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
+ } else {
+ if (!defaultVertexShader.isValid())
+ defaultVertexShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"));
+ m_material.m_vertexShader = defaultVertexShader;
+ }
+
+ m_material.m_hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
+ if (m_material.m_hasCustomFragmentShader) {
+ m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
+ } else {
+ if (!defaultFragmentShader.isValid())
+ defaultFragmentShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"));
+ m_material.m_fragmentShader = defaultFragmentShader;
+ }
+
+ m_material.m_materialType = shaderMaterialTypeCache.get(m_material.m_vertexShader, m_material.m_fragmentShader);
+ m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
+
+ if (m_material.m_hasCustomVertexShader) {
+ m_material.m_linker.feedConstants(*syncData->vertex.shader);
+ m_material.m_linker.feedSamplers(*syncData->vertex.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect vertex shader");
+ defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
+
+ // { mat4 qt_Matrix; float qt_Opacity; } where only the matrix is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Matrix");
+ v.offset = 0;
+ v.size = 16 * sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ defaultSD.varData.append(vd);
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+ m_material.m_linker.feedConstants(defaultSD);
+ }
+
+ if (m_material.m_hasCustomFragmentShader) {
+ m_material.m_linker.feedConstants(*syncData->fragment.shader);
+ m_material.m_linker.feedSamplers(*syncData->fragment.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect fragment shader");
+ defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+
+ // { mat4 qt_Matrix; float qt_Opacity; } where only the opacity is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Opacity");
+ v.offset = 16 * sizeof(float);
+ v.size = sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ defaultSD.varData.append(vd);
+
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 1;
+ v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ defaultSD.shaderInfo.variables.append(v);
+ for (const QSGShaderEffectNode::VariableData &extVarData : qAsConst(syncData->fragment.shader->varData)) {
+ if (extVarData.specialType == QSGShaderEffectNode::VariableData::Source) {
+ vd.value = extVarData.value;
+ break;
+ }
+ }
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ defaultSD.varData.append(vd);
+
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+
+ m_material.m_linker.feedConstants(defaultSD);
+ m_material.m_linker.feedSamplers(defaultSD);
+ }
+
+ m_material.m_linker.linkTextureSubRects();
+ m_material.updateTextureProviders(true);
+ markDirty(QSGNode::DirtyMaterial);
+
+ } else {
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) {
+ if (!syncData->vertex.dirtyConstants->isEmpty())
+ m_material.m_linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
+ if (!syncData->fragment.dirtyConstants->isEmpty())
+ m_material.m_linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) {
+ if (!syncData->vertex.dirtyTextures->isEmpty())
+ m_material.m_linker.feedSamplers(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
+ if (!syncData->fragment.dirtyTextures->isEmpty())
+ m_material.m_linker.feedSamplers(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
+ m_material.m_linker.linkTextureSubRects();
+ m_material.updateTextureProviders(false);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+ }
+
+ if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.m_hasCustomVertexShader) {
+ m_material.setFlag(QSGMaterial::RequiresFullMatrix, m_material.m_hasCustomVertexShader);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+}
+
+void QSGRhiShaderEffectNode::handleTextureChange()
+{
+ markDirty(QSGNode::DirtyMaterial);
+ emit m_mgr->textureChanged();
+}
+
+void QSGRhiShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
+{
+ for (QSGTextureProvider *&tp : m_material.m_textureProviders) {
+ if (tp == object)
+ tp = nullptr;
+ }
+}
+
+void QSGRhiShaderEffectNode::preprocess()
+{
+ for (QSGTextureProvider *tp : m_material.m_textureProviders) {
+ if (tp) {
+ if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
+ texture->updateTexture();
+ }
+ }
+}
+
+void QSGRhiShaderEffectNode::cleanupMaterialTypeCache()
+{
+ shaderMaterialTypeCache.reset();
+}
+
+bool QSGRhiGuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
+{
+ return false; // because SPIR-V and QRhi make it look so, regardless of the underlying API
+}
+
+QString QSGRhiGuiThreadShaderEffectManager::log() const
+{
+ return QString();
+}
+
+QSGGuiThreadShaderEffectManager::Status QSGRhiGuiThreadShaderEffectManager::status() const
+{
+ return m_status;
+}
+
+void QSGRhiGuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result)
+{
+ QUrl srcUrl(QString::fromUtf8(src));
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ if (!m_fileSelector) {
+ m_fileSelector = new QFileSelector(this);
+ m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("qsb"));
+ }
+ const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ m_status = Error;
+ emit shaderCodePrepared(false, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+ const QShader s = QShader::fromSerialized(f.readAll());
+ f.close();
+ if (!s.isValid()) {
+ qWarning("ShaderEffect: Failed to deserialize QShader from %s", qPrintable(fn));
+ m_status = Error;
+ emit shaderCodePrepared(false, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+ result->name = fn;
+ result->rhiShader = s;
+ const bool ok = reflect(result);
+ m_status = ok ? Compiled : Error;
+ emit shaderCodePrepared(ok, typeHint, src, result);
+ emit logAndStatusChanged();
+ } else {
+ qWarning("rhi shader effect only supports files (qrc or local) at the moment");
+ emit shaderCodePrepared(false, typeHint, src, result);
+ }
+}
+
+bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
+{
+ switch (result->rhiShader.stage()) {
+ case QShader::VertexStage:
+ result->type = ShaderInfo::TypeVertex;
+ break;
+ case QShader::FragmentStage:
+ result->type = ShaderInfo::TypeFragment;
+ break;
+ default:
+ result->type = ShaderInfo::TypeOther;
+ qWarning("Unsupported shader stage (%d)", result->rhiShader.stage());
+ return false;
+ }
+
+ const QShaderDescription desc = result->rhiShader.description();
+ result->constantDataSize = 0;
+
+ int ubufBinding = -1;
+ const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
+ const int ubufCount = ubufs.count();
+ for (int i = 0; i < ubufCount; ++i) {
+ const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
+ if (ubufBinding == -1 && ubuf.binding >= 0) {
+ ubufBinding = ubuf.binding;
+ result->constantDataSize = ubuf.size;
+ for (const QShaderDescription::BlockVariable &member : ubuf.members) {
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Constant;
+ v.name = member.name.toUtf8();
+ v.offset = member.offset;
+ v.size = member.size;
+ result->variables.append(v);
+ }
+ } else {
+ qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
+ }
+ }
+
+ const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = desc.combinedImageSamplers();
+ const int samplerCount = combinedImageSamplers.count();
+ for (int i = 0; i < samplerCount; ++i) {
+ const QShaderDescription::InOutVariable &combinedImageSampler(combinedImageSamplers[i]);
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Sampler;
+ v.name = combinedImageSampler.name.toUtf8();
+ v.bindPoint = combinedImageSampler.binding;
+ result->variables.append(v);
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode_p.h b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
new file mode 100644
index 0000000000..26460d24b2
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRHISHADEREFFECTNODE_P_H
+#define QSGRHISHADEREFFECTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <qsgmaterial.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QSGPlainTexture;
+class QSGRhiShaderEffectNode;
+class QSGRhiGuiThreadShaderEffectManager;
+class QFileSelector;
+
+class QSGRhiShaderLinker
+{
+public:
+ void reset(const QShader &vs, const QShader &fs);
+
+ void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void linkTextureSubRects();
+
+ void dump();
+
+ struct Constant {
+ uint size;
+ QSGShaderEffectNode::VariableData::SpecialType specialType;
+ QVariant value;
+ bool operator==(const Constant &other) const {
+ return size == other.size && specialType == other.specialType
+ && (specialType == QSGShaderEffectNode::VariableData::None ? value == other.value : true);
+ }
+ };
+
+ bool m_error;
+ QShader m_vs;
+ QShader m_fs;
+ uint m_constantBufferSize;
+ QHash<uint, Constant> m_constants; // offset -> Constant
+ QHash<int, QVariant> m_samplers; // binding -> value (source ref)
+ QHash<QByteArray, int> m_samplerNameMap; // name -> binding
+};
+
+QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c);
+
+class QSGRhiShaderEffectMaterial : public QSGMaterial
+{
+public:
+ QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node);
+ ~QSGRhiShaderEffectMaterial();
+
+ int compare(const QSGMaterial *other) const override;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
+
+ void updateTextureProviders(bool layoutChange);
+
+ static const int MAX_BINDINGS = 32;
+
+ QSGRhiShaderEffectNode *m_node;
+ QSGMaterialType *m_materialType = nullptr;
+ QSGRhiShaderLinker m_linker;
+ QVector<QSGTextureProvider *> m_textureProviders; // [binding] = QSGTextureProvider
+ bool m_geometryUsesTextureSubRect = false;
+ QSGShaderEffectNode::CullMode m_cullMode = QSGShaderEffectNode::NoCulling;
+ bool m_hasCustomVertexShader = false;
+ bool m_hasCustomFragmentShader = false;
+ QShader m_vertexShader;
+ QShader m_fragmentShader;
+ QSGPlainTexture *m_dummyTexture = nullptr;
+};
+
+class QSGRhiShaderEffectNode : public QObject, public QSGShaderEffectNode
+{
+ Q_OBJECT
+
+public:
+ QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr);
+
+ QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) override;
+ void syncMaterial(SyncData *syncData) override;
+ void preprocess() override;
+
+ static void cleanupMaterialTypeCache();
+
+private Q_SLOTS:
+ void handleTextureChange();
+ void handleTextureProviderDestroyed(QObject *object);
+
+private:
+ QSGDefaultRenderContext *m_rc;
+ QSGRhiGuiThreadShaderEffectManager *m_mgr;
+ QSGRhiShaderEffectMaterial m_material;
+};
+
+class QSGRhiGuiThreadShaderEffectManager : public QSGGuiThreadShaderEffectManager
+{
+public:
+ bool hasSeparateSamplerAndTextureObjects() const override;
+ QString log() const override;
+ Status status() const override;
+ void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override;
+
+private:
+ bool reflect(ShaderInfo *result);
+ Status m_status = Uncompiled;
+ QFileSelector *m_fileSelector = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHISHADEREFFECTNODE_P_H
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
new file mode 100644
index 0000000000..2ca66f23d5
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -0,0 +1,567 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhisupport_p.h"
+#include "qsgdefaultrendercontext_p.h"
+#include <QtGui/qwindow.h>
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/qvulkaninstance.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(vulkan)
+QVulkanInstance *s_vulkanInstance = nullptr;
+#endif
+
+QVulkanInstance *QSGRhiSupport::vulkanInstance()
+{
+#if QT_CONFIG(vulkan)
+ QSGRhiSupport *inst = QSGRhiSupport::instance();
+ if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan)
+ return nullptr;
+
+ if (!s_vulkanInstance) {
+ s_vulkanInstance = new QVulkanInstance;
+ if (inst->isDebugLayerRequested()) {
+#ifndef Q_OS_ANDROID
+ s_vulkanInstance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+#else
+ s_vulkanInstance->setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+#endif
+ }
+ s_vulkanInstance->setExtensions(QByteArrayList()
+ << "VK_KHR_get_physical_device_properties2");
+ if (!s_vulkanInstance->create()) {
+ qWarning("Failed to create Vulkan instance");
+ delete s_vulkanInstance;
+ s_vulkanInstance = nullptr;
+ }
+ }
+ return s_vulkanInstance;
+#else
+ return nullptr;
+#endif
+}
+
+void QSGRhiSupport::cleanup()
+{
+#if QT_CONFIG(vulkan)
+ delete s_vulkanInstance;
+ s_vulkanInstance = nullptr;
+#endif
+}
+
+QSGRhiSupport::QSGRhiSupport()
+ : m_set(false),
+ m_enableRhi(false),
+ m_debugLayer(false),
+ m_profile(false),
+ m_shaderEffectDebug(false)
+{
+}
+
+void QSGRhiSupport::applySettings()
+{
+ m_set = true;
+
+ if (m_requested.valid) {
+ // explicit rhi backend request from C++ (e.g. via QQuickWindow)
+ m_enableRhi = m_requested.rhi;
+ switch (m_requested.api) {
+ case QSGRendererInterface::OpenGLRhi:
+ m_rhiBackend = QRhi::OpenGLES2;
+ break;
+ case QSGRendererInterface::Direct3D11Rhi:
+ m_rhiBackend = QRhi::D3D11;
+ break;
+ case QSGRendererInterface::VulkanRhi:
+ m_rhiBackend = QRhi::Vulkan;
+ break;
+ case QSGRendererInterface::MetalRhi:
+ m_rhiBackend = QRhi::Metal;
+ break;
+ case QSGRendererInterface::NullRhi:
+ m_rhiBackend = QRhi::Null;
+ break;
+ default:
+ Q_ASSERT_X(false, "QSGRhiSupport", "Internal error: unhandled GraphicsApi type");
+ break;
+ }
+ } else {
+ // check env.vars., fall back to platform-specific defaults when backend is not set
+ m_enableRhi = qEnvironmentVariableIntValue("QSG_RHI");
+ const QByteArray rhiBackend = qgetenv("QSG_RHI_BACKEND");
+ if (rhiBackend == QByteArrayLiteral("gl")
+ || rhiBackend == QByteArrayLiteral("gles2")
+ || rhiBackend == QByteArrayLiteral("opengl"))
+ {
+ m_rhiBackend = QRhi::OpenGLES2;
+ } else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) {
+ m_rhiBackend = QRhi::D3D11;
+ } else if (rhiBackend == QByteArrayLiteral("vulkan")) {
+ m_rhiBackend = QRhi::Vulkan;
+ } else if (rhiBackend == QByteArrayLiteral("metal")) {
+ m_rhiBackend = QRhi::Metal;
+ } else if (rhiBackend == QByteArrayLiteral("null")) {
+ m_rhiBackend = QRhi::Null;
+ } else {
+#if defined(Q_OS_WIN)
+ m_rhiBackend = QRhi::D3D11;
+#elif defined(Q_OS_DARWIN)
+ m_rhiBackend = QRhi::Metal;
+#else
+ m_rhiBackend = QRhi::OpenGLES2;
+#endif
+ // Vulkan has to be requested explicitly
+ }
+ }
+
+ if (!m_enableRhi)
+ return;
+
+ // validation layers (Vulkan) or debug layer (D3D)
+ m_debugLayer = qEnvironmentVariableIntValue("QSG_RHI_DEBUG_LAYER");
+
+ // EnableProfiling + DebugMarkers
+ m_profile = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
+
+ m_shaderEffectDebug = qEnvironmentVariableIntValue("QSG_RHI_SHADEREFFECT_DEBUG");
+
+ const char *backendName = "unknown";
+ switch (m_rhiBackend) {
+ case QRhi::Null:
+ backendName = "Null";
+ break;
+ case QRhi::Vulkan:
+ backendName = "Vulkan";
+ break;
+ case QRhi::OpenGLES2:
+ backendName = "OpenGL";
+ break;
+ case QRhi::D3D11:
+ backendName = "D3D11";
+ break;
+ case QRhi::Metal:
+ backendName = "Metal";
+ break;
+ default:
+ break;
+ }
+ qDebug("Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
+ backendName, m_debugLayer, m_profile);
+}
+
+QSGRhiSupport *QSGRhiSupport::staticInst()
+{
+ static QSGRhiSupport inst;
+ return &inst;
+}
+
+void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
+{
+ Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
+ QSGRhiSupport *inst = staticInst();
+ if (inst->m_set) {
+ qWarning("QRhi is already configured, request ignored");
+ return;
+ }
+ inst->m_requested.valid = true;
+ inst->m_requested.api = api;
+ inst->m_requested.rhi = true;
+ inst->applySettings();
+}
+
+QSGRhiSupport *QSGRhiSupport::instance()
+{
+ QSGRhiSupport *inst = staticInst();
+ if (!inst->m_set)
+ inst->applySettings();
+ return inst;
+}
+
+QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
+{
+ if (!m_enableRhi)
+ return QSGRendererInterface::OpenGL;
+
+ switch (m_rhiBackend) {
+ case QRhi::Null:
+ return QSGRendererInterface::NullRhi;
+ case QRhi::Vulkan:
+ return QSGRendererInterface::VulkanRhi;
+ case QRhi::OpenGLES2:
+ return QSGRendererInterface::OpenGLRhi;
+ case QRhi::D3D11:
+ return QSGRendererInterface::Direct3D11Rhi;
+ case QRhi::Metal:
+ return QSGRendererInterface::MetalRhi;
+ default:
+ return QSGRendererInterface::Unknown;
+ }
+}
+
+QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
+{
+ if (!m_enableRhi)
+ return QSurface::OpenGLSurface;
+
+ switch (m_rhiBackend) {
+ case QRhi::Vulkan:
+ return QSurface::VulkanSurface;
+ case QRhi::OpenGLES2:
+ return QSurface::OpenGLSurface;
+ case QRhi::D3D11:
+ return QSurface::OpenGLSurface; // yup, OpenGLSurface
+ case QRhi::Metal:
+ return QSurface::MetalSurface;
+ default:
+ return QSurface::OpenGLSurface;
+ }
+}
+
+#if QT_CONFIG(vulkan)
+static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
+ const QRhiNativeHandles *cbNat)
+{
+ const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat);
+ const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat =
+ static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat);
+
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return &vknat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return &vknat->gfxQueue;
+ case QSGRendererInterface::CommandListResource:
+ if (maybeVkCbNat)
+ return &maybeVkCbNat->commandBuffer;
+ else
+ return nullptr;
+ case QSGRendererInterface::PhysicalDeviceResource:
+ return &vknat->physDev;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#if QT_CONFIG(opengl)
+static const void *qsgrhi_gl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiGles2NativeHandles *glnat = static_cast<const QRhiGles2NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::OpenGLContextResource:
+ return glnat->context;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#ifdef Q_OS_WIN
+static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiD3D11NativeHandles *d3dnat = static_cast<const QRhiD3D11NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return d3dnat->dev;
+ case QSGRendererInterface::DeviceContextResource:
+ return d3dnat->context;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#ifdef Q_OS_DARWIN
+static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
+ const QRhiNativeHandles *cbNat)
+{
+ const QRhiMetalNativeHandles *mtlnat = static_cast<const QRhiMetalNativeHandles *>(nat);
+ const QRhiMetalCommandBufferNativeHandles *maybeMtlCbNat =
+ static_cast<const QRhiMetalCommandBufferNativeHandles *>(cbNat);
+
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return mtlnat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return mtlnat->cmdQueue;
+ case QSGRendererInterface::CommandListResource:
+ if (maybeMtlCbNat)
+ return maybeMtlCbNat->commandBuffer;
+ else
+ return nullptr;
+ case QSGRendererInterface::CommandEncoderResource:
+ if (maybeMtlCbNat)
+ return maybeMtlCbNat->encoder;
+ else
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, const QSGDefaultRenderContext *rc)
+{
+ QRhi *rhi = rc->rhi();
+ if (res == QSGRendererInterface::RhiResource || !rhi)
+ return rhi;
+
+ const QRhiNativeHandles *nat = rhi->nativeHandles();
+ if (!nat)
+ return nullptr;
+
+ switch (m_rhiBackend) {
+#if QT_CONFIG(vulkan)
+ case QRhi::Vulkan:
+ {
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ return qsgrhi_vk_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr);
+ }
+#endif
+#if QT_CONFIG(opengl)
+ case QRhi::OpenGLES2:
+ return qsgrhi_gl_rifResource(res, nat);
+#endif
+#ifdef Q_OS_WIN
+ case QRhi::D3D11:
+ return qsgrhi_d3d11_rifResource(res, nat);
+#endif
+#ifdef Q_OS_DARWIN
+ case QRhi::Metal:
+ {
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ return qsgrhi_mtl_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr);
+ }
+#endif
+ default:
+ return nullptr;
+ }
+}
+
+int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
+{
+ int msaaSampleCount = qMax(QSurfaceFormat::defaultFormat().samples(), window->requestedFormat().samples());
+ if (qEnvironmentVariableIsSet("QSG_SAMPLES"))
+ msaaSampleCount = qEnvironmentVariableIntValue("QSG_SAMPLES");
+ msaaSampleCount = qMax(1, msaaSampleCount);
+ if (msaaSampleCount > 1) {
+ const QVector<int> supportedSampleCounts = rhi->supportedSampleCounts();
+ if (!supportedSampleCounts.contains(msaaSampleCount)) {
+ int reducedSampleCount = 1;
+ for (int i = supportedSampleCounts.count() - 1; i >= 0; --i) {
+ if (supportedSampleCounts[i] <= msaaSampleCount) {
+ reducedSampleCount = supportedSampleCounts[i];
+ break;
+ }
+ }
+ qWarning() << "Requested MSAA sample count" << msaaSampleCount
+ << "but supported sample counts are" << supportedSampleCounts
+ << ", using sample count" << reducedSampleCount << "instead";
+ msaaSampleCount = reducedSampleCount;
+ }
+ }
+ return msaaSampleCount;
+}
+
+// must be called on the main thread
+QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window)
+{
+ QOffscreenSurface *offscreenSurface = nullptr;
+#if QT_CONFIG(opengl)
+ if (rhiBackend() == QRhi::OpenGLES2) {
+ const QSurfaceFormat format = window->requestedFormat();
+ offscreenSurface = QRhiGles2InitParams::newFallbackSurface(format);
+ }
+#else
+ Q_UNUSED(window);
+#endif
+ return offscreenSurface;
+}
+
+// must be called on the render thread
+QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurface)
+{
+ QRhi *rhi = nullptr;
+
+ QRhi::Flags flags = 0;
+ if (isProfilingRequested())
+ flags |= QRhi::EnableProfiling | QRhi::EnableDebugMarkers;
+
+ QRhi::Implementation backend = rhiBackend();
+ if (backend == QRhi::Null) {
+ QRhiNullInitParams rhiParams;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#if QT_CONFIG(opengl)
+ if (backend == QRhi::OpenGLES2) {
+ const QSurfaceFormat format = window->requestedFormat();
+ QRhiGles2InitParams rhiParams;
+ rhiParams.format = format;
+ rhiParams.fallbackSurface = offscreenSurface;
+ rhiParams.window = window;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#if QT_CONFIG(vulkan)
+ if (backend == QRhi::Vulkan) {
+ QRhiVulkanInitParams rhiParams;
+ rhiParams.inst = window->vulkanInstance();
+ if (!rhiParams.inst)
+ qWarning("No QVulkanInstance set for QQuickWindow, this is wrong.");
+ rhiParams.window = window;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#ifdef Q_OS_WIN
+ if (backend == QRhi::D3D11) {
+ QRhiD3D11InitParams rhiParams;
+ rhiParams.enableDebugLayer = isDebugLayerRequested();
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#ifdef Q_OS_DARWIN
+ if (backend == QRhi::Metal) {
+ QRhiMetalInitParams rhiParams;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+
+ if (!rhi)
+ qWarning("Failed to create RHI (backend %d)", backend);
+
+ return rhi;
+}
+
+QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain)
+{
+ Q_ASSERT(rhi->isRecordingFrame());
+
+ QRhiReadbackResult result;
+ QRhiReadbackDescription readbackDesc; // read from swapchain backbuffer
+ QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
+ resourceUpdates->readBackTexture(readbackDesc, &result);
+
+ swapchain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+ rhi->finish(); // make sure the readback has finished, stall the pipeline if needed
+
+ // May be RGBA or BGRA. Plus premultiplied alpha.
+ QImage::Format imageFormat;
+ if (result.format == QRhiTexture::BGRA8) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ imageFormat = QImage::Format_ARGB32_Premultiplied;
+#else
+ imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ // ### and should swap too
+#endif
+ } else {
+ imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ }
+
+ const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
+ const QImage img(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat);
+
+ if (rhi->isYUpInFramebuffer())
+ return img.mirrored();
+
+ return img.copy();
+}
+
+QSGRhiProfileConnection *QSGRhiProfileConnection::instance()
+{
+ static QSGRhiProfileConnection inst;
+ return &inst;
+}
+
+void QSGRhiProfileConnection::initialize(QRhi *rhi)
+{
+#ifdef RHI_REMOTE_PROFILER
+ const QString profHost = qEnvironmentVariable("QSG_RHI_PROFILE_HOST");
+ if (!profHost.isEmpty()) {
+ int profPort = qEnvironmentVariableIntValue("QSG_RHI_PROFILE_PORT");
+ if (!profPort)
+ profPort = 30667;
+ qDebug("Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
+ m_profConn.reset(new QTcpSocket);
+ QObject::connect(m_profConn.data(), QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), m_profConn.data(),
+ [this](QAbstractSocket::SocketError socketError) { qDebug(" RHI profiler error: %d (%s)",
+ socketError, qPrintable(m_profConn->errorString())); });
+ m_profConn->connectToHost(profHost, profPort);
+ m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below
+ rhi->profiler()->setDevice(m_profConn.data());
+ m_lastMemStatWrite.start();
+ }
+#else
+ Q_UNUSED(rhi);
+#endif
+}
+
+void QSGRhiProfileConnection::cleanup()
+{
+#ifdef RHI_REMOTE_PROFILER
+ m_profConn.reset();
+#endif
+}
+
+void QSGRhiProfileConnection::send(QRhi *rhi)
+{
+#ifdef RHI_REMOTE_PROFILER
+ if (m_profConn) {
+ // do this every 5 sec at most
+ if (m_lastMemStatWrite.elapsed() >= 5000) {
+ rhi->profiler()->addVMemAllocatorStats();
+ m_lastMemStatWrite.restart();
+ }
+ }
+#else
+ Q_UNUSED(rhi);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
new file mode 100644
index 0000000000..48ad2f05ae
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRHISUPPORT_P_H
+#define QSGRHISUPPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgrenderloop_p.h"
+#include "qsgrendererinterface.h"
+
+#include <QtGui/private/qrhi_p.h>
+
+#include <QtGui/private/qrhinull_p.h>
+
+#if QT_CONFIG(opengl)
+#include <QtGui/private/qrhigles2_p.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qrhivulkan_p.h>
+#endif
+
+#ifdef Q_OS_WIN
+#include <QtGui/private/qrhid3d11_p.h>
+#endif
+
+#ifdef Q_OS_DARWIN
+#include <QtGui/private/qrhimetal_p.h>
+#endif
+
+#if QT_CONFIG(qml_network)
+#define RHI_REMOTE_PROFILER
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtGui/private/qrhiprofiler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QVulkanInstance;
+
+// Opting in/out of QRhi and choosing the default/requested backend is managed
+// by this singleton. This is because this information may be needed before
+// creating a render loop. A well-written render loop sets up its QRhi and
+// related machinery based on the settings queriable from here.
+//
+// cleanup() must be called to perform global (not per thread) cleanup, such
+// as, destroying the QVulkanInstance (if one was created in vulkanInstance()).
+//
+// In addition, the class provides handy conversion and query stuff for the
+// renderloop and the QSGRendererInterface implementations.
+//
+class QSGRhiSupport
+{
+public:
+ static void configure(QSGRendererInterface::GraphicsApi api);
+ static QSGRhiSupport *instance();
+ static QVulkanInstance *vulkanInstance();
+ void cleanup();
+
+ bool isRhiEnabled() const { return m_enableRhi; }
+ QRhi::Implementation rhiBackend() const { return m_rhiBackend; }
+ QSGRendererInterface::GraphicsApi graphicsApi() const;
+
+ bool isDebugLayerRequested() const { return m_debugLayer; }
+ bool isProfilingRequested() const { return m_profile; }
+ bool isShaderEffectDebuggingRequested() const { return m_shaderEffectDebug; }
+
+ QSurface::SurfaceType windowSurfaceType() const;
+
+ const void *rifResource(QSGRendererInterface::Resource res, const QSGDefaultRenderContext *rc);
+
+ int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi);
+
+ QOffscreenSurface *maybeCreateOffscreenSurface(QWindow *window);
+ QRhi *createRhi(QWindow *window, QOffscreenSurface *offscreenSurface);
+
+ QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain);
+
+private:
+ QSGRhiSupport();
+ void applySettings();
+ static QSGRhiSupport *staticInst();
+ struct {
+ bool valid = false;
+ QSGRendererInterface::GraphicsApi api;
+ uint rhi : 1;
+ } m_requested;
+ QRhi::Implementation m_rhiBackend = QRhi::Null;
+ uint m_set : 1;
+ uint m_enableRhi : 1;
+ uint m_debugLayer : 1;
+ uint m_profile : 1;
+ uint m_shaderEffectDebug : 1;
+};
+
+// Sends QRhi resource statistics over a QTcpSocket. To be initialized by the
+// renderloop when QSGRhiSupport::isProfilingRequested() is true. Will not do
+// anything unless extra env vars (QSG_RHI_PROFILE_HOST) are set.
+class QSGRhiProfileConnection
+{
+public:
+ static QSGRhiProfileConnection *instance();
+ void initialize(QRhi *rhi);
+ void cleanup();
+ void send(QRhi *rhi);
+
+private:
+#ifdef RHI_REMOTE_PROFILER
+ QScopedPointer<QTcpSocket> m_profConn;
+ QElapsedTimer m_lastMemStatWrite;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHISUPPORT_P_H
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
new file mode 100644
index 0000000000..f181d101c6
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhitextureglyphcache_p.h"
+#include <qrgb.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QImageTextureGlyphCache(format, matrix),
+ m_rhi(rhi)
+{
+ // Some OpenGL implementations, for instance macOS, have issues with
+ // GL_ALPHA render targets. Similarly, BGRA may be problematic on GLES 2.0.
+ // So stick with plain image uploads on GL.
+ m_resizeWithTextureCopy = m_rhi->backend() != QRhi::OpenGLES2;
+}
+
+QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache()
+{
+ if (m_resourceUpdates)
+ m_resourceUpdates->release();
+
+ delete m_texture;
+
+ // should be empty, but just in case
+ qDeleteAll(m_pendingDispose);
+}
+
+QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format)
+{
+ QRhiTexture *t = m_rhi->newTexture(format, m_size, 1, QRhiTexture::UsedAsTransferSource);
+ if (!t->build()) {
+ qWarning("Failed to build new glyph cache texture of size %dx%d", m_size.width(), m_size.height());
+ return nullptr;
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ // The new texture must be cleared to 0 always, this cannot be avoided
+ // otherwise artifacts will occur around the glyphs.
+ QByteArray data;
+ if (format == QRhiTexture::RED_OR_ALPHA8)
+ data.fill(0, m_size.width() * m_size.height());
+ else
+ data.fill(0, m_size.width() * m_size.height() * 4);
+ QRhiTextureSubresourceUploadDescription subresDesc(data.constData(), data.size());
+ subresDesc.setSourceSize(m_size);
+ m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+
+ return t;
+}
+
+void QSGRhiTextureGlyphCache::createTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (!m_resizeWithTextureCopy)
+ QImageTextureGlyphCache::createTextureData(width, height);
+
+ m_size = QSize(width, height);
+}
+
+void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (m_size.width() >= width && m_size.height() >= height)
+ return;
+
+ m_size = QSize(width, height);
+
+ if (m_texture) {
+ QRhiTexture *t = createEmptyTexture(m_texture->format());
+ if (!t)
+ return;
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ if (m_resizeWithTextureCopy) {
+ m_resourceUpdates->copyTexture(t, m_texture);
+ } else {
+ QImageTextureGlyphCache::resizeTextureData(width, height);
+ QImage img = image();
+ prepareGlyphImage(&img);
+ QRhiTextureSubresourceUploadDescription subresDesc(img);
+ const QSize oldSize = m_texture->pixelSize();
+ subresDesc.setSourceSize(QSize(qMin(oldSize.width(), width), qMin(oldSize.height(), height)));
+ m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ m_pendingDispose.insert(m_texture);
+ m_texture = t;
+ }
+}
+
+void QSGRhiTextureGlyphCache::beginFillTexture()
+{
+ Q_ASSERT(m_uploads.isEmpty());
+}
+
+void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img)
+{
+ const int maskWidth = img->width();
+ const int maskHeight = img->height();
+ const bool supportsBgra = m_rhi->isTextureFormatSupported(QRhiTexture::BGRA8);
+ m_bgra = false;
+
+ if (img->format() == QImage::Format_Mono) {
+ *img = img->convertToFormat(QImage::Format_Grayscale8);
+ } else if (img->depth() == 32) {
+ if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) {
+ // We need to make the alpha component equal to the average of the RGB values.
+ // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ for (int y = 0; y < maskHeight; ++y) {
+ QRgb *src = (QRgb *) img->scanLine(y);
+ for (int x = 0; x < maskWidth; ++x) {
+ int r = qRed(src[x]);
+ int g = qGreen(src[x]);
+ int b = qBlue(src[x]);
+ int avg;
+ if (img->format() == QImage::Format_RGB32)
+ avg = (r + g + b + 1) / 3; // "+1" for rounding.
+ else // Format_ARGB_Premultiplied
+ avg = qAlpha(src[x]);
+
+ src[x] = qRgba(r, g, b, avg);
+#if Q_BYTE_ORDER != Q_BIG_ENDIAN
+ if (supportsBgra) {
+ m_bgra = true;
+ } else {
+ // swizzle the bits to accommodate for the RGBA upload.
+ src[x] = ARGB2RGBA(src[x]);
+ m_bgra = false;
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+void QSGRhiTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ QRhiTextureSubresourceUploadDescription subresDesc;
+ QImage mask;
+
+ if (!m_resizeWithTextureCopy) {
+ QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
+ mask = image();
+ subresDesc.setSourceTopLeft(QPoint(c.x, c.y));
+ subresDesc.setSourceSize(QSize(c.w, c.h));
+ } else {
+ mask = textureMapForGlyph(glyph, subPixelPosition);
+ }
+
+ prepareGlyphImage(&mask);
+
+ subresDesc.setImage(mask);
+ subresDesc.setDestinationTopLeft(QPoint(c.x, c.y));
+ m_uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+}
+
+void QSGRhiTextureGlyphCache::endFillTexture()
+{
+ if (m_uploads.isEmpty())
+ return;
+
+ if (!m_texture) {
+ QRhiTexture::Format texFormat;
+ if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB)
+ texFormat = m_bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8;
+ else // should be R8, but there is the OpenGL ES 2.0 nonsense
+ texFormat = QRhiTexture::RED_OR_ALPHA8;
+
+ m_texture = createEmptyTexture(texFormat);
+ if (!m_texture)
+ return;
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ m_resourceUpdates->uploadTexture(m_texture, m_uploads);
+ m_uploads.clear();
+}
+
+int QSGRhiTextureGlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QSGRhiTextureGlyphCache::maxTextureWidth() const
+{
+ return m_rhi->resourceLimit(QRhi::TextureSizeMax);
+}
+
+int QSGRhiTextureGlyphCache::maxTextureHeight() const
+{
+ if (!m_resizeWithTextureCopy)
+ return qMin(1024, m_rhi->resourceLimit(QRhi::TextureSizeMax));
+
+ return m_rhi->resourceLimit(QRhi::TextureSizeMax);
+}
+
+void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
+{
+ if (m_resourceUpdates) {
+ mergeInto->merge(m_resourceUpdates);
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+
+ // now let's assume the resource updates will be committed in this frame
+ for (QRhiTexture *t : m_pendingDispose)
+ t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe
+
+ m_pendingDispose.clear();
+}
+
+bool QSGRhiTextureGlyphCache::eightBitFormatIsAlphaSwizzled() const
+{
+ // return true when the shaders for 8-bit formats need .a instead of .r
+ // when sampling the texture
+ return !m_rhi->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
new file mode 100644
index 0000000000..1533beb162
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRHITEXTUREGLYPHCACHE_P_H
+#define QSGRHITEXTUREGLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtextureglyphcache_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGRhiTextureGlyphCache : public QImageTextureGlyphCache
+{
+public:
+ QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QSGRhiTextureGlyphCache();
+
+ void createTextureData(int width, int height) override;
+ void resizeTextureData(int width, int height) override;
+ void beginFillTexture() override;
+ void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override;
+ void endFillTexture() override;
+ int glyphPadding() const override;
+ int maxTextureWidth() const override;
+ int maxTextureHeight() const override;
+
+ QRhiTexture *texture() const { return m_texture; }
+ void commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto);
+
+ int width() const { return m_size.width(); }
+ int height() const { return m_size.height(); }
+
+ bool eightBitFormatIsAlphaSwizzled() const;
+
+private:
+ void prepareGlyphImage(QImage *img);
+ QRhiTexture *createEmptyTexture(QRhiTexture::Format format);
+
+ QRhi *m_rhi;
+ bool m_resizeWithTextureCopy;
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ QRhiTexture *m_texture = nullptr;
+ QSize m_size;
+ bool m_bgra = false;
+ QVector<QRhiTextureUploadEntry> m_uploads;
+ QSet<QRhiTexture *> m_pendingDispose;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHITEXTUREGLYPHCACHE_P_H
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index c18ba4226c..db8e17a8e6 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -57,6 +57,7 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include "qsgthreadedrenderloop_p.h"
+#include "qsgrhisupport_p.h"
#include <private/qquickanimatorcontroller_p.h>
#include <private/qquickprofiler_p.h>
@@ -66,6 +67,7 @@
#if QT_CONFIG(quick_shadereffect)
#include <private/qquickopenglshadereffectnode_p.h>
#endif
+#include <private/qsgrhishadereffectnode_p.h>
#include <private/qsgdefaultrendercontext_p.h>
/*
@@ -160,6 +162,10 @@ const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
// Passed by the window when there is a render job to run
const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+// When using the QRhi this is sent upon PlatformSurfaceAboutToBeDestroyed from
+// the event filter installed on the QQuickWindow.
+const QEvent::Type WM_ReleaseSwapchain = QEvent::Type(QEvent::User + 7);
+
template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window)
{
for (int i=0; i<list.size(); ++i) {
@@ -181,14 +187,14 @@ public:
class WMTryReleaseEvent : public WMWindowEvent
{
public:
- WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback)
+ WMTryReleaseEvent(QQuickWindow *win, bool destroy, bool needsFallbackSurface)
: WMWindowEvent(win, WM_TryRelease)
, inDestructor(destroy)
- , fallbackSurface(fallback)
+ , needsFallback(needsFallbackSurface)
{}
bool inDestructor;
- QOffscreenSurface *fallbackSurface;
+ bool needsFallback;
};
class WMSyncEvent : public WMWindowEvent
@@ -197,10 +203,12 @@ public:
WMSyncEvent(QQuickWindow *c, bool inExpose, bool force)
: WMWindowEvent(c, WM_RequestSync)
, size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
, syncInExpose(inExpose)
, forceRenderPass(force)
{}
QSize size;
+ float dpr;
bool syncInExpose;
bool forceRenderPass;
};
@@ -222,6 +230,12 @@ public:
QRunnable *job;
};
+class WMReleaseSwapchainEvent : public WMWindowEvent
+{
+public:
+ WMReleaseSwapchainEvent(QQuickWindow *c) : WMWindowEvent(c, WM_ReleaseSwapchain) { }
+};
+
class QSGRenderThreadEventQueue : public QQueue<QEvent *>
{
public:
@@ -271,6 +285,9 @@ public:
QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(nullptr)
+ , enableRhi(false)
+ , rhi(nullptr)
+ , offscreenSurface(nullptr)
, animatorDriver(nullptr)
, pendingUpdate(0)
, sleeping(false)
@@ -290,6 +307,7 @@ public:
~QSGRenderThread()
{
delete sgrc;
+ delete offscreenSurface;
}
void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
@@ -298,8 +316,8 @@ public:
bool event(QEvent *) override;
void run() override;
- void syncAndRender();
- void sync(bool inExpose);
+ void syncAndRender(QImage *grabImage = nullptr);
+ void sync(bool inExpose, bool inGrab);
void requestRepaint()
{
@@ -328,7 +346,10 @@ public:
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
+ bool enableRhi;
+ QRhi *rhi;
QSGDefaultRenderContext *sgrc;
+ QOffscreenSurface *offscreenSurface;
QAnimationDriver *animatorDriver;
@@ -347,6 +368,8 @@ public:
QQuickWindow *window; // Will be 0 when window is not exposed
QSize windowSize;
+ float dpr = 1;
+ int rhiSampleCount = 1;
// Local event queue stuff...
bool stopEventProcessing;
@@ -380,6 +403,7 @@ bool QSGRenderThread::event(QEvent *e)
stopEventProcessing = true;
window = se->window;
windowSize = se->size;
+ dpr = se->dpr;
pendingUpdate |= SyncRequest;
if (se->syncInExpose) {
@@ -399,8 +423,8 @@ bool QSGRenderThread::event(QEvent *e)
WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
if (!window || wme->inDestructor) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- setting exit flag and invalidating OpenGL");
- invalidateOpenGL(wme->window, wme->inDestructor, wme->fallbackSurface);
- active = gl;
+ invalidateOpenGL(wme->window, wme->inDestructor, wme->needsFallback ? offscreenSurface : nullptr);
+ active = gl || rhi;
Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down");
if (sleeping)
stopEventProcessing = true;
@@ -427,19 +451,25 @@ bool QSGRenderThread::event(QEvent *e)
Q_ASSERT(ce->window == window || !window);
mutex.lock();
if (ce->window) {
- gl->makeCurrent(ce->window);
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync scene graph");
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window);
- d->syncSceneGraph();
- sgrc->endSync();
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering scene graph");
- QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size());
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- grabbing result");
- bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255;
- *ce->image = qt_gl_read_framebuffer(windowSize * ce->window->effectiveDevicePixelRatio(), alpha, alpha);
+ const bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255;
+ const QSize readbackSize = windowSize * ce->window->effectiveDevicePixelRatio();
+ if (rhi) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ syncAndRender(ce->image);
+ } else {
+ gl->makeCurrent(ce->window);
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync scene graph");
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window);
+ d->syncSceneGraph();
+ sgrc->endSync();
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering scene graph");
+ QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size());
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- grabbing result");
+ *ce->image = qt_gl_read_framebuffer(readbackSize, alpha, alpha);
+ }
ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio());
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- waking gui to handle result");
@@ -453,7 +483,10 @@ bool QSGRenderThread::event(QEvent *e)
WMJobEvent *ce = static_cast<WMJobEvent *>(e);
Q_ASSERT(ce->window == window);
if (window) {
- gl->makeCurrent(window);
+ if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+ else
+ gl->makeCurrent(window);
ce->job->run();
delete ce->job;
ce->job = nullptr;
@@ -462,6 +495,21 @@ bool QSGRenderThread::event(QEvent *e)
return true;
}
+ case WM_ReleaseSwapchain: {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_ReleaseSwapchain");
+ WMReleaseSwapchainEvent *ce = static_cast<WMReleaseSwapchainEvent *>(e);
+ // forget about 'window' here that may be null when already unexposed
+ Q_ASSERT(ce->window);
+ mutex.lock();
+ if (ce->window) {
+ wm->releaseSwapchain(ce->window);
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- swapchain released");
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
case WM_RequestRepaint:
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestPaint");
// When GUI posts this event, it is followed by a polishAndSync, so we mustn't
@@ -479,7 +527,7 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
{
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateOpenGL()");
- if (!gl)
+ if (!gl && !rhi)
return;
if (!window) {
@@ -491,7 +539,12 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
bool wipeGL = inDestructor || (wipeSG && !window->isPersistentOpenGLContext());
- bool current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window));
+ bool current = true;
+ if (gl)
+ current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window));
+ else if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+
if (Q_UNLIKELY(!current)) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- cleanup without an OpenGL context");
}
@@ -499,16 +552,19 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
if (current)
QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
+#endif
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
dd->cleanupNodesOnShutdown();
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup");
- if (current)
+ if (current && gl)
gl->doneCurrent();
return;
}
@@ -518,13 +574,25 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
if (inDestructor)
delete dd->animationController;
- if (current)
+ if (current && gl)
gl->doneCurrent();
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidating scene graph");
if (wipeGL) {
+ if (dd->swapchain) {
+ if (window->handle()) {
+ // We get here when exiting via QCoreApplication::quit() instead of
+ // through QWindow::close().
+ wm->releaseSwapchain(window);
+ } else {
+ qWarning("QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
+ window, dd->swapchain);
+ }
+ }
delete gl;
gl = nullptr;
+ delete rhi;
+ rhi = nullptr;
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidated OpenGL");
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent GL, avoiding cleanup");
@@ -535,23 +603,41 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
Enters the mutex lock to make sure GUI is blocking and performs
sync, then wakes GUI.
*/
-void QSGRenderThread::sync(bool inExpose)
+void QSGRenderThread::sync(bool inExpose, bool inGrab)
{
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "sync()");
- mutex.lock();
+ if (!inGrab)
+ mutex.lock();
Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
- bool current = false;
- if (windowSize.width() > 0 && windowSize.height() > 0)
- current = gl->makeCurrent(window);
- // Check for context loss.
- if (!current && !gl->isValid()) {
- QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
- sgrc->invalidate();
- current = gl->create() && gl->makeCurrent(window);
- if (current)
- sgrc->initialize(gl);
+ bool current = true;
+ if (gl) {
+ if (windowSize.width() > 0 && windowSize.height() > 0)
+ current = gl->makeCurrent(window);
+ else
+ current = false;
+ // Check for context loss.
+ if (!current && !gl->isValid()) {
+ QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
+ sgrc->invalidate();
+ current = gl->create() && gl->makeCurrent(window);
+ if (current) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = windowSize * dpr;
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ }
+ } else {
+ // With the rhi making the (OpenGL) context current serves only one
+ // purpose: to enable external OpenGL rendering connected to one of
+ // the QQuickWindow signals (beforeSynchronizing, beforeRendering,
+ // etc.) to function like it did on the direct OpenGL path. For our
+ // own rendering this call would not be necessary.
+ rhi->makeThreadLocalNativeContextCurrent();
}
if (current) {
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
@@ -576,14 +662,14 @@ void QSGRenderThread::sync(bool inExpose)
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window has bad size, sync aborted");
}
- if (!inExpose) {
+ if (!inExpose && !inGrab) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync complete, waking Gui");
waitCondition.wakeOne();
mutex.unlock();
}
}
-void QSGRenderThread::syncAndRender()
+void QSGRenderThread::syncAndRender(QImage *grabImage)
{
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
if (profileFrames) {
@@ -600,14 +686,59 @@ void QSGRenderThread::syncAndRender()
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage;
- bool syncRequested = pendingUpdate & SyncRequest;
+ bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
+ bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ // Begin the frame before syncing -> sync is where we may invoke
+ // updatePaintNode() on the items and they may want to do resource updates.
+ // Also relevant for applications that connect to the before/afterSynchronizing
+ // signals and want to do graphics stuff already there.
+ if (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0) {
+ // always prefer what the surface tells us, not the QWindow
+ const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
+ // An update request could still be delivered right before we get an
+ // unexpose. With Vulkan on Windows for example attempting to render
+ // leads to failures at this stage since the surface size is already 0.
+ if (effectiveOutputSize.isEmpty())
+ return;
+
+ const QSize previousOutputSize = cd->swapchain->currentPixelSize();
+ if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
+ if (cd->swapchainJustBecameRenderable)
+ qDebug("just became exposed");
+ cd->swapchainJustBecameRenderable = false;
+ cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
+
+ cd->depthStencilForSwapchain->build();
+ cd->hasActiveSwapchain = cd->swapchain->buildOrResize();
+
+ cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
+ if (!cd->hasActiveSwapchain)
+ qWarning("Failed to build or resize swapchain");
+ else
+ qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ }
+
+ Q_ASSERT(rhi == cd->rhi);
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ qWarning("Device lost");
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to start frame");
+ // try again later
+ if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ return;
+ }
+ }
+
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
- sync(exposeRequested);
+ sync(exposeRequested, grabImage != nullptr);
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
@@ -616,41 +747,73 @@ void QSGRenderThread::syncAndRender()
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
- if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
- int waitTime = vsyncDelta - (int) waitTimer.elapsed();
- if (waitTime > 0)
- msleep(waitTime);
- return;
+ if (!syncResultedInChanges && !repaintRequested && sgrc->isValid() && !grabImage) {
+ if (gl || !rhi->isRecordingFrame()) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started");
- if (animatorDriver->isRunning()) {
+ if (animatorDriver->isRunning() && !grabImage) {
d->animationController->lock();
animatorDriver->advance();
d->animationController->unlock();
}
- bool current = false;
- if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0)
- current = gl->makeCurrent(window);
- // Check for context loss.
- if (!current && !gl->isValid()) {
- // Cannot do anything here because gui is not locked. Request a new
- // sync+render round on the gui thread and let the sync handle it.
- QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ bool current = true;
+ if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) {
+ if (gl)
+ current = gl->makeCurrent(window);
+ else if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+ } else {
+ current = false;
+ }
+ // Check for context loss (GL, RHI case handled after the beginFrame() above)
+ if (gl) {
+ if (!current && !gl->isValid()) {
+ // Cannot do anything here because gui is not locked. Request a new
+ // sync+render round on the gui thread and let the sync handle it.
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ }
}
if (current) {
+
d->renderSceneGraph(windowSize);
+
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopRender);
- if (!d->customRenderStage || !d->customRenderStage->swap())
- gl->swapBuffers(window);
- d->fireFrameSwapped();
+
+ // With the rhi grabs can only be done by adding a readback and then
+ // blocking in a real frame. The legacy GL path never gets here with
+ // grabs as it rather invokes sync/render directly without going
+ // through syncAndRender().
+ if (grabImage) {
+ Q_ASSERT(rhi && !gl && cd->swapchain);
+ *grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain);
+ }
+
+ if (cd->swapchain) {
+ QRhi::EndFrameFlags flags = 0;
+ if (grabImage)
+ flags |= QRhi::SkipPresent;
+ rhi->endFrame(cd->swapchain, flags);
+ } else {
+ if (!cd->customRenderStage || !cd->customRenderStage->swap())
+ gl->swapBuffers(window);
+ }
+
+ if (!grabImage)
+ d->fireFrameSwapped();
+
} else {
Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync, 1);
@@ -680,6 +843,8 @@ void QSGRenderThread::syncAndRender()
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSwap);
+
+ QSGRhiProfileConnection::instance()->send(rhi);
}
@@ -723,10 +888,56 @@ void QSGRenderThread::run()
QQuickProfiler::registerAnimationCallback();
while (active) {
-
if (window) {
- if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window))
- sgrc->initialize(gl);
+ if (enableRhi) {
+ if (!rhi) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+ if (rhi) {
+ rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
+ if (rhiSupport->isProfilingRequested())
+ QSGRhiProfileConnection::instance()->initialize(rhi); // ### this breaks down with multiple windows
+ } else {
+ qWarning("Failed to create QRhi on the render thread; scenegraph is not functional");
+ }
+ }
+ if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.rhi = rhi;
+ rcParams.sampleCount = rhiSampleCount;
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = windowSize * dpr;
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (rhi && !cd->swapchain) {
+ cd->rhi = rhi;
+ QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
+ cd->swapchain = rhi->newSwapChain();
+ cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(),
+ rhiSampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ cd->swapchain->setWindow(window);
+ cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
+ qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ cd->swapchain->setSampleCount(rhiSampleCount);
+ cd->swapchain->setFlags(flags);
+ cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
+ cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
+ }
+ } else {
+ if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window)) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = windowSize * dpr;
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ }
syncAndRender();
}
@@ -741,7 +952,7 @@ void QSGRenderThread::run()
}
}
- Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread...");
+ Q_ASSERT_X(!gl && !rhi, "QSGRenderThread::run()", "The graphics context should be cleaned up before exiting the render thread...");
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run() completed");
@@ -770,12 +981,15 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop()
QSGThreadedRenderLoop::~QSGThreadedRenderLoop()
{
+ qDeleteAll(pendingRenderContexts);
delete sg;
}
QSGRenderContext *QSGThreadedRenderLoop::createRenderContext(QSGContext *sg) const
{
- return sg->createRenderContext();
+ auto context = sg->createRenderContext();
+ pendingRenderContexts.insert(context);
+ return context;
}
void QSGThreadedRenderLoop::maybePostPolishRequest(Window *w)
@@ -908,12 +1122,48 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP) << "done windowDestroyed()" << window;
}
+void QSGThreadedRenderLoop::releaseSwapchain(QQuickWindow *window)
+{
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ delete wd->rpDescForSwapchain;
+ wd->rpDescForSwapchain = nullptr;
+ delete wd->swapchain;
+ wd->swapchain = nullptr;
+ delete wd->depthStencilForSwapchain;
+ wd->depthStencilForSwapchain = nullptr;
+ wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable = false;
+}
void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "exposureChanged()" << window;
+
+ // This is tricker than used to be. We want to detect having an empty
+ // surface size (which may be the case even when window->size() is
+ // non-empty, on some platforms with some graphics APIs!) as well as the
+ // case when the window just became "newly exposed" (e.g. after a
+ // minimize-restore on Windows, or when switching between fully obscured -
+ // not fully obscured on macOS)
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ if (!window->isExposed())
+ wd->hasRenderableSwapchain = false;
+
+ bool skipThisExpose = false;
+ if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
+ wd->hasRenderableSwapchain = false;
+ skipThisExpose = true;
+ }
+
+ if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
+ && !wd->swapchain->surfacePixelSize().isEmpty())
+ {
+ wd->hasRenderableSwapchain = true;
+ wd->swapchainJustBecameRenderable = true;
+ }
+
if (window->isExposed()) {
- handleExposure(window);
+ if (!skipThisExpose)
+ handleExposure(window);
} else {
Window *w = windowFor(m_windows, window);
if (w)
@@ -935,7 +1185,10 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
Window win;
win.window = window;
win.actualWindowFormat = window->format();
- win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context);
+ auto renderContext = QQuickWindowPrivate::get(window)->context;
+ // The thread assumes ownership, so we don't need to delete it later.
+ pendingRenderContexts.remove(renderContext);
+ win.thread = new QSGRenderThread(this, renderContext);
win.updateDuringSync = false;
win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
m_windows << win;
@@ -962,27 +1215,39 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
// Start render thread if it is not running
if (!w->thread->isRunning()) {
-
qCDebug(QSG_LOG_RENDERLOOP, "- starting render thread");
- if (!w->thread->gl) {
- w->thread->gl = new QOpenGLContext();
- if (qt_gl_global_share_context())
- w->thread->gl->setShareContext(qt_gl_global_share_context());
- w->thread->gl->setFormat(w->window->requestedFormat());
- w->thread->gl->setScreen(w->window->screen());
- if (!w->thread->gl->create()) {
- const bool isEs = w->thread->gl->isOpenGLES();
- delete w->thread->gl;
- w->thread->gl = nullptr;
- handleContextCreationFailure(w->window, isEs);
- return;
+ w->thread->enableRhi = QSGRhiSupport::instance()->isRhiEnabled();
+ if (w->thread->enableRhi) {
+ if (!w->thread->rhi) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
+ window->installEventFilter(this);
}
+ } else {
+ if (!w->thread->gl) {
+ w->thread->gl = new QOpenGLContext();
+ if (qt_gl_global_share_context())
+ w->thread->gl->setShareContext(qt_gl_global_share_context());
+ w->thread->gl->setFormat(w->window->requestedFormat());
+ w->thread->gl->setScreen(w->window->screen());
+ if (!w->thread->gl->create()) {
+ const bool isEs = w->thread->gl->isOpenGLES();
+ delete w->thread->gl;
+ w->thread->gl = nullptr;
+ handleContextCreationFailure(w->window, isEs);
+ return;
+ }
+
+ QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl);
- QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl);
+ w->thread->gl->moveToThread(w->thread);
+ qCDebug(QSG_LOG_RENDERLOOP, "- OpenGL context created");
- w->thread->gl->moveToThread(w->thread);
- qCDebug(QSG_LOG_RENDERLOOP, "- OpenGL context created");
+ w->thread->offscreenSurface = new QOffscreenSurface();
+ w->thread->offscreenSurface->setFormat(w->actualWindowFormat);
+ w->thread->offscreenSurface->create();
+ }
}
QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
@@ -1027,6 +1292,30 @@ void QSGThreadedRenderLoop::handleObscurity(Window *w)
startOrStopAnimationTimer();
}
+bool QSGThreadedRenderLoop::eventFilter(QObject *watched, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
+ if (window) {
+ Window *w = windowFor(m_windows, window);
+ if (w) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new WMReleaseSwapchainEvent(window));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+ window->removeEventFilter(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(watched, event);
+}
void QSGThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
{
@@ -1119,23 +1408,14 @@ void QSGThreadedRenderLoop::releaseResources(Window *w, bool inDestructor)
QQuickWindow *window = w->window;
// The platform window might have been destroyed before
- // hide/release/windowDestroyed is called, so we need to have a
- // fallback surface to perform the cleanup of the scene graph
- // and the OpenGL resources.
- // QOffscreenSurface must be created on the GUI thread, so we
- // create it here and pass it on to QSGRenderThread::invalidateGL()
- QOffscreenSurface *fallback = nullptr;
- if (!window->handle()) {
- qCDebug(QSG_LOG_RENDERLOOP, "- using fallback surface");
- fallback = new QOffscreenSurface();
- fallback->setFormat(w->actualWindowFormat);
- fallback->create();
- }
+ // hide/release/windowDestroyed is called, so we may need to have a
+ // fallback surface to perform the cleanup of the scene graph and the
+ // OpenGL resources. QOffscreenSurface must be created on the GUI
+ // thread so that is done for us already.
qCDebug(QSG_LOG_RENDERLOOP, "- posting release request to render thread");
- w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback));
+ w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, window->handle() == nullptr));
w->thread->waitCondition.wait(&w->thread->mutex);
- delete fallback;
// Avoid a shutdown race condition.
// If SG is invalidated and 'active' becomes false, the thread's run()
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index 32bfcb7148..e5e9fa8b48 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -121,9 +121,14 @@ private:
void handleExposure(QQuickWindow *w);
void handleObscurity(Window *w);
+ void releaseSwapchain(QQuickWindow *window);
+ bool eventFilter(QObject *watched, QEvent *event) override;
QSGContext *sg;
+ // Set of contexts that have been created but are now owned by
+ // a rendering thread yet, as the window has never been exposed.
+ mutable QSet<QSGRenderContext*> pendingRenderContexts;
QAnimationDriver *m_animation_driver;
QList<Window> m_windows;
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 95df700a15..220c70c299 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -62,6 +62,10 @@
QT_BEGIN_NAMESPACE
+// Single-threaded render loop with a custom animation driver. Like a
+// combination of basic+threaded but still working on the main thread. Only
+// compatible with direct OpenGL, no RHI support here.
+
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
#define RLDEBUG(x) qCDebug(QSG_LOG_RENDERLOOP, x)
@@ -188,8 +192,14 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window)
RLDEBUG(" - making current");
bool current = m_gl->makeCurrent(window);
RLDEBUG(" - initializing SG");
- if (current)
- m_rc->initialize(m_gl);
+ if (current) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, m_gl->format().samples());
+ rcParams.openGLContext = m_gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ m_rc->initialize(&rcParams);
+ }
}
WindowData data;
@@ -440,10 +450,16 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window)
if (!m_gl->isValid()) {
d->cleanupNodesOnShutdown();
m_rc->invalidate();
- if (m_gl->create() && m_gl->makeCurrent(window))
- m_rc->initialize(m_gl);
- else
+ if (m_gl->create() && m_gl->makeCurrent(window)) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, m_gl->format().samples());
+ rcParams.openGLContext = m_gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ m_rc->initialize(&rcParams);
+ } else {
return;
+ }
}
}
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index ddd7fb7f4c..3390d2b87a 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -4,7 +4,11 @@
HEADERS += \
$$PWD/coreapi/qsggeometry.h \
$$PWD/coreapi/qsgmaterial.h \
+ $$PWD/coreapi/qsgmaterialtype.h \
+ $$PWD/coreapi/qsgmaterialshader.h \
$$PWD/coreapi/qsgmaterialshader_p.h \
+ $$PWD/coreapi/qsgmaterialrhishader.h \
+ $$PWD/coreapi/qsgmaterialrhishader_p.h \
$$PWD/coreapi/qsgnode.h \
$$PWD/coreapi/qsgnode_p.h \
$$PWD/coreapi/qsgnodeupdater_p.h \
@@ -14,12 +18,17 @@ HEADERS += \
$$PWD/coreapi/qsgrendernode.h \
$$PWD/coreapi/qsgrendernode_p.h \
$$PWD/coreapi/qsgrendererinterface.h \
- $$PWD/coreapi/qsggeometry_p.h
+ $$PWD/coreapi/qsggeometry_p.h \
+ $$PWD/coreapi/qsgtexture.h \
+ $$PWD/coreapi/qsgtexture_p.h
SOURCES += \
$$PWD/coreapi/qsgabstractrenderer.cpp \
$$PWD/coreapi/qsggeometry.cpp \
$$PWD/coreapi/qsgmaterial.cpp \
+ $$PWD/coreapi/qsgmaterialshader.cpp \
+ $$PWD/coreapi/qsgmaterialrhishader.cpp \
+ $$PWD/coreapi/qsgtexture.cpp \
$$PWD/coreapi/qsgnode.cpp \
$$PWD/coreapi/qsgnodeupdater.cpp \
$$PWD/coreapi/qsgrenderer.cpp \
@@ -39,10 +48,9 @@ HEADERS += \
$$PWD/util/qsgareaallocator_p.h \
$$PWD/util/qsgengine.h \
$$PWD/util/qsgengine_p.h \
+ $$PWD/util/qsgplaintexture_p.h \
$$PWD/util/qsgsimplerectnode.h \
$$PWD/util/qsgsimpletexturenode.h \
- $$PWD/util/qsgtexture.h \
- $$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider.h \
$$PWD/util/qsgflatcolormaterial.h \
$$PWD/util/qsgsimplematerial.h \
@@ -56,9 +64,9 @@ HEADERS += \
SOURCES += \
$$PWD/util/qsgareaallocator.cpp \
$$PWD/util/qsgengine.cpp \
+ $$PWD/util/qsgplaintexture.cpp \
$$PWD/util/qsgsimplerectnode.cpp \
$$PWD/util/qsgsimpletexturenode.cpp \
- $$PWD/util/qsgtexture.cpp \
$$PWD/util/qsgtextureprovider.cpp \
$$PWD/util/qsgflatcolormaterial.cpp \
$$PWD/util/qsgsimplematerial.cpp \
@@ -72,13 +80,29 @@ qtConfig(opengl(es1|es2)?) {
HEADERS += \
$$PWD/util/qsgdepthstencilbuffer_p.h \
$$PWD/util/qsgshadersourcebuilder_p.h \
- $$PWD/util/qsgatlastexture_p.h
+ $$PWD/util/qsgopenglatlastexture_p.h
SOURCES += \
$$PWD/util/qsgdepthstencilbuffer.cpp \
- $$PWD/util/qsgatlastexture.cpp \
+ $$PWD/util/qsgopenglatlastexture.cpp \
$$PWD/util/qsgshadersourcebuilder.cpp
-}
+ # rhi, still tied to OpenGL-enabled Qt builds for now
+ HEADERS += \
+ $$PWD/qsgrhisupport_p.h \
+ $$PWD/qsgrhitextureglyphcache_p.h \
+ $$PWD/util/qsgrhiatlastexture_p.h \
+ $$PWD/qsgrhilayer_p.h \
+ $$PWD/qsgrhishadereffectnode_p.h \
+ $$PWD/qsgrhidistancefieldglyphcache_p.h
+
+ SOURCES += \
+ $$PWD/qsgrhisupport.cpp \
+ $$PWD/qsgrhitextureglyphcache.cpp \
+ $$PWD/qsgrhilayer.cpp \
+ $$PWD/qsgrhishadereffectnode.cpp \
+ $$PWD/util/qsgrhiatlastexture.cpp \
+ $$PWD/qsgrhidistancefieldglyphcache.cpp
+}
# QML / Adaptations API
HEADERS += \
@@ -103,7 +127,7 @@ qtConfig(opengl(es1|es2)?) {
SOURCES += \
$$PWD/qsgdefaultglyphnode.cpp \
$$PWD/qsgdefaultglyphnode_p.cpp \
- $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
+ $$PWD/qsgopengldistancefieldglyphcache.cpp \
$$PWD/qsgdistancefieldglyphnode.cpp \
$$PWD/qsgdistancefieldglyphnode_p.cpp \
$$PWD/qsgdefaultinternalimagenode.cpp \
@@ -114,11 +138,11 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultrectanglenode.cpp \
$$PWD/util/qsgdefaultimagenode.cpp \
$$PWD/util/qsgdefaultninepatchnode.cpp \
- $$PWD/qsgdefaultlayer.cpp \
+ $$PWD/qsgopengllayer.cpp \
$$PWD/qsgwindowsrenderloop.cpp
HEADERS += \
$$PWD/qsgdefaultglyphnode_p.h \
- $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
+ $$PWD/qsgopengldistancefieldglyphcache_p.h \
$$PWD/qsgdistancefieldglyphnode_p.h \
$$PWD/qsgdistancefieldglyphnode_p_p.h \
$$PWD/qsgdefaultglyphnode_p_p.h \
@@ -130,7 +154,7 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultrectanglenode_p.h \
$$PWD/util/qsgdefaultimagenode_p.h \
$$PWD/util/qsgdefaultninepatchnode_p.h \
- $$PWD/qsgdefaultlayer_p.h \
+ $$PWD/qsgopengllayer_p.h \
$$PWD/qsgwindowsrenderloop_p.h
qtConfig(thread) {
diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc
index 0687530be1..c7257c17a0 100644
--- a/src/quick/scenegraph/scenegraph.qrc
+++ b/src/quick/scenegraph/scenegraph.qrc
@@ -72,5 +72,54 @@
<file>shaders/sprite.vert</file>
<file>shaders/sprite_core.frag</file>
<file>shaders/sprite_core.vert</file>
+
+ <file>shaders_ng/vertexcolor.vert.qsb</file>
+ <file>shaders_ng/vertexcolor.frag.qsb</file>
+ <file>shaders_ng/flatcolor.vert.qsb</file>
+ <file>shaders_ng/flatcolor.frag.qsb</file>
+ <file>shaders_ng/smoothcolor.vert.qsb</file>
+ <file>shaders_ng/smoothcolor.frag.qsb</file>
+ <file>shaders_ng/stencilclip.vert.qsb</file>
+ <file>shaders_ng/stencilclip.frag.qsb</file>
+ <file>shaders_ng/texture.vert.qsb</file>
+ <file>shaders_ng/texture.frag.qsb</file>
+ <file>shaders_ng/opaquetexture.vert.qsb</file>
+ <file>shaders_ng/opaquetexture.frag.qsb</file>
+ <file>shaders_ng/smoothtexture.vert.qsb</file>
+ <file>shaders_ng/smoothtexture.frag.qsb</file>
+
+ <file>shaders_ng/textmask.vert.qsb</file>
+ <file>shaders_ng/textmask.frag.qsb</file>
+ <file>shaders_ng/8bittextmask.frag.qsb</file>
+ <file>shaders_ng/8bittextmask_a.frag.qsb</file>
+ <file>shaders_ng/24bittextmask.frag.qsb</file>
+ <file>shaders_ng/32bitcolortext.frag.qsb</file>
+ <file>shaders_ng/outlinedtext.vert.qsb</file>
+ <file>shaders_ng/outlinedtext.frag.qsb</file>
+ <file>shaders_ng/outlinedtext_a.frag.qsb</file>
+ <file>shaders_ng/styledtext.vert.qsb</file>
+ <file>shaders_ng/styledtext.frag.qsb</file>
+ <file>shaders_ng/styledtext_a.frag.qsb</file>
+
+ <file>shaders_ng/distancefieldtext.vert.qsb</file>
+ <file>shaders_ng/distancefieldtext.frag.qsb</file>
+ <file>shaders_ng/distancefieldtext_a.frag.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext.vert.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext.frag.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext_a.frag.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext.vert.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext.frag.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext_a.frag.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext.vert.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext.frag.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb</file>
+
+ <file>shaders_ng/shadereffect.vert.qsb</file>
+ <file>shaders_ng/shadereffect.frag.qsb</file>
+ <file>shaders_ng/sprite.vert.qsb</file>
+ <file>shaders_ng/sprite.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
new file mode 100644
index 0000000000..bc3826a924
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ vec4 glyph = texture(_qt_texture, sampleCoord);
+ fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
new file mode 100644
index 0000000000..b16da4d76a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
new file mode 100644
index 0000000000..63e445f90b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = texture(_qt_texture, sampleCoord) * ubuf.color.a;
+}
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
new file mode 100644
index 0000000000..1a12a35b49
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
new file mode 100644
index 0000000000..6304e821ff
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * texture(_qt_texture, sampleCoord).r;
+}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
new file mode 100644
index 0000000000..2d0d23d813
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
new file mode 100644
index 0000000000..0d0fa1cd3a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
new file mode 100644
index 0000000000..65d9af4736
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/compile.bat b/src/quick/scenegraph/shaders_ng/compile.bat
new file mode 100755
index 0000000000..8ce42f3483
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/compile.bat
@@ -0,0 +1,84 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Copyright (C) 2019 The Qt Company Ltd.
+:: Contact: https://www.qt.io/licensing/
+::
+:: This file is part of the QtQuick module of the Qt Toolkit.
+::
+:: $QT_BEGIN_LICENSE:LGPL$
+:: Commercial License Usage
+:: Licensees holding valid commercial Qt licenses may use this file in
+:: accordance with the commercial license agreement provided with the
+:: Software or, alternatively, in accordance with the terms contained in
+:: a written agreement between you and The Qt Company. For licensing terms
+:: and conditions see https://www.qt.io/terms-conditions. For further
+:: information use the contact form at https://www.qt.io/contact-us.
+::
+:: GNU Lesser General Public License Usage
+:: Alternatively, this file may be used under the terms of the GNU Lesser
+:: General Public License version 3 as published by the Free Software
+:: Foundation and appearing in the file LICENSE.LGPL3 included in the
+:: packaging of this file. Please review the following information to
+:: ensure the GNU Lesser General Public License version 3 requirements
+:: will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+::
+:: GNU General Public License Usage
+:: Alternatively, this file may be used under the terms of the GNU
+:: General Public License version 2.0 or (at your option) the GNU General
+:: Public license version 3 or any later version approved by the KDE Free
+:: Qt Foundation. The licenses are as published by the Free Software
+:: Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+:: included in the packaging of this file. Please review the following
+:: information to ensure the GNU General Public License requirements will
+:: be met: https://www.gnu.org/licenses/gpl-2.0.html and
+:: https://www.gnu.org/licenses/gpl-3.0.html.
+::
+:: $QT_END_LICENSE$
+::
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o vertexcolor.vert.qsb vertexcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o vertexcolor.frag.qsb vertexcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o flatcolor.vert.qsb flatcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o flatcolor.frag.qsb flatcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothcolor.vert.qsb smoothcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothcolor.frag.qsb smoothcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o stencilclip.vert.qsb stencilclip.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o stencilclip.frag.qsb stencilclip.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o texture.vert.qsb texture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o texture.frag.qsb texture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o opaquetexture.vert.qsb opaquetexture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o opaquetexture.frag.qsb opaquetexture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothtexture.vert.qsb smoothtexture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothtexture.frag.qsb smoothtexture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o textmask.vert.qsb textmask.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o textmask.frag.qsb textmask.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 8bittextmask.frag.qsb 8bittextmask.frag
+qsb --glsl "150,120,100 es" -o 8bittextmask_a.frag.qsb 8bittextmask_a.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 24bittextmask.frag.qsb 24bittextmask.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 32bitcolortext.frag.qsb 32bitcolortext.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o outlinedtext.vert.qsb outlinedtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o outlinedtext.frag.qsb outlinedtext.frag
+qsb --glsl "150,120,100 es" -o outlinedtext_a.frag.qsb outlinedtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o styledtext.vert.qsb styledtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o styledtext.frag.qsb styledtext.frag
+qsb --glsl "150,120,100 es" -o styledtext_a.frag.qsb styledtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext.vert.qsb distancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext.frag.qsb distancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext_a.frag.qsb distancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext.vert.qsb distancefieldshiftedtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext.frag.qsb distancefieldshiftedtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext_a.frag.qsb distancefieldshiftedtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext.vert.qsb distancefieldoutlinetext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext.frag.qsb distancefieldoutlinetext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext_a.frag.qsb distancefieldoutlinetext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext.vert.qsb hiqsubpixeldistancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext.frag.qsb hiqsubpixeldistancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext_a.frag.qsb hiqsubpixeldistancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext.vert.qsb loqsubpixeldistancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext.frag.qsb loqsubpixeldistancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext_a.frag.qsb loqsubpixeldistancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.vert.qsb shadereffect.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.frag.qsb shadereffect.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.vert.qsb sprite.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.frag.qsb sprite.frag
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
new file mode 100644
index 0000000000..c8c1ac89dc
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
@@ -0,0 +1,25 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+void main()
+{
+ float d = texture(_qt_texture, sampleCoord).r;
+ fragColor = mix(ubuf.styleColor, ubuf.color, smoothstep(ubuf.alphaMin, ubuf.alphaMax, d))
+ * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb
new file mode 100644
index 0000000000..5753794649
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
new file mode 100644
index 0000000000..8f0d618503
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb
new file mode 100644
index 0000000000..6026960d68
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
new file mode 100644
index 0000000000..70fb80852d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
@@ -0,0 +1,25 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+void main()
+{
+ float d = texture(_qt_texture, sampleCoord).a;
+ fragColor = mix(ubuf.styleColor, ubuf.color, smoothstep(ubuf.alphaMin, ubuf.alphaMax, d))
+ * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb
new file mode 100644
index 0000000000..451ccbac5b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
new file mode 100644
index 0000000000..aa3390094b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).r);
+ vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, shiftedSampleCoord).r);
+ fragColor = mix(shifted, ubuf.color, a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb
new file mode 100644
index 0000000000..41ebc12abf
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
new file mode 100644
index 0000000000..f3a7671435
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 shiftedSampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb
new file mode 100644
index 0000000000..0c37ccb6ed
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
new file mode 100644
index 0000000000..ab3a5f63ff
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).a);
+ vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, shiftedSampleCoord).a);
+ fragColor = mix(shifted, ubuf.color, a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb
new file mode 100644
index 0000000000..b92235eec3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
new file mode 100644
index 0000000000..d594207567
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, sampleCoord).r);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb
new file mode 100644
index 0000000000..28ba15e3de
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
new file mode 100644
index 0000000000..d56ddddd24
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb
new file mode 100644
index 0000000000..2877ab92db
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
new file mode 100644
index 0000000000..bb807d86d8
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, sampleCoord).a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..2e6085aa39
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag b/src/quick/scenegraph/shaders_ng/flatcolor.frag
new file mode 100644
index 0000000000..3a677b7c93
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag
@@ -0,0 +1,13 @@
+#version 440
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb
new file mode 100644
index 0000000000..a528c667fd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.vert b/src/quick/scenegraph/shaders_ng/flatcolor.vert
new file mode 100644
index 0000000000..b5dfd32197
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vertexCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb
new file mode 100644
index 0000000000..e83de529e6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
new file mode 100644
index 0000000000..723227a04d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
@@ -0,0 +1,40 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec3 sampleFarLeft;
+layout(location = 2) in vec3 sampleNearLeft;
+layout(location = 3) in vec3 sampleNearRight;
+layout(location = 4) in vec3 sampleFarRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec4 n;
+ n.x = textureProj(_qt_texture, sampleFarLeft).r;
+ n.y = textureProj(_qt_texture, sampleNearLeft).r;
+ float c = texture(_qt_texture, sampleCoord).r;
+ n.z = textureProj(_qt_texture, sampleNearRight).r;
+ n.w = textureProj(_qt_texture, sampleFarRight).r;
+
+ vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
+ vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ n = smoothstep(lo.xxyy, hi.xxyy, n);
+ c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
+
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb
new file mode 100644
index 0000000000..81c51321bb
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
new file mode 100644
index 0000000000..9c7281c31c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
@@ -0,0 +1,44 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec3 sampleFarLeft;
+layout(location = 2) out vec3 sampleNearLeft;
+layout(location = 3) out vec3 sampleNearRight;
+layout(location = 4) out vec3 sampleFarRight;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+
+ // Calculate neighbor pixel position in item space.
+ vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 farLeft = vCoord.xyw - 0.667 * wDelta;
+ vec3 nearLeft = vCoord.xyw - 0.333 * wDelta;
+ vec3 nearRight = vCoord.xyw + 0.333 * wDelta;
+ vec3 farRight = vCoord.xyw + 0.667 * wDelta;
+
+ // Calculate neighbor texture coordinate.
+ vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 base = sampleCoord - scale * vCoord.xy;
+ sampleFarLeft = vec3(base * farLeft.z + scale * farLeft.xy, farLeft.z);
+ sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
+ sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z);
+ sampleFarRight = vec3(base * farRight.z + scale * farRight.xy, farRight.z);
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb
new file mode 100644
index 0000000000..6bf01658a1
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
new file mode 100644
index 0000000000..a9d56f6380
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
@@ -0,0 +1,40 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec3 sampleFarLeft;
+layout(location = 2) in vec3 sampleNearLeft;
+layout(location = 3) in vec3 sampleNearRight;
+layout(location = 4) in vec3 sampleFarRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec4 n;
+ n.x = textureProj(_qt_texture, sampleFarLeft).a;
+ n.y = textureProj(_qt_texture, sampleNearLeft).a;
+ float c = texture(_qt_texture, sampleCoord).a;
+ n.z = textureProj(_qt_texture, sampleNearRight).a;
+ n.w = textureProj(_qt_texture, sampleFarRight).a;
+
+ vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
+ vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ n = smoothstep(lo.xxyy, hi.xxyy, n);
+ c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
+
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..4a9ac900a6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
new file mode 100644
index 0000000000..08b2ce5187
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec3 sampleNearLeft;
+layout(location = 1) in vec3 sampleNearRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec2 n;
+ n.x = textureProj(_qt_texture, sampleNearLeft).r;
+ n.y = textureProj(_qt_texture, sampleNearRight).r;
+ n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ float c = 0.5 * (n.x + n.y);
+ fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb
new file mode 100644
index 0000000000..76c2459edf
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
new file mode 100644
index 0000000000..187c384959
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
@@ -0,0 +1,37 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec3 sampleNearLeft;
+layout(location = 1) out vec3 sampleNearRight;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec2 sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+
+ // Calculate neighbor pixel position in item space.
+ vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 nearLeft = vCoord.xyw - 0.25 * wDelta;
+ vec3 nearRight = vCoord.xyw + 0.25 * wDelta;
+
+ // Calculate neighbor texture coordinate.
+ vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 base = sampleCoord - scale * vCoord.xy;
+ sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
+ sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z);
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb
new file mode 100644
index 0000000000..7bfa7ccd4a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
new file mode 100644
index 0000000000..ef9407491b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec3 sampleNearLeft;
+layout(location = 1) in vec3 sampleNearRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec2 n;
+ n.x = textureProj(_qt_texture, sampleNearLeft).a;
+ n.y = textureProj(_qt_texture, sampleNearRight).a;
+ n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ float c = 0.5 * (n.x + n.y);
+ fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..8f8304fb49
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
new file mode 100644
index 0000000000..2cd2175f87
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
@@ -0,0 +1,11 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, qt_TexCoord);
+}
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb b/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb
new file mode 100644
index 0000000000..0b4554568b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.vert b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
new file mode 100644
index 0000000000..5b52a59004
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 qt_VertexPosition;
+layout(location = 1) in vec2 qt_VertexTexCoord;
+
+layout(location = 0) out vec2 qt_TexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb b/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb
new file mode 100644
index 0000000000..2872af0200
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
new file mode 100644
index 0000000000..0023ed5467
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
@@ -0,0 +1,33 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 sCoordUp;
+layout(location = 2) in vec2 sCoordDown;
+layout(location = 3) in vec2 sCoordLeft;
+layout(location = 4) in vec2 sCoordRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).r;
+ float outline = clamp(clamp(texture(_qt_texture, sCoordUp).r +
+ texture(_qt_texture, sCoordDown).r +
+ texture(_qt_texture, sCoordLeft).r +
+ texture(_qt_texture, sCoordRight).r,
+ 0.0, 1.0) - glyph,
+ 0.0, 1.0);
+ fragColor = outline * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
new file mode 100644
index 0000000000..5ab92fecca
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
new file mode 100644
index 0000000000..c683a4273c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
@@ -0,0 +1,32 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 sCoordUp;
+layout(location = 2) out vec2 sCoordDown;
+layout(location = 3) out vec2 sCoordLeft;
+layout(location = 4) out vec2 sCoordRight;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ sCoordUp = (tCoord - vec2(0.0, -1.0)) * ubuf.textureScale;
+ sCoordDown = (tCoord - vec2(0.0, 1.0)) * ubuf.textureScale;
+ sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * ubuf.textureScale;
+ sCoordRight = (tCoord - vec2(1.0, 0.0)) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
new file mode 100644
index 0000000000..6aee048faa
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
new file mode 100644
index 0000000000..9a3d8cf5ed
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
@@ -0,0 +1,33 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 sCoordUp;
+layout(location = 2) in vec2 sCoordDown;
+layout(location = 3) in vec2 sCoordLeft;
+layout(location = 4) in vec2 sCoordRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+ float outline = clamp(clamp(texture(_qt_texture, sCoordUp).a +
+ texture(_qt_texture, sCoordDown).a +
+ texture(_qt_texture, sCoordLeft).a +
+ texture(_qt_texture, sCoordRight).a,
+ 0.0, 1.0) - glyph,
+ 0.0, 1.0);
+ fragColor = outline * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
new file mode 100644
index 0000000000..6e6b1ab6c2
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag b/src/quick/scenegraph/shaders_ng/shadereffect.frag
new file mode 100644
index 0000000000..bde493f6ce
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform qt_buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+} qt_ubuf;
+
+layout(binding = 1) uniform sampler2D source;
+
+void main()
+{
+ fragColor = texture(source, qt_TexCoord0) * qt_ubuf.qt_Opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb b/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb
new file mode 100644
index 0000000000..4b08ee2ce4
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.vert b/src/quick/scenegraph/shaders_ng/shadereffect.vert
new file mode 100644
index 0000000000..ae65059f19
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 qt_Vertex;
+layout(location = 1) in vec2 qt_MultiTexCoord0;
+
+layout(location = 0) out vec2 qt_TexCoord0;
+
+layout(std140, binding = 0) uniform qt_buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+} qt_ubuf; // must use a name that does not clash with custom code when no uniform blocks
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ gl_Position = qt_ubuf.qt_Matrix * qt_Vertex;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb b/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb
new file mode 100644
index 0000000000..4a8c646a21
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
new file mode 100644
index 0000000000..ede283be0c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(location = 0) in vec4 color;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb
new file mode 100644
index 0000000000..f99cdf1176
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.vert b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
new file mode 100644
index 0000000000..03a3ff8975
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
@@ -0,0 +1,51 @@
+#version 440
+
+layout(location = 0) in vec4 vertex;
+layout(location = 1) in vec4 vertexColor;
+layout(location = 2) in vec4 vertexOffset;
+
+layout(location = 0) out vec4 color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 pixelSize;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 pos = ubuf.matrix * vertex;
+ gl_Position = pos;
+
+ if (vertexOffset.x != 0.) {
+ vec4 delta = ubuf.matrix[0] * vertexOffset.x;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ }
+
+ if (vertexOffset.y != 0.) {
+ vec4 delta = ubuf.matrix[1] * vertexOffset.y;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ }
+
+ color = vertexColor * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb
new file mode 100644
index 0000000000..59c4104a2c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
new file mode 100644
index 0000000000..b06764ad95
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
@@ -0,0 +1,13 @@
+#version 440
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 1) in float vertexOpacity;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, texCoord) * vertexOpacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb b/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb
new file mode 100644
index 0000000000..ffaecbb56c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.vert b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
new file mode 100644
index 0000000000..965c837852
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
@@ -0,0 +1,61 @@
+#version 440
+
+layout(location = 0) in vec4 vertex;
+layout(location = 1) in vec2 multiTexCoord;
+layout(location = 2) in vec2 vertexOffset;
+layout(location = 3) in vec2 texCoordOffset;
+
+layout(location = 0) out vec2 texCoord;
+layout(location = 1) out float vertexOpacity;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+ vec2 pixelSize;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 pos = ubuf.qt_Matrix * vertex;
+ gl_Position = pos;
+ texCoord = multiTexCoord;
+
+ if (vertexOffset.x != 0.) {
+ vec4 delta = ubuf.qt_Matrix[0] * vertexOffset.x;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ texCoord.x += scale * texCoordOffset.x;
+ }
+
+ if (vertexOffset.y != 0.) {
+ vec4 delta = ubuf.qt_Matrix[1] * vertexOffset.y;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ texCoord.y += scale * texCoordOffset.y;
+ }
+
+ bool onEdge = any(notEqual(vertexOffset, vec2(0.)));
+ bool outerEdge = all(equal(texCoordOffset, vec2(0.)));
+ if (onEdge && outerEdge)
+ vertexOpacity = 0.;
+ else
+ vertexOpacity = ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb b/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb
new file mode 100644
index 0000000000..b7715d4dd5
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag b/src/quick/scenegraph/shaders_ng/sprite.frag
new file mode 100644
index 0000000000..338f5e957e
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.frag
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 fTexS;
+layout(location = 1) in float progress;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D tex;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 animPos;
+ vec3 animData;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = mix(texture(tex, fTexS.xy),
+ texture(tex, fTexS.zw),
+ progress) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag.qsb b/src/quick/scenegraph/shaders_ng/sprite.frag.qsb
new file mode 100644
index 0000000000..45d5bc14ee
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/sprite.vert b/src/quick/scenegraph/shaders_ng/sprite.vert
new file mode 100644
index 0000000000..b76e2b206f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.vert
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec2 vPos;
+layout(location = 1) in vec2 vTex;
+
+layout(location = 0) out vec4 fTexS;
+layout(location = 1) out float progress;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 animPos; // x,y, x,y (two frames for interpolation)
+ vec3 animData; // w,h(premultiplied of anim), interpolation progress
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ progress = ubuf.animData.z;
+
+ // Calculate frame location in texture
+ fTexS.xy = ubuf.animPos.xy + vTex.xy * ubuf.animData.xy;
+
+ // Next frame is also passed, for interpolation
+ fTexS.zw = ubuf.animPos.zw + vTex.xy * ubuf.animData.xy;
+
+ gl_Position = ubuf.matrix * vec4(vPos.x, vPos.y, 0, 1);
+}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.vert.qsb b/src/quick/scenegraph/shaders_ng/sprite.vert.qsb
new file mode 100644
index 0000000000..b55f881734
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag b/src/quick/scenegraph/shaders_ng/stencilclip.frag
new file mode 100644
index 0000000000..3f6222389d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag
@@ -0,0 +1,8 @@
+#version 440
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(0.81, 0.83, 0.12, 1.0); // Trolltech green ftw!
+}
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb b/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb
new file mode 100644
index 0000000000..6ae7a51f7a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.vert b/src/quick/scenegraph/shaders_ng/stencilclip.vert
new file mode 100644
index 0000000000..d8b491f775
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.vert
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb b/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb
new file mode 100644
index 0000000000..ce2ed3c5b3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag
new file mode 100644
index 0000000000..0b16396037
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).r;
+ float style = clamp(texture(_qt_texture, shiftedSampleCoord).r - glyph,
+ 0.0, 1.0);
+ fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
new file mode 100644
index 0000000000..66ebc5f827
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert b/src/quick/scenegraph/shaders_ng/styledtext.vert
new file mode 100644
index 0000000000..10565107c6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 shiftedSampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * floor((vCoord * ubuf.dpr) + 0.5) / ubuf.dpr;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
new file mode 100644
index 0000000000..9a27ed0eb8
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
new file mode 100644
index 0000000000..b673137895
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+ float style = clamp(texture(_qt_texture, shiftedSampleCoord).a - glyph,
+ 0.0, 1.0);
+ fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
new file mode 100644
index 0000000000..9dd4137072
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag
new file mode 100644
index 0000000000..518d5c965f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ vec4 glyph = texture(_qt_texture, sampleCoord);
+ fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag.qsb b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
new file mode 100644
index 0000000000..b16da4d76a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert b/src/quick/scenegraph/shaders_ng/textmask.vert
new file mode 100644
index 0000000000..d7d3bf892e
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * floor((vCoord * ubuf.dpr) + 0.5) / ubuf.dpr;
+}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert.qsb b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
new file mode 100644
index 0000000000..ae196ed0ad
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/texture.frag b/src/quick/scenegraph/shaders_ng/texture.frag
new file mode 100644
index 0000000000..bd22f817e0
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.frag
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/texture.frag.qsb b/src/quick/scenegraph/shaders_ng/texture.frag.qsb
new file mode 100644
index 0000000000..3f4aa3713c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/texture.vert b/src/quick/scenegraph/shaders_ng/texture.vert
new file mode 100644
index 0000000000..537852d2bd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 qt_VertexPosition;
+layout(location = 1) in vec2 qt_VertexTexCoord;
+
+layout(location = 0) out vec2 qt_TexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/quick/scenegraph/shaders_ng/texture.vert.qsb b/src/quick/scenegraph/shaders_ng/texture.vert.qsb
new file mode 100644
index 0000000000..bf0bc7d9fa
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
new file mode 100644
index 0000000000..ede283be0c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(location = 0) in vec4 color;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb
new file mode 100644
index 0000000000..93965a55dd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.vert b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
new file mode 100644
index 0000000000..bfb9a95073
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexColor;
+
+layout(location = 0) out vec4 color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vertexCoord;
+ color = vertexColor * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb
new file mode 100644
index 0000000000..98abe4ef6f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp
index 7186ee4265..48ec8f01d3 100644
--- a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp
@@ -156,6 +156,10 @@ void QSGDefaultImageNode::setTexture(QSGTexture *texture)
m_isAtlasTexture = texture->isAtlasTexture();
if (wasAtlas || m_isAtlasTexture)
dirty |= DirtyGeometry;
+ // The geometry has also changed if the texture size changed.
+ if (m_textureSize != texture->textureSize())
+ dirty |= DirtyGeometry;
+ m_textureSize = texture->textureSize();
markDirty(dirty);
}
diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
index cb23e759d3..7b26daf541 100644
--- a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
@@ -95,6 +95,7 @@ private:
QSGTextureMaterial m_material;
QRectF m_rect;
QRectF m_sourceRect;
+ QSize m_textureSize;
TextureCoordinatesTransformMode m_texCoordMode;
uint m_isAtlasTexture : 1;
uint m_ownsTexture : 1;
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index 981ea089be..f15ea67b46 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
#define QT_MINIMUM_DYNAMIC_FBO_SIZE 64U
QSGPainterTexture::QSGPainterTexture()
- : QSGPlainTexture()
+ : QSGPlainTexture(*(new QSGPainterTexturePrivate))
{
m_retain_image = true;
}
@@ -73,6 +73,16 @@ void QSGPainterTexture::bind()
m_dirty_rect = QRect();
}
+void QSGPainterTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGPainterTexture);
+ if (!q->m_dirty_rect.isNull()) {
+ q->setImage(q->m_image);
+ q->m_dirty_rect = QRect();
+ }
+ QSGPlainTexturePrivate::updateRhiTexture(rhi, resourceUpdates);
+}
+
QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
: QSGPainterNode()
, m_preferredRenderTarget(QQuickPaintedItem::Image)
@@ -126,6 +136,7 @@ void QSGDefaultPainterNode::paint()
return;
painter.begin(&m_image);
} else {
+ Q_ASSERT(!m_context->rhi());
if (!m_gl_device) {
m_gl_device = new QOpenGLPaintDevice(m_fboSize);
m_gl_device->setPaintFlipped(true);
@@ -237,7 +248,7 @@ void QSGDefaultPainterNode::updateGeometry()
void QSGDefaultPainterNode::updateRenderTarget()
{
- if (!m_extensionsChecked) {
+ if (!m_extensionsChecked && !m_context->rhi()) {
QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
m_multisamplingSupported = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
&& e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
@@ -250,7 +261,10 @@ void QSGDefaultPainterNode::updateRenderTarget()
if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
m_actualRenderTarget = QQuickPaintedItem::Image;
} else {
- if (!m_multisamplingSupported && m_smoothPainting)
+ // Image is the only option when there is no multisample framebuffer
+ // support and smooth painting is wanted, and when using the RHI. The
+ // latter may change in the future.
+ if ((!m_multisamplingSupported && m_smoothPainting) || m_context->rhi())
m_actualRenderTarget = QQuickPaintedItem::Image;
else
m_actualRenderTarget = m_preferredRenderTarget;
@@ -265,7 +279,9 @@ void QSGDefaultPainterNode::updateRenderTarget()
}
if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
- m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
+ m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
+ {
+ Q_ASSERT(!m_context->rhi());
const QOpenGLContext *ctx = m_context->openglContext();
if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
return;
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index 084fc1e004..a86f7397be 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -53,7 +53,7 @@
#include <private/qsgadaptationlayer_p.h>
#include "qsgtexturematerial.h"
-#include "qsgtexture_p.h"
+#include "qsgplaintexture_p.h"
#include <QtQuick/qquickpainteditem.h>
@@ -64,9 +64,11 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QOpenGLPaintDevice;
class QSGDefaultRenderContext;
+class QSGPainterTexturePrivate;
class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture
{
+ Q_DECLARE_PRIVATE(QSGPainterTexture)
public:
QSGPainterTexture();
@@ -78,6 +80,13 @@ private:
QRect m_dirty_rect;
};
+class QSGPainterTexturePrivate : public QSGPlainTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGPainterTexture)
+public:
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QSGDefaultPainterNode : public QSGPainterNode
{
public:
diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp
index 91fa46033c..c05005c467 100644
--- a/src/quick/scenegraph/util/qsgengine.cpp
+++ b/src/quick/scenegraph/util/qsgengine.cpp
@@ -42,7 +42,7 @@
#include <QtQuick/qsgtexture.h>
#include <private/qsgcontext_p.h>
#include <private/qsgrenderer_p.h>
-#include <private/qsgtexture_p.h>
+#include <private/qsgplaintexture_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/QOpenGLContext>
@@ -126,12 +126,29 @@ void QSGEngine::initialize(QOpenGLContext *context)
#endif
if (d->sgRenderContext && !d->sgRenderContext->isValid()) {
d->sgRenderContext->setAttachToGraphicsContext(false);
- d->sgRenderContext->initialize(context);
+#if QT_CONFIG(opengl)
+ QSGDefaultRenderContext *rc = qobject_cast<QSGDefaultRenderContext *>(d->sgRenderContext.data());
+ if (rc) {
+ QSGDefaultRenderContext::InitParams params;
+ params.sampleCount = qMax(1, context->format().samples());
+ params.openGLContext = context;
+ // leave the size hint and surface unset, we do not know, that's fine
+ rc->initialize(&params);
+ } else {
+ d->sgRenderContext->initialize(nullptr);
+ }
+#else
+ d->sgRenderContext->initialize(nullptr);
+#endif
#if QT_CONFIG(opengl)
if (context)
connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
#endif
}
+
+#if !QT_CONFIG(opengl)
+ Q_UNUSED(context);
+#endif
}
/*!
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index 28f6113a60..f121d2a9e2 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
@@ -116,6 +116,51 @@ void FlatColorMaterialShader::initialize()
}
+class FlatColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ FlatColorMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+FlatColorMaterialRhiShader::FlatColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.frag.qsb"));
+}
+
+bool FlatColorMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(!oldMaterial || newMaterial->type() == oldMaterial->type());
+ QSGFlatColorMaterial *oldMat = static_cast<QSGFlatColorMaterial *>(oldMaterial);
+ QSGFlatColorMaterial *mat = static_cast<QSGFlatColorMaterial *>(newMaterial);
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ const QColor &c = mat->color();
+ if (!oldMat || c != oldMat->color() || state.isOpacityDirty()) {
+ const float opacity = state.opacity() * c.alphaF();
+ QVector4D v(c.redF() * opacity,
+ c.greenF() * opacity,
+ c.blueF() * opacity,
+ opacity);
+ Q_ASSERT(sizeof(v) == 16);
+ memcpy(buf->data() + 64, &v, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
/*!
\class QSGFlatColorMaterial
@@ -126,7 +171,7 @@ void FlatColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
+ \warning This utility class is only functional when running with the default
backend of the Qt Quick scenegraph.
The flat color material will fill every pixel in a geometry using
@@ -150,10 +195,9 @@ void FlatColorMaterialShader::initialize()
QSGFlatColorMaterial::QSGFlatColorMaterial() : m_color(QColor(255, 255, 255))
{
+ setFlag(SupportsRhiShader, true);
}
-
-
/*!
\fn const QColor &QSGFlatColorMaterial::color() const
@@ -193,7 +237,10 @@ QSGMaterialType *QSGFlatColorMaterial::type() const
QSGMaterialShader *QSGFlatColorMaterial::createShader() const
{
- return new FlatColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new FlatColorMaterialRhiShader;
+ else
+ return new FlatColorMaterialShader;
}
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp
index 921ed0c1fc..ae7d9cf8cc 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qsgatlastexture_p.h"
+#include "qsgopenglatlastexture_p.h"
#include <QtCore/QVarLengthArray>
#include <QtCore/QElapsedTimer>
@@ -71,24 +71,22 @@ static QElapsedTimer qsg_renderer_timer;
DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
-namespace QSGAtlasTexture
+namespace QSGOpenGLAtlasTexture
{
-Manager::Manager()
+Manager::Manager(const QSize &surfacePixelSize)
: m_atlas(nullptr)
{
QOpenGLContext *gl = QOpenGLContext::currentContext();
Q_ASSERT(gl);
- QSurface *surface = gl->surface();
- QSize surfaceSize = surface->size();
int max;
gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
- int w = qMin(max, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfaceSize.width() - 1))));
- int h = qMin(max, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfaceSize.height() - 1))));
+ int w = qMin(max, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfacePixelSize.width() - 1))));
+ int h = qMin(max, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfacePixelSize.height() - 1))));
- if (surface->surfaceClass() == QSurface::Window) {
- QWindow *window = static_cast<QWindow *>(surface);
+ if (gl->surface()->surfaceClass() == QSurface::Window) {
+ QWindow *window = static_cast<QWindow *>(gl->surface());
// Coverwindows, optimize for memory rather than speed
if ((window->type() & Qt::CoverWindow) == Qt::CoverWindow) {
w /= 2;
@@ -99,10 +97,9 @@ Manager::Manager()
m_atlas_size_limit = qt_sg_envInt("QSG_ATLAS_SIZE_LIMIT", qMax(w, h) / 2);
m_atlas_size = QSize(w, h);
- qCDebug(QSG_LOG_INFO, "texture atlas dimensions: %dx%d", w, h);
+ qCDebug(QSG_LOG_INFO, "opengl texture atlas dimensions: %dx%d", w, h);
}
-
Manager::~Manager()
{
Q_ASSERT(m_atlas == nullptr);
@@ -593,4 +590,4 @@ QSGTexture *Texture::removedFromAtlas() const
QT_END_NAMESPACE
-#include "moc_qsgatlastexture_p.cpp"
+#include "moc_qsgopenglatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgopenglatlastexture_p.h
index 14dc8f7958..f8dd7cdf02 100644
--- a/src/quick/scenegraph/util/qsgatlastexture_p.h
+++ b/src/quick/scenegraph/util/qsgopenglatlastexture_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGATLASTEXTURE_P_H
-#define QSGATLASTEXTURE_P_H
+#ifndef QSGOPENGLATLASTEXTURE_P_H
+#define QSGOPENGLATLASTEXTURE_P_H
//
// W A R N I N G
@@ -56,7 +56,7 @@
#include <QtGui/qopengl.h>
#include <QtQuick/QSGTexture>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qsgareaallocator_p.h>
QT_BEGIN_NAMESPACE
@@ -66,7 +66,7 @@ namespace QSGCompressedAtlasTexture {
}
class QSGCompressedTextureFactory;
-namespace QSGAtlasTexture
+namespace QSGOpenGLAtlasTexture
{
class Texture;
@@ -78,7 +78,7 @@ class Manager : public QObject
Q_OBJECT
public:
- Manager();
+ Manager(const QSize &surfacePixelSize);
~Manager();
QSGTexture *create(const QImage &image, bool hasAlphaChannel);
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
new file mode 100644
index 0000000000..4c2b452b45
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -0,0 +1,425 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgplaintexture_p.h"
+#include <QtQuick/private/qsgcontext_p.h>
+#include <qmath.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmlglobal_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglcontext.h>
+# include <QtGui/qopenglfunctions.h>
+# include <QtGui/private/qopengltextureuploader_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+#include <QtGui/private/qrhi_p.h>
+
+#if QT_CONFIG(opengl)
+static QElapsedTimer qsg_renderer_timer;
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QSGPlainTexture::QSGPlainTexture()
+ : QSGTexture(*(new QSGPlainTexturePrivate))
+ , m_texture_id(0)
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_dirty_bind_options(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::QSGPlainTexture(QSGPlainTexturePrivate &dd)
+ : QSGTexture(dd)
+ , m_texture_id(0)
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_dirty_bind_options(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::~QSGPlainTexture()
+{
+#if QT_CONFIG(opengl)
+ if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext())
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
+ if (m_texture && m_owns_texture)
+ delete m_texture;
+}
+
+void QSGPlainTexture::setImage(const QImage &image)
+{
+ m_image = image;
+ m_texture_size = image.size();
+ m_has_alpha = image.hasAlphaChannel();
+ m_dirty_texture = true;
+ m_dirty_bind_options = true;
+ m_mipmaps_generated = false;
+ }
+
+int QSGPlainTexture::textureId() const // legacy (GL-only)
+{
+ if (m_dirty_texture) {
+ if (m_image.isNull()) {
+ // The actual texture and id will be updated/deleted in a later bind()
+ // or ~QSGPlainTexture so just keep it minimal here.
+ return 0;
+ } else if (m_texture_id == 0){
+#if QT_CONFIG(opengl)
+ // Generate a texture id for use later and return it.
+ QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
+#endif
+ return m_texture_id;
+ }
+ }
+ return m_texture_id;
+}
+
+void QSGPlainTexture::setTextureId(int id) // legacy (GL-only)
+{
+#if QT_CONFIG(opengl)
+ if (m_texture_id && m_owns_texture)
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
+
+ m_texture_id = id;
+ m_dirty_texture = false;
+ m_dirty_bind_options = true;
+ m_image = QImage();
+ m_mipmaps_generated = false;
+}
+
+void QSGPlainTexture::bind() // legacy (GL-only)
+{
+#if QT_CONFIG(opengl)
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QOpenGLFunctions *funcs = context->functions();
+ if (!m_dirty_texture) {
+ funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
+ if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) {
+ funcs->glGenerateMipmap(GL_TEXTURE_2D);
+ m_mipmaps_generated = true;
+ }
+ updateBindOptions(m_dirty_bind_options);
+ m_dirty_bind_options = false;
+ return;
+ }
+
+ m_dirty_texture = false;
+
+ bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
+ if (profileFrames)
+ qsg_renderer_timer.start();
+ Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTextureDeletion);
+
+
+ if (m_image.isNull()) {
+ if (m_texture_id && m_owns_texture) {
+ funcs->glDeleteTextures(1, &m_texture_id);
+ qCDebug(QSG_LOG_TIME_TEXTURE, "plain texture deleted in %dms - %dx%d",
+ (int) qsg_renderer_timer.elapsed(),
+ m_texture_size.width(),
+ m_texture_size.height());
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTextureDeletion,
+ QQuickProfiler::SceneGraphTextureDeletionDelete);
+ }
+ m_texture_id = 0;
+ m_texture_size = QSize();
+ m_has_alpha = false;
+
+ return;
+ }
+
+ if (m_texture_id == 0)
+ funcs->glGenTextures(1, &m_texture_id);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
+
+ qint64 bindTime = 0;
+ if (profileFrames)
+ bindTime = qsg_renderer_timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareBind);
+
+ // ### TODO: check for out-of-memory situations...
+
+ QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
+
+ // Downscale the texture to fit inside the max texture limit if it is too big.
+ // It would be better if the image was already downscaled to the right size,
+ // but this information is not always available at that time, so as a last
+ // resort we can do it here. Texture coordinates are normalized, so it
+ // won't cause any problems and actual texture sizes will be written
+ // based on QSGTexture::textureSize which is updated after this, so that
+ // should be ok.
+ int max;
+ if (auto rc = QSGDefaultRenderContext::from(context))
+ max = rc->maxTextureSize();
+ else
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
+
+ m_texture_size = m_texture_size.boundedTo(QSize(max, max));
+
+ // Scale to a power of two size if mipmapping is requested and the
+ // texture is npot and npot textures are not properly supported.
+ if (mipmapFiltering() != QSGTexture::None
+ && !funcs->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
+ options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
+ }
+
+ updateBindOptions(m_dirty_bind_options);
+
+ QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, m_image, options, QSize(max, max));
+
+ qint64 uploadTime = 0;
+ if (profileFrames)
+ uploadTime = qsg_renderer_timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload);
+
+ if (mipmapFiltering() != QSGTexture::None) {
+ funcs->glGenerateMipmap(GL_TEXTURE_2D);
+ m_mipmaps_generated = true;
+ }
+
+ qint64 mipmapTime = 0;
+ if (profileFrames) {
+ mipmapTime = qsg_renderer_timer.nsecsElapsed();
+ qCDebug(QSG_LOG_TIME_TEXTURE,
+ "plain texture uploaded in: %dms (%dx%d), bind=%d, upload=%d, mipmap=%d%s",
+ int(mipmapTime / 1000000),
+ m_texture_size.width(), m_texture_size.height(),
+ int(bindTime / 1000000),
+ int((uploadTime - bindTime)/1000000),
+ int((mipmapTime - uploadTime)/1000000),
+ m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : "");
+ }
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareMipmap);
+
+ m_texture_rect = QRectF(0, 0, 1, 1);
+
+ m_dirty_bind_options = false;
+ if (!m_retain_image)
+ m_image = QImage();
+#endif
+}
+
+void QSGPlainTexture::setTexture(QRhiTexture *texture) // RHI only
+{
+ if (m_texture && m_owns_texture && m_texture != texture)
+ delete m_texture;
+
+ m_texture = texture;
+ m_dirty_texture = false;
+ m_dirty_bind_options = true;
+ m_image = QImage();
+ m_mipmaps_generated = false;
+}
+
+int QSGPlainTexturePrivate::comparisonKey() const
+{
+ Q_Q(const QSGPlainTexture);
+
+ // not textureId() as that would create an id when not yet done - that's not wanted here
+ if (q->m_texture_id)
+ return q->m_texture_id;
+
+ if (q->m_texture)
+ return int(qintptr(q->m_texture));
+
+ // two textures (and so materials) with not-yet-created texture underneath are never equal
+ return int(qintptr(q));
+}
+
+QRhiTexture *QSGPlainTexturePrivate::rhiTexture() const
+{
+ Q_Q(const QSGPlainTexture);
+ return q->m_texture;
+}
+
+void QSGPlainTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGPlainTexture);
+
+ const bool hasMipMaps = q->mipmapFiltering() != QSGTexture::None;
+ const bool mipmappingChanged = q->m_texture && ((hasMipMaps && !q->m_texture->flags().testFlag(QRhiTexture::MipMapped)) // did not have it before
+ || (!hasMipMaps && q->m_texture->flags().testFlag(QRhiTexture::MipMapped))); // does not have it anymore
+
+ if (!q->m_dirty_texture) {
+ if (!q->m_texture)
+ return;
+ if (q->m_texture && !mipmappingChanged) {
+ if (hasMipMaps && !q->m_mipmaps_generated) {
+ resourceUpdates->generateMips(q->m_texture);
+ q->m_mipmaps_generated = true;
+ }
+ return;
+ }
+ }
+
+ if (q->m_image.isNull()) {
+ if (!q->m_dirty_texture && mipmappingChanged) {
+ // Full Mipmap Panic!
+ if (!q->m_mipmap_warned) {
+ qWarning("QSGPlainTexture: Mipmap settings changed without having image data available. "
+ "Call setImage() again or enable m_retain_image. "
+ "Falling back to previous mipmap filtering mode.");
+ q->m_mipmap_warned = true;
+ }
+ // leave the texture valid and rather ignore the mipmap mode change attempt
+ q->setMipmapFiltering(m_last_mipmap_filter);
+ return;
+ }
+
+ if (q->m_texture && q->m_owns_texture)
+ delete q->m_texture;
+
+ q->m_texture = nullptr;
+ q->m_texture_size = QSize();
+ q->m_has_alpha = false;
+
+ q->m_dirty_texture = false;
+ return;
+ }
+
+ q->m_dirty_texture = false;
+
+ QImage tmp;
+ bool bgra = false;
+ bool needsConvert = false;
+ if (q->m_image.format() == QImage::Format_RGB32 || q->m_image.format() == QImage::Format_ARGB32_Premultiplied) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (rhi->isTextureFormatSupported(QRhiTexture::BGRA8)) {
+ tmp = q->m_image;
+ bgra = true;
+ } else {
+ needsConvert = true;
+ }
+#else
+ needsConvert = true;
+#endif
+ } else if (q->m_image.format() == QImage::Format_RGBX8888 || q->m_image.format() == QImage::Format_RGBA8888_Premultiplied) {
+ tmp = q->m_image;
+ } else {
+ needsConvert = true;
+ }
+
+ if (needsConvert)
+ tmp = q->m_image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+
+ // Downscale the texture to fit inside the max texture limit if it is too big.
+ // It would be better if the image was already downscaled to the right size,
+ // but this information is not always available at that time, so as a last
+ // resort we can do it here. Texture coordinates are normalized, so it
+ // won't cause any problems and actual texture sizes will be written
+ // based on QSGTexture::textureSize which is updated after this, so that
+ // should be ok.
+ const int max = rhi->resourceLimit(QRhi::TextureSizeMax);
+ if (tmp.width() > max || tmp.height() > max) {
+ tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ q->m_texture_size = tmp.size();
+ }
+
+ bool needsRebuild = q->m_texture && q->m_texture->pixelSize() != q->m_texture_size;
+
+ if (mipmappingChanged) {
+ QRhiTexture::Flags f = q->m_texture->flags();
+ f.setFlag(QRhiTexture::MipMapped, hasMipMaps);
+ f.setFlag(QRhiTexture::UsedWithGenerateMips, hasMipMaps);
+ q->m_texture->setFlags(f);
+ needsRebuild = true;
+ }
+
+ if (!q->m_texture) {
+ QRhiTexture::Flags f = 0;
+ if (hasMipMaps)
+ f |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ q->m_texture = rhi->newTexture(bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8, q->m_texture_size, 1, f);
+ needsRebuild = true;
+ }
+
+ if (needsRebuild) {
+ if (!q->m_texture->build()) {
+ qWarning("Failed to build texture for QSGPlainTexture (size %dx%d)",
+ q->m_texture_size.width(), q->m_texture_size.height());
+ return;
+ }
+ }
+
+ if (tmp.width() * 4 != tmp.bytesPerLine())
+ tmp = tmp.copy();
+
+ resourceUpdates->uploadTexture(q->m_texture, tmp);
+
+ if (hasMipMaps) {
+ resourceUpdates->generateMips(q->m_texture);
+ q->m_mipmaps_generated = true;
+ }
+
+ m_last_mipmap_filter = q->mipmapFiltering();
+ q->m_texture_rect = QRectF(0, 0, 1, 1);
+
+ if (!q->m_retain_image)
+ q->m_image = QImage();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgplaintexture_p.h
index 18dd5eff68..69c0153f9a 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgplaintexture_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGTEXTURE_P_H
-#define QSGTEXTURE_P_H
+#ifndef QSGPLAINTEXTURE_P_H
+#define QSGPLAINTEXTURE_P_H
//
// W A R N I N G
@@ -51,36 +51,17 @@
// We mean it.
//
-#include <QtQuick/qtquickglobal.h>
-#include <private/qobject_p.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/qopengl.h>
-#endif
-#include "qsgtexture.h"
-#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qsgtexture_p.h>
QT_BEGIN_NAMESPACE
-class QSGTexturePrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QSGTexture)
-public:
- QSGTexturePrivate();
-
- uint wrapChanged : 1;
- uint filteringChanged : 1;
- uint anisotropyChanged : 1;
-
- uint horizontalWrap : 2;
- uint verticalWrap : 2;
- uint mipmapMode : 2;
- uint filterMode : 2;
- uint anisotropyLevel: 3;
-};
+class QSGPlainTexturePrivate;
class Q_QUICK_PRIVATE_EXPORT QSGPlainTexture : public QSGTexture
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGPlainTexture)
public:
QSGPlainTexture();
~QSGPlainTexture() override;
@@ -103,6 +84,8 @@ public:
void bind() override;
+ void setTexture(QRhiTexture *texture);
+
static QSGPlainTexture *fromImage(const QImage &image) {
QSGPlainTexture *t = new QSGPlainTexture();
t->setImage(image);
@@ -110,22 +93,35 @@ public:
}
protected:
+ QSGPlainTexture(QSGPlainTexturePrivate &dd);
+
QImage m_image;
uint m_texture_id;
QSize m_texture_size;
QRectF m_texture_rect;
+ QRhiTexture *m_texture;
uint m_has_alpha : 1;
uint m_dirty_texture : 1;
- uint m_dirty_bind_options : 1;
+ uint m_dirty_bind_options : 1; // legacy (GL-only)
uint m_owns_texture : 1;
uint m_mipmaps_generated : 1;
- uint m_retain_image: 1;
+ uint m_retain_image : 1;
+ uint m_mipmap_warned : 1; // RHI only
};
-Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+class QSGPlainTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGPlainTexture)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ QSGTexture::Filtering m_last_mipmap_filter = QSGTexture::None;
+};
QT_END_NAMESPACE
-#endif // QSGTEXTURE_P_H
+#endif // QSGPLAINTEXTURE_P_H
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
new file mode 100644
index 0000000000..665e9bb412
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrhiatlastexture_p.h"
+
+#include <QtCore/QVarLengthArray>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QtMath>
+
+#include <QtGui/QWindow>
+
+#include <private/qqmlglobal_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
+#include <private/qsgtexture_p.h>
+#if 0
+#include <private/qsgcompressedtexture_p.h>
+#include <private/qsgcompressedatlastexture_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+int qt_sg_envInt(const char *name, int defaultValue);
+
+static QElapsedTimer qsg_renderer_timer;
+
+//DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
+
+namespace QSGRhiAtlasTexture
+{
+
+Manager::Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSurface *maybeSurface)
+ : m_rc(rc)
+ , m_rhi(rc->rhi())
+{
+ const int maxSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ int w = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfacePixelSize.width() - 1))));
+ int h = qMin(maxSize, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfacePixelSize.height() - 1))));
+
+ if (maybeSurface && maybeSurface->surfaceClass() == QSurface::Window) {
+ QWindow *window = static_cast<QWindow *>(maybeSurface);
+ // Coverwindows, optimize for memory rather than speed
+ if ((window->type() & Qt::CoverWindow) == Qt::CoverWindow) {
+ w /= 2;
+ h /= 2;
+ }
+ }
+
+ m_atlas_size_limit = qt_sg_envInt("QSG_ATLAS_SIZE_LIMIT", qMax(w, h) / 2);
+ m_atlas_size = QSize(w, h);
+
+ qCDebug(QSG_LOG_INFO, "rhi texture atlas dimensions: %dx%d", w, h);
+}
+
+Manager::~Manager()
+{
+ Q_ASSERT(m_atlas == nullptr);
+ Q_ASSERT(m_atlases.isEmpty());
+}
+
+void Manager::invalidate()
+{
+ if (m_atlas) {
+ m_atlas->invalidate();
+ m_atlas->deleteLater();
+ m_atlas = nullptr;
+ }
+
+ #if 0
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin();
+ while (i != m_atlases.end()) {
+ i.value()->invalidate();
+ i.value()->deleteLater();
+ ++i;
+ }
+ m_atlases.clear();
+#endif
+}
+
+QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel)
+{
+ Texture *t = nullptr;
+ if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) {
+ if (!m_atlas)
+ m_atlas = new Atlas(m_rc, m_atlas_size);
+ t = m_atlas->create(image);
+ if (t && !hasAlphaChannel && t->hasAlphaChannel())
+ t->setHasAlphaChannel(false);
+ }
+ return t;
+}
+
+QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
+{
+ Q_UNUSED(factory);
+ return nullptr;
+ // ###
+
+#if 0
+ QSGTexture *t = nullptr;
+ if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid())
+ return t;
+
+ // TODO: further abstract the atlas and remove this restriction
+ unsigned int format = factory->m_textureData.glInternalFormat();
+ switch (format) {
+ case QOpenGLTexture::RGB8_ETC1:
+ case QOpenGLTexture::RGB8_ETC2:
+ case QOpenGLTexture::RGBA8_ETC2_EAC:
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ break;
+ default:
+ return t;
+ }
+
+ QSize size = factory->m_textureData.size();
+ if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) {
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format);
+ if (i == m_atlases.end())
+ i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format));
+ // must be multiple of 4
+ QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4);
+ QByteArray data = factory->m_textureData.data();
+ t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize);
+ }
+#endif
+}
+
+AtlasBase::AtlasBase(QSGDefaultRenderContext *rc, const QSize &size)
+ : m_rc(rc)
+ , m_rhi(rc->rhi())
+ , m_allocator(size)
+ , m_size(size)
+{
+}
+
+AtlasBase::~AtlasBase()
+{
+ Q_ASSERT(!m_texture);
+}
+
+void AtlasBase::invalidate()
+{
+ delete m_texture;
+ m_texture = nullptr;
+}
+
+void AtlasBase::updateRhiTexture(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (!m_allocated) {
+ m_allocated = true;
+ if (!generateTexture()) {
+ qWarning("QSGTextureAtlas: Failed to create texture");
+ return;
+ }
+ }
+
+ for (TextureBase *t : m_pending_uploads) {
+ // ### this profiling is all wrong, the real work is done elsewhere
+ bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
+ if (profileFrames)
+ qsg_renderer_timer.start();
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare);
+
+ // Skip bind, convert, swizzle; they're irrelevant
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareStart, 3);
+
+ enqueueTextureUpload(t, resourceUpdates);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload);
+
+ // Skip mipmap; unused
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload, 1);
+ Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareMipmap);
+ }
+
+ m_pending_uploads.clear();
+}
+
+void AtlasBase::remove(TextureBase *t)
+{
+ QRect atlasRect = t->atlasSubRect();
+ m_allocator.deallocate(atlasRect);
+ m_pending_uploads.removeOne(t);
+}
+
+Atlas::Atlas(QSGDefaultRenderContext *rc, const QSize &size)
+ : AtlasBase(rc, size)
+{
+ // use RGBA texture internally as that is the only one guaranteed to be always supported
+ m_format = QRhiTexture::RGBA8;
+
+ m_debug_overlay = qt_sg_envInt("QSG_ATLAS_OVERLAY", 0);
+
+ // images smaller than this will retain their QImage.
+ // by default no images are retained (favoring memory)
+ // set to a very large value to retain all images (allowing quick removal from the atlas)
+ m_atlas_transient_image_threshold = qt_sg_envInt("QSG_ATLAS_TRANSIENT_IMAGE_THRESHOLD", 0);
+}
+
+Atlas::~Atlas()
+{
+}
+
+Texture *Atlas::create(const QImage &image)
+{
+ // No need to lock, as manager already locked it.
+ QRect rect = m_allocator.allocate(QSize(image.width() + 2, image.height() + 2));
+ if (rect.width() > 0 && rect.height() > 0) {
+ Texture *t = new Texture(this, rect, image);
+ m_pending_uploads << t;
+ return t;
+ }
+ return nullptr;
+}
+
+bool Atlas::generateTexture()
+{
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, QRhiTexture::UsedAsTransferSource);
+ if (!m_texture)
+ return false;
+
+ if (!m_texture->build()) {
+ delete m_texture;
+ m_texture = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+void Atlas::enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Texture *tex = static_cast<Texture *>(t);
+ const QRect &r = tex->atlasSubRect();
+ QImage image = tex->image();
+
+ if (image.isNull())
+ return;
+
+ if (image.format() != QImage::Format_RGBA8888_Premultiplied)
+ image = std::move(image).convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+
+ if (m_debug_overlay) {
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ p.fillRect(0, 0, image.width(), image.height(), QBrush(QColor::fromRgbF(0, 1, 1, 0.5)));
+ }
+
+ const int iw = image.width();
+ const int ih = image.height();
+ const int bpl = image.bytesPerLine() / 4;
+ QVarLengthArray<quint32, 1024> tmpBits(qMax(iw + 2, ih + 2));
+ const int tmpBitsSize = tmpBits.size() * 4;
+ const quint32 *src = reinterpret_cast<const quint32 *>(image.constBits());
+ quint32 *dst = tmpBits.data();
+ QVector<QRhiTextureUploadEntry> entries;
+ entries.reserve(5);
+
+ // top row, padding corners
+ dst[0] = src[0];
+ memcpy(dst + 1, src, iw * sizeof(quint32));
+ dst[1 + iw] = src[iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y()));
+ subresDesc.setSourceSize(QSize(iw + 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // bottom row, padded corners
+ const quint32 *lastRow = src + bpl * (ih - 1);
+ dst[0] = lastRow[0];
+ memcpy(dst + 1, lastRow, iw * sizeof(quint32));
+ dst[1 + iw] = lastRow[iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y() + ih + 1));
+ subresDesc.setSourceSize(QSize(iw + 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // left column
+ for (int i = 0; i < ih; ++i)
+ dst[i] = src[i * bpl];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y() + 1));
+ subresDesc.setSourceSize(QSize(1, ih));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+
+ // right column
+ for (int i = 0; i < ih; ++i)
+ dst[i] = src[i * bpl + iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + iw + 1, r.y() + 1));
+ subresDesc.setSourceSize(QSize(1, ih));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // Inner part of the image....
+ if (bpl != iw) {
+ int sy = r.y() + 1;
+ int ey = sy + r.height() - 2;
+ entries.reserve(4 + (ey - sy));
+ for (int y = sy; y < ey; ++y) {
+ QRhiTextureSubresourceUploadDescription subresDesc(src, image.bytesPerLine());
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + 1, y));
+ subresDesc.setSourceSize(QSize(r.width() - 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ src += bpl;
+ }
+ } else {
+ QRhiTextureSubresourceUploadDescription subresDesc(src, image.sizeInBytes());
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + 1, r.y() + 1));
+ subresDesc.setSourceSize(QSize(r.width() - 2, r.height() - 2));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ resourceUpdates->uploadTexture(m_texture, QRhiTextureUploadDescription(entries));
+
+ const QSize textureSize = t->textureSize();
+ if (textureSize.width() > m_atlas_transient_image_threshold || textureSize.height() > m_atlas_transient_image_threshold)
+ tex->releaseImage();
+
+ qCDebug(QSG_LOG_TIME_TEXTURE, "atlastexture upload enqueued in: %lldms (%dx%d)",
+ qsg_renderer_timer.elapsed(),
+ t->textureSize().width(),
+ t->textureSize().height());
+}
+
+TextureBase::TextureBase(AtlasBase *atlas, const QRect &textureRect)
+ : QSGTexture(*(new TextureBasePrivate))
+ , m_allocated_rect(textureRect)
+ , m_atlas(atlas)
+{
+}
+
+TextureBase::~TextureBase()
+{
+ m_atlas->remove(this);
+}
+
+QRhiResourceUpdateBatch *TextureBase::workResourceUpdateBatch() const
+{
+ Q_D(const TextureBase);
+ return d->workResourceUpdateBatch;
+}
+
+int TextureBasePrivate::comparisonKey() const
+{
+ Q_Q(const TextureBase);
+
+ // We need special care here: a typical comparisonKey() implementation
+ // returns a unique result when there is no underlying texture yet. This is
+ // not quite ideal for atlasing however since textures with the same atlas
+ // should be considered equal regardless of the state of the underlying
+ // graphics resources.
+
+ // base the comparison on the atlas ptr; this way textures for the same
+ // atlas are considered equal
+ return int(qintptr(q->m_atlas));
+}
+
+QRhiTexture *TextureBasePrivate::rhiTexture() const
+{
+ Q_Q(const TextureBase);
+ return q->m_atlas->m_texture;
+}
+
+void TextureBasePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(TextureBase);
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(rhi);
+#endif
+ Q_ASSERT(rhi == q->m_atlas->m_rhi);
+ q->m_atlas->updateRhiTexture(resourceUpdates);
+}
+
+Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image)
+ : TextureBase(atlas, textureRect)
+ , m_image(image)
+ , m_has_alpha(image.hasAlphaChannel())
+{
+ float w = atlas->size().width();
+ float h = atlas->size().height();
+ QRect nopad = atlasSubRectWithoutPadding();
+ m_texture_coords_rect = QRectF(nopad.x() / w,
+ nopad.y() / h,
+ nopad.width() / w,
+ nopad.height() / h);
+}
+
+Texture::~Texture()
+{
+ if (m_nonatlas_texture)
+ delete m_nonatlas_texture;
+}
+
+QSGTexture *Texture::removedFromAtlas() const
+{
+ if (!m_nonatlas_texture) {
+ m_nonatlas_texture = new QSGPlainTexture;
+ if (!m_image.isNull()) {
+ m_nonatlas_texture->setImage(m_image);
+ m_nonatlas_texture->setFiltering(filtering());
+ } else {
+ QSGDefaultRenderContext *rc = m_atlas->renderContext();
+ QRhi *rhi = m_atlas->rhi();
+ Q_ASSERT(rhi->isRecordingFrame());
+ const QRect r = atlasSubRectWithoutPadding();
+
+ QRhiTexture *extractTex = rhi->newTexture(m_atlas->texture()->format(), r.size());
+ if (extractTex->build()) {
+ bool ownResUpd = false;
+ QRhiResourceUpdateBatch *resUpd = workResourceUpdateBatch(); // ### Qt 6: should be an arg to this function
+ if (!resUpd) {
+ ownResUpd = true;
+ resUpd = rhi->nextResourceUpdateBatch();
+ }
+ QRhiTextureCopyDescription desc;
+ desc.setSourceTopLeft(r.topLeft());
+ desc.setPixelSize(r.size());
+ resUpd->copyTexture(extractTex, m_atlas->texture(), desc);
+ if (ownResUpd)
+ rc->currentFrameCommandBuffer()->resourceUpdate(resUpd);
+ }
+
+ m_nonatlas_texture->setTexture(extractTex);
+ m_nonatlas_texture->setOwnsTexture(true);
+ m_nonatlas_texture->setHasAlphaChannel(m_has_alpha);
+ m_nonatlas_texture->setTextureSize(r.size());
+ }
+ }
+
+ m_nonatlas_texture->setMipmapFiltering(mipmapFiltering());
+ m_nonatlas_texture->setFiltering(filtering());
+ return m_nonatlas_texture;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsgrhiatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture_p.h b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
new file mode 100644
index 0000000000..50d7b2a53f
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRHIATLASTEXTURE_P_H
+#define QSGRHIATLASTEXTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QSize>
+#include <QtQuick/private/qsgplaintexture_p.h>
+#include <QtQuick/private/qsgareaallocator_p.h>
+#include <QtGui/QSurface>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+
+namespace QSGCompressedAtlasTexture {
+ class Atlas;
+}
+class QSGCompressedTextureFactory;
+
+namespace QSGRhiAtlasTexture
+{
+
+class Texture;
+class TextureBase;
+class TextureBasePrivate;
+class Atlas;
+
+class Manager : public QObject
+{
+ Q_OBJECT
+
+public:
+ Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSurface *maybeSurface);
+ ~Manager();
+
+ QSGTexture *create(const QImage &image, bool hasAlphaChannel);
+ QSGTexture *create(const QSGCompressedTextureFactory *factory);
+ void invalidate();
+
+private:
+ QSGDefaultRenderContext *m_rc;
+ QRhi *m_rhi;
+ Atlas *m_atlas = nullptr;
+ // set of atlases for different compressed formats
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*> m_atlases;
+
+ QSize m_atlas_size;
+ int m_atlas_size_limit;
+};
+
+class AtlasBase : public QObject
+{
+ Q_OBJECT
+public:
+ AtlasBase(QSGDefaultRenderContext *rc, const QSize &size);
+ ~AtlasBase();
+
+ void invalidate();
+ void updateRhiTexture(QRhiResourceUpdateBatch *resourceUpdates);
+ void remove(TextureBase *t);
+
+ QSGDefaultRenderContext *renderContext() const { return m_rc; }
+ QRhi *rhi() const { return m_rhi; }
+ QRhiTexture *texture() const { return m_texture; }
+ QSize size() const { return m_size; }
+
+protected:
+ virtual bool generateTexture() = 0;
+ virtual void enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+protected:
+ QSGDefaultRenderContext *m_rc;
+ QRhi *m_rhi;
+ QSGAreaAllocator m_allocator;
+ QRhiTexture *m_texture = nullptr;
+ QSize m_size;
+ QVector<TextureBase *> m_pending_uploads;
+ friend class TextureBase;
+ friend class TextureBasePrivate;
+
+private:
+ bool m_allocated = false;
+};
+
+class Atlas : public AtlasBase
+{
+public:
+ Atlas(QSGDefaultRenderContext *rc, const QSize &size);
+ ~Atlas();
+
+ bool generateTexture() override;
+ void enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ Texture *create(const QImage &image);
+
+ QRhiTexture::Format format() const { return m_format; }
+
+private:
+ QRhiTexture::Format m_format;
+ int m_atlas_transient_image_threshold = 0;
+
+ uint m_debug_overlay : 1;
+};
+
+class TextureBase : public QSGTexture
+{
+ Q_DECLARE_PRIVATE(TextureBase)
+ Q_OBJECT
+public:
+ TextureBase(AtlasBase *atlas, const QRect &textureRect);
+ ~TextureBase();
+
+ int textureId() const override { return 0; } // not used
+ void bind() override { } // not used
+
+ bool isAtlasTexture() const override { return true; }
+ QRect atlasSubRect() const { return m_allocated_rect; }
+
+ QRhiResourceUpdateBatch *workResourceUpdateBatch() const;
+
+protected:
+ QRect m_allocated_rect;
+ AtlasBase *m_atlas;
+};
+
+class TextureBasePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(TextureBase)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
+class Texture : public TextureBase
+{
+ Q_OBJECT
+public:
+ Texture(Atlas *atlas, const QRect &textureRect, const QImage &image);
+ ~Texture();
+
+ QSize textureSize() const override { return atlasSubRectWithoutPadding().size(); }
+ void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; }
+ bool hasAlphaChannel() const override { return m_has_alpha; }
+ bool hasMipmaps() const override { return false; }
+
+ QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; }
+
+ QRect atlasSubRect() const { return m_allocated_rect; }
+ QRect atlasSubRectWithoutPadding() const { return m_allocated_rect.adjusted(1, 1, -1, -1); }
+
+ QSGTexture *removedFromAtlas() const override;
+
+ void releaseImage() { m_image = QImage(); }
+ const QImage &image() const { return m_image; }
+
+private:
+ QRectF m_texture_coords_rect;
+ QImage m_image;
+ mutable QSGPlainTexture *m_nonatlas_texture = nullptr;
+ bool m_has_alpha;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp
index 376f7dce5c..1064caccc7 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.cpp
+++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp
@@ -46,8 +46,9 @@
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the legacy
+ OpenGL renderer of the Qt Quick scenegraph. Its usage is not recommended in
+ new application code.
Where the QSGMaterial and QSGMaterialShader API requires a bit of
boilerplate code to create a functioning material, the
diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
index 28b177be84..7b96a3fdde 100644
--- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp
+++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
@@ -49,10 +49,10 @@ QT_BEGIN_NAMESPACE
solid filled rectangles using scenegraph.
\inmodule QtQuick
- \warning This utility class is only functional when running with the OpenGL
- or software backends of the Qt Quick scenegraph. For a proper cross-platform
- alternative prefer using QSGRectangleNode via
- QQuickWindow::createRectangleNode() or QSGEngine::createRectangleNode().
+ \warning This utility class is only functional when running with the default
+ or software backends of the Qt Quick scenegraph. As an alternative, prefer
+ using QSGRectangleNode via QQuickWindow::createRectangleNode() or
+ QSGEngine::createRectangleNode().
\deprecated
*/
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 0c49ca9aa5..1d0a423aa9 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -97,10 +97,10 @@ static void qsgsimpletexturenode_update(QSGGeometry *g,
\warning The simple texture node class must have a texture before being
added to the scene graph to be rendered.
- \warning This utility class is only functional when running with the OpenGL
- or software backends of the Qt Quick scenegraph. For a proper cross-platform
- alternative prefer using QSGImageNode via
- QQuickWindow::createImageNode() or QSGEngine::createImageNode().
+ \warning This utility class is only functional when running with the default
+ or software backends of the Qt Quick scenegraph. As an alternative, prefer
+ using QSGImageNode via QQuickWindow::createImageNode() or
+ QSGEngine::createImageNode().
\deprecated
*/
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 7b1d5abb26..a154ae269a 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -38,23 +38,20 @@
****************************************************************************/
#include "qsgtexturematerial_p.h"
-#include "qsgtexture_p.h"
+#include <private/qsgtexture_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/qopenglshaderprogram.h>
# include <QtGui/qopenglfunctions.h>
#endif
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(opengl)
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
-#endif
-
-QSGMaterialType QSGOpaqueTextureMaterialShader::type;
QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader()
{
@@ -122,6 +119,59 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
}
+QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"));
+}
+
+bool QSGOpaqueTextureMaterialRhiShader::updateUniformData(const RenderState &state, QSGMaterial *, QSGMaterial *)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QSGOpaqueTextureMaterialRhiShader::updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ if (binding != 1)
+ return;
+
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newMaterial);
+ QSGTexture *t = tx->texture();
+
+ t->setFiltering(tx->filtering());
+ t->setMipmapFiltering(tx->mipmapFiltering());
+ t->setAnisotropyLevel(tx->anisotropyLevel());
+
+ t->setHorizontalWrapMode(tx->horizontalWrapMode());
+ t->setVerticalWrapMode(tx->verticalWrapMode());
+ if (!state.rhi()->isFeatureSupported(QRhi::NPOTTextureRepeat)) {
+ QSize size = t->textureSize();
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ if (isNpot) {
+ t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ t->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ }
+ }
+
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
+
/*!
\class QSGOpaqueTextureMaterial
\brief The QSGOpaqueTextureMaterial class provides a convenient way of
@@ -129,8 +179,8 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The opaque textured material will fill every pixel in a geometry with
the supplied texture. The material does not respect the opacity of the
@@ -175,6 +225,7 @@ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial()
, m_vertical_wrap(QSGTexture::ClampToEdge)
, m_anisotropy_level(QSGTexture::AnisotropyNone)
{
+ setFlag(SupportsRhiShader, true);
}
@@ -183,7 +234,8 @@ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial()
*/
QSGMaterialType *QSGOpaqueTextureMaterial::type() const
{
- return &QSGOpaqueTextureMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
/*!
@@ -191,11 +243,13 @@ QSGMaterialType *QSGOpaqueTextureMaterial::type() const
*/
QSGMaterialShader *QSGOpaqueTextureMaterial::createShader() const
{
- return new QSGOpaqueTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGOpaqueTextureMaterialRhiShader;
+ else
+ return new QSGOpaqueTextureMaterialShader;
}
-
/*!
\fn QSGTexture *QSGOpaqueTextureMaterial::texture() const
@@ -323,7 +377,7 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o);
- if (int diff = m_texture->textureId() - other->texture()->textureId())
+ if (int diff = m_texture->comparisonKey() - other->texture()->comparisonKey())
return diff;
return int(m_filtering) - int(other->m_filtering);
}
@@ -337,8 +391,8 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The textured material will fill every pixel in a geometry with
the supplied texture.
@@ -363,32 +417,30 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
a material in the scene graph.
*/
-QSGMaterialType QSGTextureMaterialShader::type;
-
-
-
/*!
\internal
*/
QSGMaterialType *QSGTextureMaterial::type() const
{
- return &QSGTextureMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
-
-
/*!
\internal
*/
QSGMaterialShader *QSGTextureMaterial::createShader() const
{
- return new QSGTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGTextureMaterialRhiShader;
+ else
+ return new QSGTextureMaterialShader;
}
+
QSGTextureMaterialShader::QSGTextureMaterialShader()
- : QSGOpaqueTextureMaterialShader()
{
#if QT_CONFIG(opengl)
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag"));
@@ -413,4 +465,27 @@ void QSGTextureMaterialShader::initialize()
#endif
}
+
+QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"));
+}
+
+bool QSGTextureMaterialRhiShader::updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ changed = true;
+ }
+
+ changed |= QSGOpaqueTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ return changed;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h
index a99e872580..7ef17af526 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial_p.h
+++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h
@@ -64,15 +64,22 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
protected:
void initialize() override;
int m_matrix_id;
};
-class Q_QUICK_PRIVATE_EXPORT QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader
+class Q_QUICK_PRIVATE_EXPORT QSGOpaqueTextureMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGOpaqueTextureMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader
{
public:
QSGTextureMaterialShader();
@@ -80,12 +87,18 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
void initialize() override;
- static QSGMaterialType type;
-
protected:
int m_opacity_id;
};
+class QSGTextureMaterialRhiShader : public QSGOpaqueTextureMaterialRhiShader
+{
+public:
+ QSGTextureMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
QT_END_NAMESPACE
#endif // QSGTEXTUREMATERIAL_P_H
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
index cb61430e2e..23f57141a6 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
@@ -51,8 +51,6 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
private:
void initialize() override;
#if QT_CONFIG(opengl)
@@ -61,8 +59,6 @@ private:
#endif
};
-QSGMaterialType QSGVertexColorMaterialShader::type;
-
QSGVertexColorMaterialShader::QSGVertexColorMaterialShader()
{
#if QT_CONFIG(opengl)
@@ -99,6 +95,43 @@ void QSGVertexColorMaterialShader::initialize()
}
+class QSGVertexColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGVertexColorMaterialRhiShader();
+
+ bool updateUniformData(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+};
+
+QSGVertexColorMaterialRhiShader::QSGVertexColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.frag.qsb"));
+}
+
+bool QSGVertexColorMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial * /*newEffect*/,
+ QSGMaterial * /*oldEffect*/)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+
/*!
\class QSGVertexColorMaterial
\brief The QSGVertexColorMaterial class provides a convenient way of rendering per-vertex
@@ -107,8 +140,8 @@ void QSGVertexColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The vertex color material will give each vertex in a geometry a color. Pixels between
vertices will be linearly interpolated. The colors can contain transparency.
@@ -135,6 +168,7 @@ void QSGVertexColorMaterialShader::initialize()
QSGVertexColorMaterial::QSGVertexColorMaterial()
{
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
@@ -158,7 +192,8 @@ int QSGVertexColorMaterial::compare(const QSGMaterial * /* other */) const
QSGMaterialType *QSGVertexColorMaterial::type() const
{
- return &QSGVertexColorMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
@@ -169,7 +204,10 @@ QSGMaterialType *QSGVertexColorMaterial::type() const
QSGMaterialShader *QSGVertexColorMaterial::createShader() const
{
- return new QSGVertexColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGVertexColorMaterialRhiShader;
+ else
+ return new QSGVertexColorMaterialShader;
}
QT_END_NAMESPACE