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
4#include "qsgadaptationlayer_p.h"
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(f: 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(narrowOutlineFont: 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(narrowOutlineFont: 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(key: glyph, value: 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(key: glyph);
70 if (data == m_glyphsData.end()) {
71 GlyphData &gd = emptyData(glyph);
72 gd.path = m_referenceFont.pathForGlyph(glyphIndex: glyph);
73 // need bounding rect in base font size scale
74 qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(narrowOutlineFont: m_doubleGlyphResolution);
75 QTransform scaleDown;
76 scaleDown.scale(sx: scaleFactor, sy: 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 QVector<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 if ((int) glyphIndex >= glyphCount() && glyphCount() > 0) {
105 qWarning(msg: "Warning: distance-field glyph is not available with index %d", glyphIndex);
106 continue;
107 }
108
109 GlyphData &gd = glyphData(glyph: glyphIndex);
110 ++gd.ref;
111 referencedGlyphs.insert(value: glyphIndex);
112
113 if (gd.texCoord.isValid() || m_populatingGlyphs.contains(value: glyphIndex))
114 continue;
115
116 m_populatingGlyphs.insert(value: glyphIndex);
117
118 if (gd.boundingRect.isEmpty()) {
119 gd.texCoord.width = 0;
120 gd.texCoord.height = 0;
121 } else {
122 newGlyphs.insert(value: glyphIndex);
123 }
124 }
125
126 referenceGlyphs(glyphs: referencedGlyphs);
127 if (!newGlyphs.isEmpty())
128 requestGlyphs(glyphs: newGlyphs);
129}
130
131void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
132{
133 QSet<glyph_t> unusedGlyphs;
134 int count = glyphs.size();
135 for (int i = 0; i < count; ++i) {
136 glyph_t glyphIndex = glyphs.at(i);
137 GlyphData &gd = glyphData(glyph: glyphIndex);
138 if (--gd.ref == 0 && !gd.texCoord.isNull())
139 unusedGlyphs.insert(value: glyphIndex);
140 }
141 releaseGlyphs(glyphs: unusedGlyphs);
142}
143
144bool QSGDistanceFieldGlyphCache::isActive() const
145{
146 return true;
147}
148
149void QSGDistanceFieldGlyphCache::update()
150{
151 m_populatingGlyphs.clear();
152
153 if (m_pendingGlyphs.isEmpty())
154 return;
155
156 Q_TRACE_SCOPE(QSGDistanceFieldGlyphCache_update, m_pendingGlyphs.size());
157
158 bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled();
159 if (profileFrames)
160 qsg_render_timer.start();
161 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame);
162 Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_entry);
163
164 QList<QDistanceField> distanceFields;
165 const int pendingGlyphsSize = m_pendingGlyphs.size();
166 distanceFields.reserve(size: pendingGlyphsSize);
167 for (int i = 0; i < pendingGlyphsSize; ++i) {
168 GlyphData &gd = glyphData(glyph: m_pendingGlyphs.at(i));
169 distanceFields.append(t: QDistanceField(gd.path,
170 m_pendingGlyphs.at(i),
171 m_doubleGlyphResolution));
172 gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path
173 }
174
175 qint64 renderTime = 0;
176 int count = m_pendingGlyphs.size();
177 if (profileFrames)
178 renderTime = qsg_render_timer.nsecsElapsed();
179
180 Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_exit);
181 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
182 QQuickProfiler::SceneGraphAdaptationLayerGlyphRender);
183 Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_entry);
184
185 m_pendingGlyphs.reset();
186
187 storeGlyphs(glyphs: distanceFields);
188
189#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
190 for (Texture texture : std::as_const(m_textures))
191 saveTexture(texture.texture, m_referenceFont.familyName());
192#endif
193
194 if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
195 quint64 now = qsg_render_timer.elapsed();
196 qCDebug(QSG_LOG_TIME_GLYPH,
197 "distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
198 count,
199 (int) now,
200 int(renderTime / 1000000),
201 int((now - (renderTime / 1000000))));
202 }
203 Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_exit);
204 Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
205 QQuickProfiler::SceneGraphAdaptationLayerGlyphStore,
206 (qint64)count);
207}
208
209void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
210{
211 QVector<quint32> invalidatedGlyphs;
212
213 int count = glyphs.size();
214 for (int i = 0; i < count; ++i) {
215 GlyphPosition glyph = glyphs.at(i);
216 GlyphData &gd = glyphData(glyph: glyph.glyph);
217
218 if (!gd.texCoord.isNull())
219 invalidatedGlyphs.append(t: glyph.glyph);
220
221 gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(narrowOutlineFont: m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(narrowOutlineFont: m_doubleGlyphResolution));
222 gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(narrowOutlineFont: m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(narrowOutlineFont: m_doubleGlyphResolution));
223 gd.texCoord.x = glyph.position.x();
224 gd.texCoord.y = glyph.position.y();
225 gd.texCoord.width = gd.boundingRect.width();
226 gd.texCoord.height = gd.boundingRect.height();
227 }
228
229 if (!invalidatedGlyphs.isEmpty()) {
230 for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
231 iter->invalidateGlyphs(glyphs: invalidatedGlyphs);
232 }
233 }
234}
235
236void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
237{
238 Q_UNUSED(ownerElement);
239}
240
241void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
242{
243 Q_UNUSED(ownerElement);
244}
245
246void QSGDistanceFieldGlyphCache::processPendingGlyphs()
247{
248 /* Intentionally empty */
249}
250
251void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
252{
253 int i = m_textures.indexOf(t: tex);
254 if (i == -1) {
255 m_textures.append(t: tex);
256 i = m_textures.size() - 1;
257 } else {
258 m_textures[i].size = tex.size;
259 }
260 Texture *texture = &(m_textures[i]);
261
262 QVector<quint32> invalidatedGlyphs;
263
264 int count = glyphs.size();
265 for (int j = 0; j < count; ++j) {
266 glyph_t glyphIndex = glyphs.at(i: j);
267 GlyphData &gd = glyphData(glyph: glyphIndex);
268 if (gd.texture != &s_emptyTexture)
269 invalidatedGlyphs.append(t: glyphIndex);
270 gd.texture = texture;
271 }
272
273 if (!invalidatedGlyphs.isEmpty()) {
274 for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
275 iter->invalidateGlyphs(glyphs: invalidatedGlyphs);
276 }
277 }
278}
279
280void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
281{
282 int count = glyphs.size();
283 for (int i = 0; i < count; ++i)
284 m_pendingGlyphs.add(t: glyphs.at(i));
285}
286
287void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
288{
289 int count = m_textures.size();
290 for (int i = 0; i < count; ++i) {
291 Texture &tex = m_textures[i];
292 if (tex.texture == oldTex) {
293 tex.texture = newTex;
294 tex.size = newTexSize;
295 return;
296 }
297 }
298}
299
300QSGNodeVisitorEx::~QSGNodeVisitorEx()
301 = default;
302
303void QSGNodeVisitorEx::visitChildren(QSGNode *node)
304{
305 for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
306 switch (child->type()) {
307 case QSGNode::ClipNodeType: {
308 QSGClipNode *c = static_cast<QSGClipNode*>(child);
309 if (visit(c))
310 visitChildren(node: c);
311 endVisit(c);
312 break;
313 }
314 case QSGNode::TransformNodeType: {
315 QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
316 if (visit(c))
317 visitChildren(node: c);
318 endVisit(c);
319 break;
320 }
321 case QSGNode::OpacityNodeType: {
322 QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
323 if (visit(c))
324 visitChildren(node: c);
325 endVisit(c);
326 break;
327 }
328 case QSGNode::GeometryNodeType: {
329 if (child->flags() & QSGNode::IsVisitableNode) {
330 QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
331 v->accept(this);
332 } else {
333 QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
334 if (visit(c))
335 visitChildren(node: c);
336 endVisit(c);
337 }
338 break;
339 }
340 case QSGNode::RootNodeType: {
341 QSGRootNode *root = static_cast<QSGRootNode*>(child);
342 if (visit(root))
343 visitChildren(node: root);
344 endVisit(root);
345 break;
346 }
347 case QSGNode::BasicNodeType: {
348 visitChildren(node: child);
349 break;
350 }
351 case QSGNode::RenderNodeType: {
352 QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
353 if (visit(r))
354 visitChildren(node: r);
355 endVisit(r);
356 break;
357 }
358 default:
359 Q_UNREACHABLE();
360 break;
361 }
362 }
363}
364
365QSGVisitableNode::~QSGVisitableNode()
366 = default;
367
368QSGInternalRectangleNode::~QSGInternalRectangleNode()
369 = default;
370
371QSGInternalImageNode::~QSGInternalImageNode()
372 = default;
373
374QSGPainterNode::~QSGPainterNode()
375 = default;
376
377#ifndef QT_NO_DEBUG_STREAM
378QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
379{
380 QDebugStateSaver saver(debug);
381 debug.space();
382 debug << v.name;
383 switch (v.type) {
384 case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
385 debug << "cvar" << "offset" << v.offset << "size" << v.size;
386 break;
387 case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
388 debug << "sampler" << "bindpoint" << v.bindPoint;
389 break;
390 case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
391 debug << "texture" << "bindpoint" << v.bindPoint;
392 break;
393 default:
394 break;
395 }
396 return debug;
397}
398
399QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
400{
401 QDebugStateSaver saver(debug);
402 debug.space();
403 debug << vd.specialType;
404 return debug;
405}
406#endif
407
408/*!
409 \internal
410 */
411QSGLayer::QSGLayer(QSGTexturePrivate &dd)
412 : QSGDynamicTexture(dd)
413{
414}
415
416QSGLayer::~QSGLayer()
417 = default;
418
419#if QT_CONFIG(quick_sprite)
420
421QSGSpriteNode::~QSGSpriteNode()
422 = default;
423
424#endif
425
426QSGGuiThreadShaderEffectManager::~QSGGuiThreadShaderEffectManager()
427 = default;
428
429QSGShaderEffectNode::~QSGShaderEffectNode()
430 = default;
431
432QSGGlyphNode::~QSGGlyphNode()
433 = default;
434
435QSGDistanceFieldGlyphConsumer::~QSGDistanceFieldGlyphConsumer()
436 = default;
437
438QT_END_NAMESPACE
439
440#include "moc_qsgadaptationlayer_p.cpp"
441

source code of qtdeclarative/src/quick/scenegraph/qsgadaptationlayer.cpp