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
qsgadaptationlayer.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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
5
6#include <qmath.h>
7#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
8#include <QtQuick/private/qsgcontext_p.h>
9#include <private/qrawfont_p.h>
10#include <QtGui/qguiapplication.h>
11#include <qdir.h>
12#include <qsgrendernode.h>
13
14#include <private/qquickprofiler_p.h>
15#include <QElapsedTimer>
16
17#include <qtquick_tracepoints_p.h>
18
19QT_BEGIN_NAMESPACE
20
21Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_entry, int count)
22Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_exit)
23Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_entry)
24Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_exit)
25Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_entry)
26Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_exit)
27
28static QElapsedTimer qsg_render_timer;
29
30QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
31
32QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
33 : m_renderTypeQuality(renderTypeQuality)
34 , m_pendingGlyphs(64)
35{
36 Q_ASSERT(font.isValid());
37
38 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
39 m_glyphCount = fontD->fontEngine->glyphCount();
40
41 m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
42
43 m_referenceFont = font;
44 // we set the same pixel size as used by the distance field internally.
45 // this allows us to call pathForGlyph once and reuse the result.
46 m_referenceFont.setPixelSize(baseFontSize() * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
47 Q_ASSERT(m_referenceFont.isValid());
48}
49
50QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
51{
52}
53
54int QSGDistanceFieldGlyphCache::baseFontSize() const
55{
56 return m_renderTypeQuality > 0 ? m_renderTypeQuality : QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
57}
58
59QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
60{
61 GlyphData gd;
62 gd.texture = &s_emptyTexture;
63 QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
64 return it.value();
65}
66
67QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
68{
69 QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
70 if (data == m_glyphsData.end()) {
71 GlyphData &gd = emptyData(glyph);
72 gd.path = m_referenceFont.pathForGlyph(glyph);
73 // need bounding rect in base font size scale
74 qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
75 QTransform scaleDown;
76 scaleDown.scale(scaleFactor, scaleFactor);
77 gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
78 return gd;
79 }
80 return data.value();
81}
82
83QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize)
84{
85 GlyphData &gd = glyphData(glyph);
86 qreal scale = fontScale(pixelSize);
87
88 Metrics m;
89 m.width = gd.boundingRect.width() * scale;
90 m.height = gd.boundingRect.height() * scale;
91 m.baselineX = gd.boundingRect.x() * scale;
92 m.baselineY = -gd.boundingRect.y() * scale;
93
94 return m;
95}
96
97void QSGDistanceFieldGlyphCache::populate(const QList<glyph_t> &glyphs)
98{
99 QSet<glyph_t> referencedGlyphs;
100 QSet<glyph_t> newGlyphs;
101 int count = glyphs.size();
102 for (int i = 0; i < count; ++i) {
103 glyph_t glyphIndex = glyphs.at(i);
104 const bool isValid = int(glyphIndex) < glyphCount() || glyphCount() <= 0;
105
106 if (!isValid) {
107 qWarning("Warning: distance-field glyph is not available with index %d (glyph count: %d, font: %s)",
108 glyphIndex,
109 glyphCount(),
110 qPrintable(m_referenceFont.familyName()));
111 }
112
113 GlyphData &gd = isValid ? glyphData(glyphIndex) : emptyData(glyphIndex);
114 ++gd.ref;
115 referencedGlyphs.insert(glyphIndex);
116
117 if (gd.texCoord.isValid() || m_populatingGlyphs.contains(glyphIndex))
118 continue;
119
120 m_populatingGlyphs.insert(glyphIndex);
121
122 if (gd.boundingRect.isEmpty()) {
123 gd.texCoord.width = 0;
124 gd.texCoord.height = 0;
125 } else {
126 newGlyphs.insert(glyphIndex);
127 }
128 }
129
130 referenceGlyphs(referencedGlyphs);
131 if (!newGlyphs.isEmpty())
132 requestGlyphs(newGlyphs);
133}
134
135void QSGDistanceFieldGlyphCache::release(const QList<glyph_t> &glyphs)
136{
137 QSet<glyph_t> unusedGlyphs;
138 for (glyph_t glyphIndex : glyphs) {
139 if (auto it = m_glyphsData.find(glyphIndex); it != m_glyphsData.end()) {
140 GlyphData &gd = it.value();
141 if (--gd.ref == 0)
142 unusedGlyphs.insert(glyphIndex);
143 }
144 }
145 releaseGlyphs(unusedGlyphs);
146}
147
148bool QSGDistanceFieldGlyphCache::isActive() const
149{
150 return true;
151}
152
153void QSGDistanceFieldGlyphCache::update()
154{
155 m_populatingGlyphs.clear();
156
157 if (m_pendingGlyphs.isEmpty())
158 return;
159
160 Q_TRACE_SCOPE(QSGDistanceFieldGlyphCache_update, m_pendingGlyphs.size());
161
162 bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled();
163 if (profileFrames)
164 qsg_render_timer.start();
165 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame);
166 Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_entry);
167
168 QList<QDistanceField> distanceFields;
169 const int pendingGlyphsSize = m_pendingGlyphs.size();
170 distanceFields.reserve(pendingGlyphsSize);
171 for (int i = 0; i < pendingGlyphsSize; ++i) {
172 GlyphData &gd = glyphData(m_pendingGlyphs.at(i));
173
174 QSize size = QSize(qCeil(gd.texCoord.width + gd.texCoord.xMargin * 2),
175 qCeil(gd.texCoord.height + gd.texCoord.yMargin * 2));
176
177 distanceFields.append(QDistanceField(size,
178 gd.path,
179 m_pendingGlyphs.at(i),
180 m_doubleGlyphResolution));
181 gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path
182 }
183
184 qint64 renderTime = 0;
185 int count = m_pendingGlyphs.size();
186 if (profileFrames)
187 renderTime = qsg_render_timer.nsecsElapsed();
188
189 Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_exit);
190 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
191 QQuickProfiler::SceneGraphAdaptationLayerGlyphRender);
192 Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_entry);
193
194 m_pendingGlyphs.reset();
195
196 storeGlyphs(distanceFields);
197
198#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
199 for (Texture texture : std::as_const(m_textures))
200 saveTexture(texture.texture, m_referenceFont.familyName());
201#endif
202
203 if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
204 quint64 now = qsg_render_timer.elapsed();
205 qCDebug(QSG_LOG_TIME_GLYPH,
206 "distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
207 count,
208 (int) now,
209 int(renderTime / 1000000),
210 int((now - (renderTime / 1000000))));
211 }
212 Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_exit);
213 Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
214 QQuickProfiler::SceneGraphAdaptationLayerGlyphStore,
215 (qint64)count);
216}
217
218void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
219{
220 QList<quint32> invalidatedGlyphs;
221
222 int count = glyphs.size();
223 for (int i = 0; i < count; ++i) {
224 GlyphPosition glyph = glyphs.at(i);
225 GlyphData &gd = glyphData(glyph.glyph);
226
227 if (!gd.texCoord.isNull())
228 invalidatedGlyphs.append(glyph.glyph);
229
230 gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
231 gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
232 gd.texCoord.x = glyph.position.x();
233 gd.texCoord.y = glyph.position.y();
234 gd.texCoord.width = gd.boundingRect.width();
235 gd.texCoord.height = gd.boundingRect.height();
236 }
237
238 if (!invalidatedGlyphs.isEmpty()) {
239 for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
240 iter->invalidateGlyphs(invalidatedGlyphs);
241 }
242 }
243}
244
245void QSGDistanceFieldGlyphCache::processPendingGlyphs()
246{
247 /* Intentionally empty */
248}
249
250void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QList<glyph_t> &glyphs, const Texture &tex)
251{
252 int i = m_textures.indexOf(tex);
253 if (i == -1) {
254 m_textures.append(tex);
255 i = m_textures.size() - 1;
256 } else {
257 m_textures[i].size = tex.size;
258 }
259 Texture *texture = &(m_textures[i]);
260
261 QList<quint32> invalidatedGlyphs;
262
263 int count = glyphs.size();
264 for (int j = 0; j < count; ++j) {
265 glyph_t glyphIndex = glyphs.at(j);
266 GlyphData &gd = glyphData(glyphIndex);
267 if (gd.texture != &s_emptyTexture)
268 invalidatedGlyphs.append(glyphIndex);
269 gd.texture = texture;
270 }
271
272 if (!invalidatedGlyphs.isEmpty()) {
273 for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
274 iter->invalidateGlyphs(invalidatedGlyphs);
275 }
276 }
277}
278
279void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QList<glyph_t> &glyphs)
280{
281 int count = glyphs.size();
282 for (int i = 0; i < count; ++i)
283 m_pendingGlyphs.add(glyphs.at(i));
284}
285
286void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
287{
288 int count = m_textures.size();
289 for (int i = 0; i < count; ++i) {
290 Texture &tex = m_textures[i];
291 if (tex.texture == oldTex) {
292 tex.texture = newTex;
293 tex.size = newTexSize;
294 return;
295 }
296 }
297}
298
299QSGNodeVisitorEx::~QSGNodeVisitorEx()
300 = default;
301
302void QSGNodeVisitorEx::visitChildren(QSGNode *node)
303{
304 for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
305 switch (child->type()) {
306 case QSGNode::ClipNodeType: {
307 QSGClipNode *c = static_cast<QSGClipNode*>(child);
308 if (visit(c))
309 visitChildren(c);
310 endVisit(c);
311 break;
312 }
313 case QSGNode::TransformNodeType: {
314 QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
315 if (visit(c))
316 visitChildren(c);
317 endVisit(c);
318 break;
319 }
320 case QSGNode::OpacityNodeType: {
321 QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
322 if (visit(c))
323 visitChildren(c);
324 endVisit(c);
325 break;
326 }
327 case QSGNode::GeometryNodeType: {
328 if (child->flags() & QSGNode::IsVisitableNode) {
329 QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
330 v->accept(this);
331 } else {
332 QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
333 if (visit(c))
334 visitChildren(c);
335 endVisit(c);
336 }
337 break;
338 }
339 case QSGNode::RootNodeType: {
340 QSGRootNode *root = static_cast<QSGRootNode*>(child);
341 if (visit(root))
342 visitChildren(root);
343 endVisit(root);
344 break;
345 }
346 case QSGNode::BasicNodeType: {
347 visitChildren(child);
348 break;
349 }
350 case QSGNode::RenderNodeType: {
351 QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
352 if (visit(r))
353 visitChildren(r);
354 endVisit(r);
355 break;
356 }
357 default:
358 Q_UNREACHABLE();
359 break;
360 }
361 }
362}
363
364QSGVisitableNode::~QSGVisitableNode()
365 = default;
366
367QSGInternalRectangleNode::~QSGInternalRectangleNode()
368 = default;
369
370QSGInternalImageNode::~QSGInternalImageNode()
371 = default;
372
373QSGPainterNode::~QSGPainterNode()
374 = default;
375
376#ifndef QT_NO_DEBUG_STREAM
377QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
378{
379 QDebugStateSaver saver(debug);
380 debug.space();
381 debug << v.name;
382 switch (v.type) {
383 case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
384 debug << "cvar" << "offset" << v.offset << "size" << v.size;
385 break;
386 case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
387 debug << "sampler" << "bindpoint" << v.bindPoint;
388 break;
389 case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
390 debug << "texture" << "bindpoint" << v.bindPoint;
391 break;
392 default:
393 break;
394 }
395 return debug;
396}
397
398QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
399{
400 QDebugStateSaver saver(debug);
401 debug.space();
402 debug << vd.specialType;
403 return debug;
404}
405#endif
406
407/*!
408 \internal
409 */
410QSGLayer::QSGLayer(QSGTexturePrivate &dd)
411 : QSGDynamicTexture(dd)
412{
413}
414
415QSGLayer::~QSGLayer()
416 = default;
417
418#if QT_CONFIG(quick_sprite)
419
420QSGSpriteNode::~QSGSpriteNode()
421 = default;
422
423#endif
424
425QSGGuiThreadShaderEffectManager::~QSGGuiThreadShaderEffectManager()
426 = default;
427
428QSGShaderEffectNode::~QSGShaderEffectNode()
429 = default;
430
431QSGGlyphNode::~QSGGlyphNode()
432 = default;
433
434QSGDistanceFieldGlyphConsumer::~QSGDistanceFieldGlyphConsumer()
435 = default;
436
437QT_END_NAMESPACE
438
439#include "moc_qsgadaptationlayer_p.cpp"
QDebug operator<<(QDebug dbg, const QFileInfo &fi)