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 m_currentFrameCommandBuffer = nullptr;
257 m_currentFrameRenderPass = nullptr;
258}
259
260QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const
261{
262 bool atlas = flags & CreateTexture_Atlas;
263 bool mipmap = flags & CreateTexture_Mipmap;
264 bool alpha = flags & CreateTexture_Alpha;
265
266 // The atlas implementation is only supported from the render thread and
267 // does not support mipmaps.
268 if (m_rhi) {
269 if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
270 QSGTexture *t = m_rhiAtlasManager->create(image, alpha);
271 if (t)
272 return t;
273 }
274 }
275
276 QSGPlainTexture *texture = new QSGPlainTexture;
277 texture->setImage(image);
278 if (texture->hasAlphaChannel() && !alpha)
279 texture->setHasAlphaChannel(false);
280
281 return texture;
282}
283
284QSGRenderer *QSGDefaultRenderContext::createRenderer(QSGRendererInterface::RenderMode renderMode)
285{
286 return new QSGBatchRenderer::Renderer(this, renderMode);
287}
288
289QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
290{
291 // This is only used for atlasing compressed textures. Returning null implies no atlas.
292
293 if (m_rhi && QThread::currentThread() == m_rhi->thread())
294 return m_rhiAtlasManager->create(factory);
295
296 return nullptr;
297}
298
299void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant)
300{
301 QSGMaterialShaderPrivate::get(shader)->prepare(shaderVariant);
302}
303
304void QSGDefaultRenderContext::preprocess()
305{
306 for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
307 it.value()->processPendingGlyphs();
308 it.value()->update();
309 }
310
311 for (auto it = m_staleGlyphCaches.begin(); it != m_staleGlyphCaches.end(); ++it) {
312 (*it)->processPendingGlyphs();
313 (*it)->update();
314 }
315}
316
317QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font)
318{
319 FontKey key = FontKey(font, 0);
320 QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, nullptr);
321 if (atlas == nullptr) {
322 atlas = new QSGCurveGlyphAtlas(font);
323 m_curveGlyphAtlases.insert(key, atlas);
324 }
325
326 return atlas;
327}
328
329QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
330{
331 FontKey key(font, renderTypeQuality);
332 QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
333 if (!cache && font.isValid()) {
334 cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality);
335 m_glyphCaches.insert(key, cache);
336
337 qCDebug(lcGlyphCaches()) << "Creating new glyph cache:"
338 << cache
339 << "fontEngine:" << fontEngineOfRawFont(cache->referenceFont())
340 << cache->referenceFont().familyName()
341 << cache->referenceFont().styleName()
342 << ", key:"
343 << key.familyName
344 << key.styleName
345 << key.faceId.filename;
346 } else {
347 qCDebug(lcGlyphCaches()) << "Found existing glyph cache:"
348 << cache
349 << "fontEngine:" << fontEngineOfRawFont(cache->referenceFont())
350 << cache->referenceFont().familyName()
351 << cache->referenceFont().styleName()
352 << ", key:"
353 << key.familyName
354 << key.styleName
355 << key.faceId.filename;
356 }
357
358 return cache;
359}
360
361QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates()
362{
363 return m_glyphCacheResourceUpdates;
364}
365
366QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates()
367{
368 if (!m_glyphCacheResourceUpdates)
369 m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch();
370
371 return m_glyphCacheResourceUpdates;
372}
373
374void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture)
375{
376 if (texture)
377 m_pendingGlyphCacheTextures.insert(texture);
378}
379
380void QSGDefaultRenderContext::resetGlyphCacheResources()
381{
382 if (m_glyphCacheResourceUpdates) {
383 m_glyphCacheResourceUpdates->release();
384 m_glyphCacheResourceUpdates = nullptr;
385 }
386
387 for (QRhiTexture *t : std::as_const(m_pendingGlyphCacheTextures))
388 t->deleteLater(); // the QRhiTexture object stays valid for the current frame
389
390 m_pendingGlyphCacheTextures.clear();
391}
392
394{
395 if (rc && ds) {
396 const auto key = std::pair(ds->pixelSize(), ds->sampleCount());
397 rc->m_depthStencilBuffers.remove(key);
398 }
399 delete ds;
400}
401
402QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::getDepthStencilBuffer(const QSize &size, int sampleCount)
403{
404 const auto key = std::pair(size, sampleCount);
405 auto it = m_depthStencilBuffers.constFind(key);
406 if (it != m_depthStencilBuffers.cend()) {
407 if (it.value())
408 return it.value().toStrongRef();
409 }
410
411 QRhiRenderBuffer *ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, sampleCount);
412 if (!ds->create()) {
413 qWarning("Failed to build depth-stencil buffer for layer");
414 delete ds;
415 return {};
416 }
417 QSharedPointer<QSGDepthStencilBuffer> buf(new QSGDepthStencilBuffer(ds));
418 addDepthStencilBuffer(buf);
419 return buf;
420}
421
422void QSGDefaultRenderContext::addDepthStencilBuffer(const QSharedPointer<QSGDepthStencilBuffer> &ds)
423{
424 if (ds) {
425 const auto key = std::pair(ds->ds->pixelSize(), ds->ds->sampleCount());
426 ds->rc = this;
427 m_depthStencilBuffers.insert(key, ds.toWeakRef());
428 }
429}
430
431QT_END_NAMESPACE
432
433#include "moc_qsgdefaultrendercontext_p.cpp"
QSGDefaultRenderContext * rc
Combined button and popup list for selecting options.
static QFontEngine * fontEngineOfRawFont(const QRawFont &font)