1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsgadaptationlayer_p.h"
41
42#include <qmath.h>
43#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
44#include <QtQuick/private/qsgcontext_p.h>
45#include <private/qrawfont_p.h>
46#include <QtGui/qguiapplication.h>
47#include <qdir.h>
48#include <qsgrendernode.h>
49
50#include <private/qquickprofiler_p.h>
51#include <QElapsedTimer>
52
53QT_BEGIN_NAMESPACE
54
55static QElapsedTimer qsg_render_timer;
56
57QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
58
59QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font)
60 : m_pendingGlyphs(64)
61{
62 Q_ASSERT(font.isValid());
63
64 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
65 m_glyphCount = fontD->fontEngine->glyphCount();
66
67 m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
68
69 m_referenceFont = font;
70 // we set the same pixel size as used by the distance field internally.
71 // this allows us to call pathForGlyph once and reuse the result.
72 m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution) * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
73 Q_ASSERT(m_referenceFont.isValid());
74}
75
76QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
77{
78}
79
80QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
81{
82 GlyphData gd;
83 gd.texture = &s_emptyTexture;
84 QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
85 return it.value();
86}
87
88QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
89{
90 QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
91 if (data == m_glyphsData.end()) {
92 GlyphData &gd = emptyData(glyph);
93 gd.path = m_referenceFont.pathForGlyph(glyph);
94 // need bounding rect in base font size scale
95 qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
96 QTransform scaleDown;
97 scaleDown.scale(scaleFactor, scaleFactor);
98 gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
99 return gd;
100 }
101 return data.value();
102}
103
104QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize)
105{
106 GlyphData &gd = glyphData(glyph);
107 qreal scale = fontScale(pixelSize);
108
109 Metrics m;
110 m.width = gd.boundingRect.width() * scale;
111 m.height = gd.boundingRect.height() * scale;
112 m.baselineX = gd.boundingRect.x() * scale;
113 m.baselineY = -gd.boundingRect.y() * scale;
114
115 return m;
116}
117
118void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
119{
120 QSet<glyph_t> referencedGlyphs;
121 QSet<glyph_t> newGlyphs;
122 int count = glyphs.count();
123 for (int i = 0; i < count; ++i) {
124 glyph_t glyphIndex = glyphs.at(i);
125 if ((int) glyphIndex >= glyphCount() && glyphCount() > 0) {
126 qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
127 continue;
128 }
129
130 GlyphData &gd = glyphData(glyphIndex);
131 ++gd.ref;
132 referencedGlyphs.insert(glyphIndex);
133
134 if (gd.texCoord.isValid() || m_populatingGlyphs.contains(glyphIndex))
135 continue;
136
137 m_populatingGlyphs.insert(glyphIndex);
138
139 if (gd.boundingRect.isEmpty()) {
140 gd.texCoord.width = 0;
141 gd.texCoord.height = 0;
142 } else {
143 newGlyphs.insert(glyphIndex);
144 }
145 }
146
147 referenceGlyphs(referencedGlyphs);
148 if (!newGlyphs.isEmpty())
149 requestGlyphs(newGlyphs);
150}
151
152void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
153{
154 QSet<glyph_t> unusedGlyphs;
155 int count = glyphs.count();
156 for (int i = 0; i < count; ++i) {
157 glyph_t glyphIndex = glyphs.at(i);
158 GlyphData &gd = glyphData(glyphIndex);
159 if (--gd.ref == 0 && !gd.texCoord.isNull())
160 unusedGlyphs.insert(glyphIndex);
161 }
162 releaseGlyphs(unusedGlyphs);
163}
164
165void QSGDistanceFieldGlyphCache::update()
166{
167 m_populatingGlyphs.clear();
168
169 if (m_pendingGlyphs.isEmpty())
170 return;
171
172 bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled();
173 if (profileFrames)
174 qsg_render_timer.start();
175 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame);
176
177 QList<QDistanceField> distanceFields;
178 const int pendingGlyphsSize = m_pendingGlyphs.size();
179 distanceFields.reserve(pendingGlyphsSize);
180 for (int i = 0; i < pendingGlyphsSize; ++i) {
181 GlyphData &gd = glyphData(m_pendingGlyphs.at(i));
182 distanceFields.append(QDistanceField(gd.path,
183 m_pendingGlyphs.at(i),
184 m_doubleGlyphResolution));
185 gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path
186 }
187
188 qint64 renderTime = 0;
189 int count = m_pendingGlyphs.size();
190 if (profileFrames)
191 renderTime = qsg_render_timer.nsecsElapsed();
192 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
193 QQuickProfiler::SceneGraphAdaptationLayerGlyphRender);
194
195 m_pendingGlyphs.reset();
196
197 storeGlyphs(distanceFields);
198
199#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
200 for (Texture texture : qAsConst(m_textures))
201 saveTexture(texture.textureId, texture.size.width(), texture.size.height());
202#endif
203
204 if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
205 quint64 now = qsg_render_timer.elapsed();
206 qCDebug(QSG_LOG_TIME_GLYPH,
207 "distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
208 count,
209 (int) now,
210 int(renderTime / 1000000),
211 int((now - (renderTime / 1000000))));
212 }
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 QVector<quint32> invalidatedGlyphs;
221
222 int count = glyphs.count();
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::registerOwnerElement(QQuickItem *ownerElement)
246{
247 Q_UNUSED(ownerElement);
248}
249
250void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
251{
252 Q_UNUSED(ownerElement);
253}
254
255void QSGDistanceFieldGlyphCache::processPendingGlyphs()
256{
257 /* Intentionally empty */
258}
259
260void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
261{
262 int i = m_textures.indexOf(tex);
263 if (i == -1) {
264 m_textures.append(tex);
265 i = m_textures.size() - 1;
266 } else {
267 m_textures[i].size = tex.size;
268 }
269 Texture *texture = &(m_textures[i]);
270
271 QVector<quint32> invalidatedGlyphs;
272
273 int count = glyphs.count();
274 for (int j = 0; j < count; ++j) {
275 glyph_t glyphIndex = glyphs.at(j);
276 GlyphData &gd = glyphData(glyphIndex);
277 if (gd.texture != &s_emptyTexture)
278 invalidatedGlyphs.append(glyphIndex);
279 gd.texture = texture;
280 }
281
282 if (!invalidatedGlyphs.isEmpty()) {
283 for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
284 iter->invalidateGlyphs(invalidatedGlyphs);
285 }
286 }
287}
288
289void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
290{
291 int count = glyphs.count();
292 for (int i = 0; i < count; ++i)
293 m_pendingGlyphs.add(glyphs.at(i));
294}
295
296void QSGDistanceFieldGlyphCache::updateTexture(uint oldTex, uint newTex, const QSize &newTexSize)
297{
298 int count = m_textures.count();
299 for (int i = 0; i < count; ++i) {
300 Texture &tex = m_textures[i];
301 if (tex.textureId == oldTex) {
302 tex.textureId = newTex;
303 tex.size = newTexSize;
304 return;
305 }
306 }
307}
308
309void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
310{
311 int count = m_textures.count();
312 for (int i = 0; i < count; ++i) {
313 Texture &tex = m_textures[i];
314 if (tex.texture == oldTex) {
315 tex.texture = newTex;
316 tex.size = newTexSize;
317 return;
318 }
319 }
320}
321
322#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
323#include <QtGui/qopenglfunctions.h>
324
325void QSGDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height) const
326{
327 QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
328
329 GLuint fboId;
330 functions->glGenFramebuffers(1, &fboId);
331
332 GLuint tmpTexture = 0;
333 functions->glGenTextures(1, &tmpTexture);
334 functions->glBindTexture(GL_TEXTURE_2D, tmpTexture);
335 functions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
336 functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
337 functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
338 functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
339 functions->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
340 functions->glBindTexture(GL_TEXTURE_2D, 0);
341
342 functions->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
343 functions->glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
344 tmpTexture, 0);
345
346 functions->glActiveTexture(GL_TEXTURE0);
347 functions->glBindTexture(GL_TEXTURE_2D, textureId);
348
349 functions->glDisable(GL_STENCIL_TEST);
350 functions->glDisable(GL_DEPTH_TEST);
351 functions->glDisable(GL_SCISSOR_TEST);
352 functions->glDisable(GL_BLEND);
353
354 GLfloat textureCoordinateArray[8];
355 textureCoordinateArray[0] = 0.0f;
356 textureCoordinateArray[1] = 0.0f;
357 textureCoordinateArray[2] = 1.0f;
358 textureCoordinateArray[3] = 0.0f;
359 textureCoordinateArray[4] = 1.0f;
360 textureCoordinateArray[5] = 1.0f;
361 textureCoordinateArray[6] = 0.0f;
362 textureCoordinateArray[7] = 1.0f;
363
364 GLfloat vertexCoordinateArray[8];
365 vertexCoordinateArray[0] = -1.0f;
366 vertexCoordinateArray[1] = -1.0f;
367 vertexCoordinateArray[2] = 1.0f;
368 vertexCoordinateArray[3] = -1.0f;
369 vertexCoordinateArray[4] = 1.0f;
370 vertexCoordinateArray[5] = 1.0f;
371 vertexCoordinateArray[6] = -1.0f;
372 vertexCoordinateArray[7] = 1.0f;
373
374 functions->glViewport(0, 0, width, height);
375 functions->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
376 functions->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
377
378 {
379 static const char *vertexShaderSource =
380 "attribute vec4 vertexCoordsArray; \n"
381 "attribute vec2 textureCoordArray; \n"
382 "varying vec2 textureCoords; \n"
383 "void main(void) \n"
384 "{ \n"
385 " gl_Position = vertexCoordsArray; \n"
386 " textureCoords = textureCoordArray; \n"
387 "} \n";
388
389 static const char *fragmentShaderSource =
390 "varying vec2 textureCoords; \n"
391 "uniform sampler2D texture; \n"
392 "void main() \n"
393 "{ \n"
394 " gl_FragColor = texture2D(texture, textureCoords); \n"
395 "} \n";
396
397 GLuint vertexShader = functions->glCreateShader(GL_VERTEX_SHADER);
398 GLuint fragmentShader = functions->glCreateShader(GL_FRAGMENT_SHADER);
399
400 if (vertexShader == 0 || fragmentShader == 0) {
401 GLenum error = functions->glGetError();
402 qWarning("QSGDistanceFieldGlyphCache::saveTexture: Failed to create shaders. (GL error: %x)",
403 error);
404 return;
405 }
406
407 functions->glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
408 functions->glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
409 functions->glCompileShader(vertexShader);
410
411 GLint len = 1;
412 functions->glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
413
414 char infoLog[2048];
415 functions->glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
416 if (qstrlen(infoLog) > 0)
417 qWarning("Problems compiling vertex shader:\n %s", infoLog);
418
419 functions->glCompileShader(fragmentShader);
420 functions->glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
421 if (qstrlen(infoLog) > 0)
422 qWarning("Problems compiling fragment shader:\n %s", infoLog);
423
424 GLuint shaderProgram = functions->glCreateProgram();
425 functions->glAttachShader(shaderProgram, vertexShader);
426 functions->glAttachShader(shaderProgram, fragmentShader);
427
428 functions->glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
429 functions->glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
430
431 functions->glLinkProgram(shaderProgram);
432 functions->glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
433 if (qstrlen(infoLog) > 0)
434 qWarning("Problems linking shaders:\n %s", infoLog);
435
436 functions->glUseProgram(shaderProgram);
437 functions->glEnableVertexAttribArray(0);
438 functions->glEnableVertexAttribArray(1);
439
440 int textureUniformLocation = functions->glGetUniformLocation(shaderProgram, "texture");
441 functions->glUniform1i(textureUniformLocation, 0);
442 }
443
444 functions->glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
445
446 {
447 GLenum error = functions->glGetError();
448 if (error != GL_NO_ERROR)
449 qWarning("glDrawArrays reported error 0x%x", error);
450 }
451
452 uchar *data = new uchar[width * height * 4];
453
454 functions->glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
455
456 QImage image(data, width, height, QImage::Format_ARGB32);
457
458 QByteArray fileName = m_referenceFont.familyName().toLatin1() + '_' + QByteArray::number(textureId);
459 fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
460
461 image.save(QString::fromLocal8Bit(fileName));
462
463 {
464 GLenum error = functions->glGetError();
465 if (error != GL_NO_ERROR)
466 qWarning("glReadPixels reported error 0x%x", error);
467 }
468
469 functions->glDisableVertexAttribArray(0);
470 functions->glDisableVertexAttribArray(1);
471
472 functions->glDeleteFramebuffers(1, &fboId);
473 functions->glDeleteTextures(1, &tmpTexture);
474
475 delete[] data;
476}
477#endif
478
479void QSGNodeVisitorEx::visitChildren(QSGNode *node)
480{
481 for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
482 switch (child->type()) {
483 case QSGNode::ClipNodeType: {
484 QSGClipNode *c = static_cast<QSGClipNode*>(child);
485 if (visit(c))
486 visitChildren(c);
487 endVisit(c);
488 break;
489 }
490 case QSGNode::TransformNodeType: {
491 QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
492 if (visit(c))
493 visitChildren(c);
494 endVisit(c);
495 break;
496 }
497 case QSGNode::OpacityNodeType: {
498 QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
499 if (visit(c))
500 visitChildren(c);
501 endVisit(c);
502 break;
503 }
504 case QSGNode::GeometryNodeType: {
505 if (child->flags() & QSGNode::IsVisitableNode) {
506 QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
507 v->accept(this);
508 } else {
509 QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
510 if (visit(c))
511 visitChildren(c);
512 endVisit(c);
513 }
514 break;
515 }
516 case QSGNode::RootNodeType: {
517 QSGRootNode *root = static_cast<QSGRootNode*>(child);
518 if (visit(root))
519 visitChildren(root);
520 endVisit(root);
521 break;
522 }
523 case QSGNode::BasicNodeType: {
524 visitChildren(child);
525 break;
526 }
527 case QSGNode::RenderNodeType: {
528 QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
529 if (visit(r))
530 visitChildren(r);
531 endVisit(r);
532 break;
533 }
534 default:
535 Q_UNREACHABLE();
536 break;
537 }
538 }
539}
540
541#ifndef QT_NO_DEBUG_STREAM
542QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
543{
544 QDebugStateSaver saver(debug);
545 debug.space();
546 debug << v.name;
547 switch (v.type) {
548 case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
549 debug << "cvar" << "offset" << v.offset << "size" << v.size;
550 break;
551 case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
552 debug << "sampler" << "bindpoint" << v.bindPoint;
553 break;
554 case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
555 debug << "texture" << "bindpoint" << v.bindPoint;
556 break;
557 default:
558 break;
559 }
560 return debug;
561}
562
563QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
564{
565 QDebugStateSaver saver(debug);
566 debug.space();
567 debug << vd.specialType;
568 return debug;
569}
570#endif
571
572/*!
573 \internal
574 */
575QSGLayer::QSGLayer(QSGTexturePrivate &dd)
576 : QSGDynamicTexture(dd)
577{
578}
579
580QT_END_NAMESPACE
581
582#include "moc_qsgadaptationlayer_p.cpp"
583