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