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