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