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 | |
4 | #include "qsgdefaultrendercontext_p.h" |
5 | #include "qsgcurveglyphatlas_p.h" |
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 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context) |
23 | : QSGRenderContext(context) |
24 | , m_rhi(nullptr) |
25 | , m_maxTextureSize(0) |
26 | , m_rhiAtlasManager(nullptr) |
27 | , m_currentFrameCommandBuffer(nullptr) |
28 | , m_currentFrameRenderPass(nullptr) |
29 | , m_useDepthBufferFor2D(true) |
30 | , m_glyphCacheResourceUpdates(nullptr) |
31 | { |
32 | } |
33 | |
34 | /*! |
35 | Initializes the scene graph render context with the GL context \a context. This also |
36 | emits the ready() signal so that the QML graph can start building scene graph nodes. |
37 | */ |
38 | void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params) |
39 | { |
40 | if (!m_sg) |
41 | return; |
42 | |
43 | const InitParams *initParams = static_cast<const InitParams *>(params); |
44 | if (initParams->sType != INIT_PARAMS_MAGIC) |
45 | qFatal(msg: "QSGDefaultRenderContext: Invalid parameters passed to initialize()"); |
46 | |
47 | m_initParams = *initParams; |
48 | |
49 | m_rhi = m_initParams.rhi; |
50 | m_maxTextureSize = m_rhi->resourceLimit(limit: QRhi::TextureSizeMax); |
51 | if (!m_rhiAtlasManager) |
52 | m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface); |
53 | |
54 | m_glyphCacheResourceUpdates = nullptr; |
55 | |
56 | m_sg->renderContextInitialized(renderContext: this); |
57 | |
58 | emit initialized(); |
59 | } |
60 | |
61 | void QSGDefaultRenderContext::invalidateGlyphCaches() |
62 | { |
63 | { |
64 | auto it = m_glyphCaches.begin(); |
65 | while (it != m_glyphCaches.end()) { |
66 | if (!(*it)->isActive()) { |
67 | delete *it; |
68 | it = m_glyphCaches.erase(it); |
69 | } else { |
70 | ++it; |
71 | } |
72 | } |
73 | } |
74 | |
75 | qDeleteAll(c: m_curveGlyphAtlases); |
76 | m_curveGlyphAtlases.clear(); |
77 | |
78 | { |
79 | auto it = m_fontEnginesToClean.begin(); |
80 | while (it != m_fontEnginesToClean.end()) { |
81 | if (it.value() == 0) { |
82 | it.key()->clearGlyphCache(key: this); |
83 | if (!it.key()->ref.deref()) |
84 | delete it.key(); |
85 | it = m_fontEnginesToClean.erase(it); |
86 | } else { |
87 | ++it; |
88 | } |
89 | } |
90 | } |
91 | } |
92 | |
93 | void QSGDefaultRenderContext::invalidate() |
94 | { |
95 | if (!m_rhi) |
96 | return; |
97 | |
98 | qDeleteAll(c: m_texturesToDelete); |
99 | m_texturesToDelete.clear(); |
100 | |
101 | qDeleteAll(c: m_textures); |
102 | m_textures.clear(); |
103 | |
104 | /* The cleanup of the atlas textures is a bit intriguing. |
105 | As part of the cleanup in the threaded render loop, we |
106 | do: |
107 | 1. call this function |
108 | 2. call QCoreApp::sendPostedEvents() to immediately process |
109 | any pending deferred deletes. |
110 | 3. delete the GL context. |
111 | |
112 | As textures need the atlas manager while cleaning up, the |
113 | manager needs to be cleaned up after the textures, so |
114 | we post a deleteLater here at the very bottom so it gets |
115 | deferred deleted last. |
116 | |
117 | Another alternative would be to use a QPointer in |
118 | QSGOpenGLAtlasTexture::Texture, but this seemed simpler. |
119 | */ |
120 | if (m_rhiAtlasManager) { |
121 | m_rhiAtlasManager->invalidate(); |
122 | m_rhiAtlasManager->deleteLater(); |
123 | m_rhiAtlasManager = nullptr; |
124 | } |
125 | |
126 | // The following piece of code will read/write to the font engine's caches, |
127 | // potentially from different threads. However, this is safe because this |
128 | // code is only called from QQuickWindow's shutdown which is called |
129 | // only when the GUI is blocked, and multiple threads will call it in |
130 | // sequence. (see qsgdefaultglyphnode_p.cpp's init()) |
131 | for (auto it = m_fontEnginesToClean.constBegin(); it != m_fontEnginesToClean.constEnd(); ++it) { |
132 | it.key()->clearGlyphCache(key: this); |
133 | if (!it.key()->ref.deref()) |
134 | delete it.key(); |
135 | } |
136 | m_fontEnginesToClean.clear(); |
137 | |
138 | qDeleteAll(c: m_curveGlyphAtlases); |
139 | m_curveGlyphAtlases.clear(); |
140 | |
141 | qDeleteAll(c: m_glyphCaches); |
142 | m_glyphCaches.clear(); |
143 | |
144 | resetGlyphCacheResources(); |
145 | |
146 | m_rhi = nullptr; |
147 | |
148 | if (m_sg) |
149 | m_sg->renderContextInvalidated(renderContext: this); |
150 | |
151 | emit invalidated(); |
152 | } |
153 | |
154 | void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio, |
155 | QRhiCommandBuffer *cb, |
156 | const QQuickGraphicsConfiguration &config) |
157 | { |
158 | m_currentDevicePixelRatio = devicePixelRatio; |
159 | m_useDepthBufferFor2D = config.isDepthBufferEnabledFor2D(); |
160 | |
161 | // we store the command buffer already here, in case there is something in |
162 | // an updatePaintNode() implementation that leads to needing it (for |
163 | // example, an updateTexture() call on a QSGRhiLayer) |
164 | m_currentFrameCommandBuffer = cb; |
165 | } |
166 | |
167 | void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer, const QSGRenderTarget &renderTarget, |
168 | RenderPassCallback mainPassRecordingStart, |
169 | RenderPassCallback mainPassRecordingEnd, |
170 | void *callbackUserData) |
171 | { |
172 | renderer->setRenderTarget(renderTarget); |
173 | renderer->setRenderPassRecordingCallbacks(start: mainPassRecordingStart, end: mainPassRecordingEnd, userData: callbackUserData); |
174 | |
175 | m_currentFrameCommandBuffer = renderTarget.cb; // usually the same as what was passed to prepareSync() but cannot count on that having been called |
176 | m_currentFrameRenderPass = renderTarget.rpDesc; |
177 | } |
178 | |
179 | void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer) |
180 | { |
181 | renderer->renderScene(); |
182 | } |
183 | |
184 | void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer) |
185 | { |
186 | Q_UNUSED(renderer); |
187 | m_currentFrameCommandBuffer = nullptr; |
188 | m_currentFrameRenderPass = nullptr; |
189 | } |
190 | |
191 | QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const |
192 | { |
193 | bool atlas = flags & CreateTexture_Atlas; |
194 | bool mipmap = flags & CreateTexture_Mipmap; |
195 | bool alpha = flags & CreateTexture_Alpha; |
196 | |
197 | // The atlas implementation is only supported from the render thread and |
198 | // does not support mipmaps. |
199 | if (m_rhi) { |
200 | if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) { |
201 | QSGTexture *t = m_rhiAtlasManager->create(image, hasAlphaChannel: alpha); |
202 | if (t) |
203 | return t; |
204 | } |
205 | } |
206 | |
207 | QSGPlainTexture *texture = new QSGPlainTexture; |
208 | texture->setImage(image); |
209 | if (texture->hasAlphaChannel() && !alpha) |
210 | texture->setHasAlphaChannel(false); |
211 | |
212 | return texture; |
213 | } |
214 | |
215 | QSGRenderer *QSGDefaultRenderContext::createRenderer(QSGRendererInterface::RenderMode renderMode) |
216 | { |
217 | return new QSGBatchRenderer::Renderer(this, renderMode); |
218 | } |
219 | |
220 | QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const |
221 | { |
222 | // This is only used for atlasing compressed textures. Returning null implies no atlas. |
223 | |
224 | if (m_rhi && QThread::currentThread() == m_rhi->thread()) |
225 | return m_rhiAtlasManager->create(factory); |
226 | |
227 | return nullptr; |
228 | } |
229 | |
230 | void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant) |
231 | { |
232 | QSGMaterialShaderPrivate::get(s: shader)->prepare(vertexShaderVariant: shaderVariant); |
233 | } |
234 | |
235 | void QSGDefaultRenderContext::preprocess() |
236 | { |
237 | for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) { |
238 | it.value()->processPendingGlyphs(); |
239 | it.value()->update(); |
240 | } |
241 | } |
242 | |
243 | QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font) |
244 | { |
245 | FontKey key = FontKey(font, 0); |
246 | QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, defaultValue: nullptr); |
247 | if (atlas == nullptr) { |
248 | atlas = new QSGCurveGlyphAtlas(font); |
249 | m_curveGlyphAtlases.insert(key, value: atlas); |
250 | } |
251 | |
252 | return atlas; |
253 | } |
254 | |
255 | QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality) |
256 | { |
257 | FontKey key(font, renderTypeQuality); |
258 | QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, defaultValue: 0); |
259 | if (!cache && font.isValid()) { |
260 | cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality); |
261 | m_glyphCaches.insert(key, value: cache); |
262 | } |
263 | |
264 | return cache; |
265 | } |
266 | |
267 | QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates() |
268 | { |
269 | return m_glyphCacheResourceUpdates; |
270 | } |
271 | |
272 | QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates() |
273 | { |
274 | if (!m_glyphCacheResourceUpdates) |
275 | m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch(); |
276 | |
277 | return m_glyphCacheResourceUpdates; |
278 | } |
279 | |
280 | void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture) |
281 | { |
282 | if (texture) |
283 | m_pendingGlyphCacheTextures.insert(value: texture); |
284 | } |
285 | |
286 | void QSGDefaultRenderContext::resetGlyphCacheResources() |
287 | { |
288 | if (m_glyphCacheResourceUpdates) { |
289 | m_glyphCacheResourceUpdates->release(); |
290 | m_glyphCacheResourceUpdates = nullptr; |
291 | } |
292 | |
293 | for (QRhiTexture *t : std::as_const(t&: m_pendingGlyphCacheTextures)) |
294 | t->deleteLater(); // the QRhiTexture object stays valid for the current frame |
295 | |
296 | m_pendingGlyphCacheTextures.clear(); |
297 | } |
298 | |
299 | QT_END_NAMESPACE |
300 | |
301 | #include "moc_qsgdefaultrendercontext_p.cpp" |
302 |
Definitions
- QSGDefaultRenderContext
- initialize
- invalidateGlyphCaches
- invalidate
- prepareSync
- beginNextFrame
- renderNextFrame
- endNextFrame
- createTexture
- createRenderer
- compressedTextureForFactory
- initializeRhiShader
- preprocess
- curveGlyphAtlas
- distanceFieldGlyphCache
- maybeGlyphCacheResourceUpdates
- glyphCacheResourceUpdates
- deferredReleaseGlyphCacheTexture
Start learning QML with our Intro Training
Find out more