1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgdefaultrendercontext_p.h"
5#include "qsgcurveglyphatlas_p.h"
6
7#include <QtGui/QGuiApplication>
8
9#include <QtQuick/private/qsgbatchrenderer_p.h>
10#include <QtQuick/private/qsgrenderer_p.h>
11#include <QtQuick/private/qsgrhiatlastexture_p.h>
12#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
13#include <QtQuick/private/qsgmaterialshader_p.h>
14
15#include <QtQuick/private/qsgcompressedtexture_p.h>
16
17#include <QtQuick/qsgrendererinterface.h>
18#include <QtQuick/qquickgraphicsconfiguration.h>
19
20QT_BEGIN_NAMESPACE
21
22QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
23 : QSGRenderContext(context)
24 , m_rhi(nullptr)
25 , m_maxTextureSize(0)
26 , m_rhiAtlasManager(nullptr)
27 , m_currentFrameCommandBuffer(nullptr)
28 , m_currentFrameRenderPass(nullptr)
29 , m_useDepthBufferFor2D(true)
30 , m_glyphCacheResourceUpdates(nullptr)
31{
32}
33
34/*!
35 Initializes the scene graph render context with the GL context \a context. This also
36 emits the ready() signal so that the QML graph can start building scene graph nodes.
37 */
38void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params)
39{
40 if (!m_sg)
41 return;
42
43 const InitParams *initParams = static_cast<const InitParams *>(params);
44 if (initParams->sType != INIT_PARAMS_MAGIC)
45 qFatal(msg: "QSGDefaultRenderContext: Invalid parameters passed to initialize()");
46
47 m_initParams = *initParams;
48
49 m_rhi = m_initParams.rhi;
50 m_maxTextureSize = m_rhi->resourceLimit(limit: QRhi::TextureSizeMax);
51 if (!m_rhiAtlasManager)
52 m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
53
54 m_glyphCacheResourceUpdates = nullptr;
55
56 m_sg->renderContextInitialized(renderContext: this);
57
58 emit initialized();
59}
60
61void QSGDefaultRenderContext::invalidateGlyphCaches()
62{
63 {
64 auto it = m_glyphCaches.begin();
65 while (it != m_glyphCaches.end()) {
66 if (!(*it)->isActive()) {
67 delete *it;
68 it = m_glyphCaches.erase(it);
69 } else {
70 ++it;
71 }
72 }
73 }
74
75 qDeleteAll(c: m_curveGlyphAtlases);
76 m_curveGlyphAtlases.clear();
77
78 {
79 auto it = m_fontEnginesToClean.begin();
80 while (it != m_fontEnginesToClean.end()) {
81 if (it.value() == 0) {
82 it.key()->clearGlyphCache(key: this);
83 if (!it.key()->ref.deref())
84 delete it.key();
85 it = m_fontEnginesToClean.erase(it);
86 } else {
87 ++it;
88 }
89 }
90 }
91}
92
93void QSGDefaultRenderContext::invalidate()
94{
95 if (!m_rhi)
96 return;
97
98 qDeleteAll(c: m_texturesToDelete);
99 m_texturesToDelete.clear();
100
101 qDeleteAll(c: m_textures);
102 m_textures.clear();
103
104 /* The cleanup of the atlas textures is a bit intriguing.
105 As part of the cleanup in the threaded render loop, we
106 do:
107 1. call this function
108 2. call QCoreApp::sendPostedEvents() to immediately process
109 any pending deferred deletes.
110 3. delete the GL context.
111
112 As textures need the atlas manager while cleaning up, the
113 manager needs to be cleaned up after the textures, so
114 we post a deleteLater here at the very bottom so it gets
115 deferred deleted last.
116
117 Another alternative would be to use a QPointer in
118 QSGOpenGLAtlasTexture::Texture, but this seemed simpler.
119 */
120 if (m_rhiAtlasManager) {
121 m_rhiAtlasManager->invalidate();
122 m_rhiAtlasManager->deleteLater();
123 m_rhiAtlasManager = nullptr;
124 }
125
126 // The following piece of code will read/write to the font engine's caches,
127 // potentially from different threads. However, this is safe because this
128 // code is only called from QQuickWindow's shutdown which is called
129 // only when the GUI is blocked, and multiple threads will call it in
130 // sequence. (see qsgdefaultglyphnode_p.cpp's init())
131 for (auto it = m_fontEnginesToClean.constBegin(); it != m_fontEnginesToClean.constEnd(); ++it) {
132 it.key()->clearGlyphCache(key: this);
133 if (!it.key()->ref.deref())
134 delete it.key();
135 }
136 m_fontEnginesToClean.clear();
137
138 qDeleteAll(c: m_curveGlyphAtlases);
139 m_curveGlyphAtlases.clear();
140
141 qDeleteAll(c: m_glyphCaches);
142 m_glyphCaches.clear();
143
144 resetGlyphCacheResources();
145
146 m_rhi = nullptr;
147
148 if (m_sg)
149 m_sg->renderContextInvalidated(renderContext: this);
150
151 emit invalidated();
152}
153
154void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio,
155 QRhiCommandBuffer *cb,
156 const QQuickGraphicsConfiguration &config)
157{
158 m_currentDevicePixelRatio = devicePixelRatio;
159 m_useDepthBufferFor2D = config.isDepthBufferEnabledFor2D();
160
161 // we store the command buffer already here, in case there is something in
162 // an updatePaintNode() implementation that leads to needing it (for
163 // example, an updateTexture() call on a QSGRhiLayer)
164 m_currentFrameCommandBuffer = cb;
165}
166
167void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer, const QSGRenderTarget &renderTarget,
168 RenderPassCallback mainPassRecordingStart,
169 RenderPassCallback mainPassRecordingEnd,
170 void *callbackUserData)
171{
172 renderer->setRenderTarget(renderTarget);
173 renderer->setRenderPassRecordingCallbacks(start: mainPassRecordingStart, end: mainPassRecordingEnd, userData: callbackUserData);
174
175 m_currentFrameCommandBuffer = renderTarget.cb; // usually the same as what was passed to prepareSync() but cannot count on that having been called
176 m_currentFrameRenderPass = renderTarget.rpDesc;
177}
178
179void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer)
180{
181 renderer->renderScene();
182}
183
184void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
185{
186 Q_UNUSED(renderer);
187 m_currentFrameCommandBuffer = nullptr;
188 m_currentFrameRenderPass = nullptr;
189}
190
191QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const
192{
193 bool atlas = flags & CreateTexture_Atlas;
194 bool mipmap = flags & CreateTexture_Mipmap;
195 bool alpha = flags & CreateTexture_Alpha;
196
197 // The atlas implementation is only supported from the render thread and
198 // does not support mipmaps.
199 if (m_rhi) {
200 if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
201 QSGTexture *t = m_rhiAtlasManager->create(image, hasAlphaChannel: alpha);
202 if (t)
203 return t;
204 }
205 }
206
207 QSGPlainTexture *texture = new QSGPlainTexture;
208 texture->setImage(image);
209 if (texture->hasAlphaChannel() && !alpha)
210 texture->setHasAlphaChannel(false);
211
212 return texture;
213}
214
215QSGRenderer *QSGDefaultRenderContext::createRenderer(QSGRendererInterface::RenderMode renderMode)
216{
217 return new QSGBatchRenderer::Renderer(this, renderMode);
218}
219
220QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
221{
222 // This is only used for atlasing compressed textures. Returning null implies no atlas.
223
224 if (m_rhi && QThread::currentThread() == m_rhi->thread())
225 return m_rhiAtlasManager->create(factory);
226
227 return nullptr;
228}
229
230void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant)
231{
232 QSGMaterialShaderPrivate::get(s: shader)->prepare(vertexShaderVariant: shaderVariant);
233}
234
235void QSGDefaultRenderContext::preprocess()
236{
237 for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
238 it.value()->processPendingGlyphs();
239 it.value()->update();
240 }
241}
242
243QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font)
244{
245 FontKey key = FontKey(font, 0);
246 QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, defaultValue: nullptr);
247 if (atlas == nullptr) {
248 atlas = new QSGCurveGlyphAtlas(font);
249 m_curveGlyphAtlases.insert(key, value: atlas);
250 }
251
252 return atlas;
253}
254
255QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
256{
257 FontKey key(font, renderTypeQuality);
258 QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, defaultValue: 0);
259 if (!cache && font.isValid()) {
260 cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality);
261 m_glyphCaches.insert(key, value: cache);
262 }
263
264 return cache;
265}
266
267QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates()
268{
269 return m_glyphCacheResourceUpdates;
270}
271
272QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates()
273{
274 if (!m_glyphCacheResourceUpdates)
275 m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch();
276
277 return m_glyphCacheResourceUpdates;
278}
279
280void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture)
281{
282 if (texture)
283 m_pendingGlyphCacheTextures.insert(value: texture);
284}
285
286void QSGDefaultRenderContext::resetGlyphCacheResources()
287{
288 if (m_glyphCacheResourceUpdates) {
289 m_glyphCacheResourceUpdates->release();
290 m_glyphCacheResourceUpdates = nullptr;
291 }
292
293 for (QRhiTexture *t : std::as_const(t&: m_pendingGlyphCacheTextures))
294 t->deleteLater(); // the QRhiTexture object stays valid for the current frame
295
296 m_pendingGlyphCacheTextures.clear();
297}
298
299QT_END_NAMESPACE
300
301#include "moc_qsgdefaultrendercontext_p.cpp"
302

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quick/scenegraph/qsgdefaultrendercontext.cpp