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

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