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