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 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | static QElapsedTimer qsg_render_timer; |
58 | |
59 | QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; |
60 | |
61 | QSGDistanceFieldGlyphCache::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 | |
78 | QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() |
79 | { |
80 | } |
81 | |
82 | QSGDistanceFieldGlyphCache::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 | |
90 | QSGDistanceFieldGlyphCache::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 | |
106 | QSGDistanceFieldGlyphCache::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 | |
120 | void 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 | |
154 | void 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 | |
167 | void 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 | |
227 | void 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 | |
254 | void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement) |
255 | { |
256 | Q_UNUSED(ownerElement); |
257 | } |
258 | |
259 | void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement) |
260 | { |
261 | Q_UNUSED(ownerElement); |
262 | } |
263 | |
264 | void QSGDistanceFieldGlyphCache::processPendingGlyphs() |
265 | { |
266 | /* Intentionally empty */ |
267 | } |
268 | |
269 | void 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 | |
298 | void 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 | |
305 | void 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 | |
318 | void 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 | |
334 | void 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 | |
488 | void 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 |
551 | QDebug 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 | |
572 | QDebug 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 | */ |
584 | QSGLayer::QSGLayer(QSGTexturePrivate &dd) |
585 | : QSGDynamicTexture(dd) |
586 | { |
587 | } |
588 | |
589 | QT_END_NAMESPACE |
590 | |
591 | #include "moc_qsgadaptationlayer_p.cpp" |
592 | |