Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgdefaultrendercontext.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
7
8#include <QtGui/QGuiApplication>
9
10#include <QtQuick/private/qsgbatchrenderer_p.h>
11#include <QtQuick/private/qsgrenderer_p.h>
12#include <QtQuick/private/qsgrhiatlastexture_p.h>
13#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
14#include <QtQuick/private/qsgmaterialshader_p.h>
15
16#include <QtQuick/private/qsgcompressedtexture_p.h>
17
18#include <QtQuick/qsgrendererinterface.h>
19#include <QtQuick/qquickgraphicsconfiguration.h>
20
22
23Q_STATIC_LOGGING_CATEGORY(lcGlyphCaches, "qt.scenegraph.text.glyphcache")
24
25/*!
26 \class QSGDefaultRenderContext
27 \inmodule QtQuick
28 \internal
29*/
30QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
31 : QSGRenderContext(context)
32 , m_rhi(nullptr)
33 , m_maxTextureSize(0)
34 , m_rhiAtlasManager(nullptr)
35 , m_currentFrameCommandBuffer(nullptr)
36 , m_currentFrameRenderPass(nullptr)
37 , m_useDepthBufferFor2D(true)
38 , m_glyphCacheResourceUpdates(nullptr)
39{
40}
41
42/*!
43 Initializes the scene graph render context with the GL context \a context. This also
44 emits the ready() signal so that the QML graph can start building scene graph nodes.
45 */
46void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params)
47{
48 if (!m_sg)
49 return;
50
51 const InitParams *initParams = static_cast<const InitParams *>(params);
52 if (initParams->sType != INIT_PARAMS_MAGIC)
53 qFatal("QSGDefaultRenderContext: Invalid parameters passed to initialize()");
54
55 m_initParams = *initParams;
56
57 m_rhi = m_initParams.rhi;
58 m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
59 if (!m_rhiAtlasManager)
60 m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
61
62 m_glyphCacheResourceUpdates = nullptr;
63
64 m_sg->renderContextInitialized(this);
65
66 emit initialized();
67}
68
69static QFontEngine *fontEngineOfRawFont(const QRawFont &font)
70{
71 QRawFontPrivate *d = QRawFontPrivate::get(font);
72 if (d != nullptr)
73 return d->fontEngine;
74 else
75 return nullptr;
76}
77
78void QSGDefaultRenderContext::flushGlyphCaches()
79{
80 auto it = m_staleGlyphCaches.begin();
81 while (it != m_staleGlyphCaches.end()) {
82 if (!(*it)->isActive()) {
83 qCDebug(lcGlyphCaches()) << "Deleting stale glyph cache:"
84 << (*it)
85 << "fontEngine:" << fontEngineOfRawFont((*it)->referenceFont())
86 << (*it)->referenceFont().familyName();
87 delete *it;
88 it = m_staleGlyphCaches.erase(it);
89 } else {
90 ++it;
91 }
92 }
93
94 {
95 auto it = m_fontEnginesToClean.begin();
96 while (it != m_fontEnginesToClean.end()) {
97 if (it.value() == 0) {
98 it.key()->clearGlyphCache(this);
99 if (!it.key()->ref.deref())
100 delete it.key();
101 it = m_fontEnginesToClean.erase(it);
102 } else {
103 ++it;
104 }
105 }
106 }
107}
108
109void QSGDefaultRenderContext::invalidateGlyphCaches()
110{
111 {
112 auto it = m_glyphCaches.begin();
113 while (it != m_glyphCaches.end()) {
114 if (!(*it)->isActive()) {
115 qCDebug(lcGlyphCaches()) << "Direct delete glyph cache:"
116 << it.value()
117 << "fontEngine:" << fontEngineOfRawFont(it.value()->referenceFont())
118 << it.value()->referenceFont().familyName()
119 << it.value()->referenceFont().styleName()
120 << ", key:"
121 << it.key().familyName
122 << it.key().styleName
123 << it.key().faceId.filename;
124 delete *it;
125 } else {
126 qCDebug(lcGlyphCaches()) << "Stale glyph cache:"
127 << it.value()
128 << "fontEngine:" << fontEngineOfRawFont(it.value()->referenceFont())
129 << it.value()->referenceFont().familyName()
130 << it.value()->referenceFont().styleName()
131 << ", key:"
132 << it.key().familyName
133 << it.key().styleName
134 << it.key().faceId.filename;
135
136 m_staleGlyphCaches.append(*it);
137 }
138
139 it = m_glyphCaches.erase(it);
140 }
141 }
142
143 qDeleteAll(m_curveGlyphAtlases);
144 m_curveGlyphAtlases.clear();
145}
146
147void QSGDefaultRenderContext::invalidate()
148{
149 if (!m_rhi)
150 return;
151
152 qDeleteAll(m_texturesToDelete);
153 m_texturesToDelete.clear();
154
155 qDeleteAll(m_textures);
156 m_textures.clear();
157
158 for (auto it = m_depthStencilBuffers.cbegin(), end = m_depthStencilBuffers.cend(); it != end; ++it) {
159 QSharedPointer<QSGDepthStencilBuffer> buf = it.value().toStrongRef();
160 delete buf->ds;
161 buf->ds = nullptr;
162 buf->rc = nullptr;
163 }
164 m_depthStencilBuffers.clear();
165
166 /* The cleanup of the atlas textures is a bit intriguing.
167 As part of the cleanup in the threaded render loop, we
168 do:
169 1. call this function
170 2. call QCoreApp::sendPostedEvents() to immediately process
171 any pending deferred deletes.
172 3. delete the GL context.
173
174 As textures need the atlas manager while cleaning up, the
175 manager needs to be cleaned up after the textures, so
176 we post a deleteLater here at the very bottom so it gets
177 deferred deleted last.
178
179 Another alternative would be to use a QPointer in
180 QSGOpenGLAtlasTexture::Texture, but this seemed simpler.
181 */
182 if (m_rhiAtlasManager) {
183 m_rhiAtlasManager->invalidate();
184 m_rhiAtlasManager->deleteLater();
185 m_rhiAtlasManager = nullptr;
186 }
187
188 // The following piece of code will read/write to the font engine's caches,
189 // potentially from different threads. However, this is safe because this
190 // code is only called from QQuickWindow's shutdown which is called
191 // only when the GUI is blocked, and multiple threads will call it in
192 // sequence. (see qsgdefaultglyphnode_p.cpp's init())
193 for (auto it = m_fontEnginesToClean.constBegin(); it != m_fontEnginesToClean.constEnd(); ++it) {
194 it.key()->clearGlyphCache(this);
195 if (!it.key()->ref.deref())
196 delete it.key();
197 }
198 m_fontEnginesToClean.clear();
199
200 qDeleteAll(m_curveGlyphAtlases);
201 m_curveGlyphAtlases.clear();
202
203 qCDebug(lcGlyphCaches()) << "Deleting" << m_glyphCaches.size() << "live glyph caches";
204
205 qDeleteAll(m_glyphCaches);
206 m_glyphCaches.clear();
207
208 qCDebug(lcGlyphCaches()) << "Deleting" << m_staleGlyphCaches.size() << "stale glyph caches";
209
210 qDeleteAll(m_staleGlyphCaches);
211 m_staleGlyphCaches.clear();
212
213 resetGlyphCacheResources();
214
215 m_rhi = nullptr;
216
217 if (m_sg)
218 m_sg->renderContextInvalidated(this);
219
220 emit invalidated();
221}
222
223void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio,
224 QRhiCommandBuffer *cb,
225 const QQuickGraphicsConfiguration &config)
226{
227 m_currentDevicePixelRatio = devicePixelRatio;
228 m_useDepthBufferFor2D = config.isDepthBufferEnabledFor2D();
229
230 // we store the command buffer already here, in case there is something in
231 // an updatePaintNode() implementation that leads to needing it (for
232 // example, an updateTexture() call on a QSGRhiLayer)
233 m_currentFrameCommandBuffer = cb;
234}
235
236void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer, const QSGRenderTarget &renderTarget,
237 RenderPassCallback mainPassRecordingStart,
238 RenderPassCallback mainPassRecordingEnd,
239 void *callbackUserData)
240{
241 renderer->setRenderTarget(renderTarget);
242 renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
243
244 m_currentFrameCommandBuffer = renderTarget.cb; // usually the same as what was passed to prepareSync() but cannot count on that having been called
245 m_currentFrameRenderPass = renderTarget.rpDesc;
246}
247
248void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer)
249{
250 renderer->renderScene();
251}
252
253void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
254{
255 Q_UNUSED(renderer);
256
257 // In case glyphs were generated in this frame but no glyph node was
258 // rendered because all Text items in the scene were invisible.
259 if (m_glyphCacheResourceUpdates) {
260 if (m_currentFrameCommandBuffer)
261 m_currentFrameCommandBuffer->resourceUpdate(m_glyphCacheResourceUpdates);
262 else
263 m_glyphCacheResourceUpdates->release();
264 m_glyphCacheResourceUpdates = nullptr;
265 }
266
267 m_currentFrameCommandBuffer = nullptr;
268 m_currentFrameRenderPass = nullptr;
269}
270
271QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const
272{
273 bool atlas = flags & CreateTexture_Atlas;
274 bool mipmap = flags & CreateTexture_Mipmap;
275 bool alpha = flags & CreateTexture_Alpha;
276
277 // The atlas implementation is only supported from the render thread and
278 // does not support mipmaps.
279 if (m_rhi) {
280 if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
281 QSGTexture *t = m_rhiAtlasManager->create(image, alpha);
282 if (t)
283 return t;
284 }
285 }
286
287 QSGPlainTexture *texture = new QSGPlainTexture;
288 texture->setImage(image);
289 if (texture->hasAlphaChannel() && !alpha)
290 texture->setHasAlphaChannel(false);
291
292 return texture;
293}
294
295QSGRenderer *QSGDefaultRenderContext::createRenderer(QSGRendererInterface::RenderMode renderMode)
296{
297 return new QSGBatchRenderer::Renderer(this, renderMode);
298}
299
300QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
301{
302 // This is only used for atlasing compressed textures. Returning null implies no atlas.
303
304 if (m_rhi && QThread::currentThread() == m_rhi->thread())
305 return m_rhiAtlasManager->create(factory);
306
307 return nullptr;
308}
309
310void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant)
311{
312 QSGMaterialShaderPrivate::get(shader)->prepare(shaderVariant);
313}
314
315void QSGDefaultRenderContext::preprocess()
316{
317 for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
318 it.value()->processPendingGlyphs();
319 it.value()->update();
320 }
321
322 for (auto it = m_staleGlyphCaches.begin(); it != m_staleGlyphCaches.end(); ++it) {
323 (*it)->processPendingGlyphs();
324 (*it)->update();
325 }
326}
327
328QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font)
329{
330 FontKey key = FontKey(font, 0);
331 QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, nullptr);
332 if (atlas == nullptr) {
333 atlas = new QSGCurveGlyphAtlas(font);
334 m_curveGlyphAtlases.insert(key, atlas);
335 }
336
337 return atlas;
338}
339
340QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
341{
342 FontKey key(font, renderTypeQuality);
343 QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
344 if (!cache && font.isValid()) {
345 cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality);
346 m_glyphCaches.insert(key, cache);
347
348 qCDebug(lcGlyphCaches()) << "Creating new glyph cache:"
349 << cache
350 << "fontEngine:" << fontEngineOfRawFont(cache->referenceFont())
351 << cache->referenceFont().familyName()
352 << cache->referenceFont().styleName()
353 << ", key:"
354 << key.familyName
355 << key.styleName
356 << key.faceId.filename;
357 } else {
358 qCDebug(lcGlyphCaches()) << "Found existing glyph cache:"
359 << cache
360 << "fontEngine:" << fontEngineOfRawFont(cache->referenceFont())
361 << cache->referenceFont().familyName()
362 << cache->referenceFont().styleName()
363 << ", key:"
364 << key.familyName
365 << key.styleName
366 << key.faceId.filename;
367 }
368
369 return cache;
370}
371
372QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates()
373{
374 return m_glyphCacheResourceUpdates;
375}
376
377QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates()
378{
379 if (!m_glyphCacheResourceUpdates)
380 m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch();
381
382 return m_glyphCacheResourceUpdates;
383}
384
385void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture)
386{
387 if (texture)
388 m_pendingGlyphCacheTextures.insert(texture);
389}
390
391void QSGDefaultRenderContext::resetGlyphCacheResources()
392{
393 if (m_glyphCacheResourceUpdates) {
394 m_glyphCacheResourceUpdates->release();
395 m_glyphCacheResourceUpdates = nullptr;
396 }
397
398 for (QRhiTexture *t : std::as_const(m_pendingGlyphCacheTextures))
399 t->deleteLater(); // the QRhiTexture object stays valid for the current frame
400
401 m_pendingGlyphCacheTextures.clear();
402}
403
405{
406 if (rc && ds) {
407 const auto key = std::pair(ds->pixelSize(), ds->sampleCount());
408 rc->m_depthStencilBuffers.remove(key);
409 }
410 delete ds;
411}
412
413QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::getDepthStencilBuffer(const QSize &size, int sampleCount)
414{
415 const auto key = std::pair(size, sampleCount);
416 auto it = m_depthStencilBuffers.constFind(key);
417 if (it != m_depthStencilBuffers.cend()) {
418 if (it.value())
419 return it.value().toStrongRef();
420 }
421
422 QRhiRenderBuffer *ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, sampleCount);
423 if (!ds->create()) {
424 qWarning("Failed to build depth-stencil buffer for layer");
425 delete ds;
426 return {};
427 }
428 QSharedPointer<QSGDepthStencilBuffer> buf(new QSGDepthStencilBuffer(ds));
429 addDepthStencilBuffer(buf);
430 return buf;
431}
432
433void QSGDefaultRenderContext::addDepthStencilBuffer(const QSharedPointer<QSGDepthStencilBuffer> &ds)
434{
435 if (ds) {
436 const auto key = std::pair(ds->ds->pixelSize(), ds->ds->sampleCount());
437 ds->rc = this;
438 m_depthStencilBuffers.insert(key, ds.toWeakRef());
439 }
440}
441
442QT_END_NAMESPACE
443
444#include "moc_qsgdefaultrendercontext_p.cpp"
QSGDefaultRenderContext * rc
Combined button and popup list for selecting options.
static QFontEngine * fontEngineOfRawFont(const QRawFont &font)