1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtQuick module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsgopenglvisualizer_p.h"
43#include <qmath.h>
44#include <private/qsgshadersourcebuilder_p.h>
45
46QT_BEGIN_NAMESPACE
47
48namespace QSGBatchRenderer
49{
50
51#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
52#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
53#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
54 | QSGNode::DirtyOpacity \
55 | QSGNode::DirtyMatrix \
56 | QSGNode::DirtyNodeRemoved)
57
58QMatrix4x4 qsg_matrixForRoot(Node *node);
59
60class VisualizeShader : public QOpenGLShaderProgram
61{
62public:
63 int color;
64 int matrix;
65 int rotation;
66 int pattern;
67 int projection;
68};
69
70OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer)
71 : Visualizer(renderer),
72 m_funcs(QOpenGLContext::currentContext()->functions()),
73 m_visualizeProgram(nullptr)
74{
75}
76
77OpenGLVisualizer::~OpenGLVisualizer()
78{
79 releaseResources();
80}
81
82void OpenGLVisualizer::releaseResources()
83{
84 delete m_visualizeProgram;
85 m_visualizeProgram = nullptr;
86}
87
88void OpenGLVisualizer::prepareVisualize()
89{
90 // nothing to do here
91}
92
93void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g)
94{
95 if (g->attributeCount() < 1)
96 return;
97 const QSGGeometry::Attribute *a = g->attributes();
98 m_funcs->glVertexAttribPointer(indx: 0, size: a->tupleSize, type: a->type, normalized: false, stride: g->sizeOfVertex(), ptr: g->vertexData());
99 if (g->indexCount())
100 m_funcs->glDrawElements(mode: g->drawingMode(), count: g->indexCount(), type: g->indexType(), indices: g->indexData());
101 else
102 m_funcs->glDrawArrays(mode: g->drawingMode(), first: 0, count: g->vertexCount());
103
104}
105
106void OpenGLVisualizer::visualizeBatch(Batch *b)
107{
108 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
109
110 if (b->positionAttribute != 0)
111 return;
112
113 QSGGeometryNode *gn = b->first->node;
114 QSGGeometry *g = gn->geometry();
115 const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
116
117 m_funcs->glBindBuffer(GL_ARRAY_BUFFER, buffer: b->vbo.id);
118
119 QMatrix4x4 matrix(m_renderer->m_current_projection_matrix);
120 if (b->root)
121 matrix = matrix * qsg_matrixForRoot(node: b->root);
122
123 shader->setUniformValue(location: shader->pattern, value: float(b->merged ? 0 : 1));
124
125 QColor color = QColor::fromHsvF(h: (rand() & 1023) / 1023.0, s: 1.0, v: 1.0);
126 float cr = color.redF();
127 float cg = color.greenF();
128 float cb = color.blueF();
129 shader->setUniformValue(location: shader->color, x: cr, y: cg, z: cb, w: 1.0);
130
131 if (b->merged) {
132 shader->setUniformValue(location: shader->matrix, value: matrix);
133 const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
134 for (int ds=0; ds<b->drawSets.size(); ++ds) {
135 const DrawSet &set = b->drawSets.at(i: ds);
136 m_funcs->glVertexAttribPointer(indx: a.position, size: 2, type: a.type, normalized: false, stride: g->sizeOfVertex(),
137 ptr: (void *) (qintptr) (set.vertices));
138 m_funcs->glDrawElements(mode: g->drawingMode(), count: set.indexCount, GL_UNSIGNED_SHORT,
139 indices: (void *)(qintptr)(dataStart + set.indices));
140 }
141 } else {
142 Element *e = b->first;
143 int offset = 0;
144 while (e) {
145 gn = e->node;
146 g = gn->geometry();
147 shader->setUniformValue(location: shader->matrix, value: matrix * *gn->matrix());
148 m_funcs->glVertexAttribPointer(indx: a.position, size: a.tupleSize, type: a.type, normalized: false, stride: g->sizeOfVertex(),
149 ptr: (void *) (qintptr) offset);
150 if (g->indexCount())
151 m_funcs->glDrawElements(mode: g->drawingMode(), count: g->indexCount(), type: g->indexType(), indices: g->indexData());
152 else
153 m_funcs->glDrawArrays(mode: g->drawingMode(), first: 0, count: g->vertexCount());
154 offset += g->sizeOfVertex() * g->vertexCount();
155 e = e->nextInBatch;
156 }
157 }
158}
159
160void OpenGLVisualizer::visualizeClipping(QSGNode *node)
161{
162 if (node->type() == QSGNode::ClipNodeType) {
163 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
164 QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
165 QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
166 if (clipNode->matrix())
167 matrix = matrix * *clipNode->matrix();
168 shader->setUniformValue(location: shader->matrix, value: matrix);
169 visualizeDrawGeometry(g: clipNode->geometry());
170 }
171
172 QSGNODE_TRAVERSE(node) {
173 visualizeClipping(node: child);
174 }
175}
176
177void OpenGLVisualizer::visualizeChanges(Node *n)
178{
179
180 if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(akey: n)) {
181 uint dirty = m_visualizeChangeSet.value(akey: n);
182 bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
183
184 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
185 QColor color = QColor::fromHsvF(h: (rand() & 1023) / 1023.0, s: 0.3, v: 1.0);
186 float ca = 0.5;
187 float cr = color.redF() * ca;
188 float cg = color.greenF() * ca;
189 float cb = color.blueF() * ca;
190 shader->setUniformValue(location: shader->color, x: cr, y: cg, z: cb, w: ca);
191 shader->setUniformValue(location: shader->pattern, value: float(tinted ? 0.5 : 0));
192
193 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
194
195 QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
196 if (n->element()->batch->root)
197 matrix = matrix * qsg_matrixForRoot(node: n->element()->batch->root);
198 matrix = matrix * *gn->matrix();
199 shader->setUniformValue(location: shader->matrix, value: matrix);
200 visualizeDrawGeometry(g: gn->geometry());
201
202 // This is because many changes don't propegate their dirty state to the
203 // parent so the node updater will not unset these states. They are
204 // not used for anything so, unsetting it should have no side effects.
205 n->dirtyState = {};
206 }
207
208 SHADOWNODE_TRAVERSE(n) {
209 visualizeChanges(n: child);
210 }
211}
212
213void OpenGLVisualizer::visualizeOverdraw_helper(Node *node)
214{
215 if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
216 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
217 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
218
219 QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
220 matrix(2, 2) = m_renderer->m_zRange;
221 matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange;
222
223 if (node->element()->batch->root)
224 matrix = matrix * qsg_matrixForRoot(node: node->element()->batch->root);
225 matrix = matrix * *gn->matrix();
226 shader->setUniformValue(location: shader->matrix, value: matrix);
227
228 QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(r: 0.3, g: 1.0, b: 0.3) : QColor::fromRgbF(r: 1.0, g: 0.3, b: 0.3);
229 float ca = 0.33f;
230 shader->setUniformValue(location: shader->color, x: color.redF() * ca, y: color.greenF() * ca, z: color.blueF() * ca, w: ca);
231
232 visualizeDrawGeometry(g: gn->geometry());
233 }
234
235 SHADOWNODE_TRAVERSE(node) {
236 visualizeOverdraw_helper(node: child);
237 }
238}
239
240void OpenGLVisualizer::visualizeOverdraw()
241{
242 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
243 shader->setUniformValue(location: shader->color, x: 0.5f, y: 0.5f, z: 1.0f, w: 1.0f);
244 shader->setUniformValue(location: shader->projection, value: 1);
245
246 m_funcs->glBlendFunc(GL_ONE, GL_ONE);
247
248 static float step = 0;
249 step += static_cast<float>(M_PI * 2 / 1000.);
250 if (step > M_PI * 2)
251 step = 0;
252 float angle = 80.0 * std::sin(x: step);
253
254 QMatrix4x4 xrot; xrot.rotate(angle: 20, x: 1, y: 0, z: 0);
255 QMatrix4x4 zrot; zrot.rotate(angle, x: 0, y: 0, z: 1);
256 QMatrix4x4 tx; tx.translate(x: 0, y: 0, z: 1);
257
258 QMatrix4x4 m;
259
260// m.rotate(180, 0, 1, 0);
261
262 m.translate(x: 0, y: 0.5, z: 4);
263 m.scale(x: 2, y: 2, z: 1);
264
265 m.rotate(angle: -30, x: 1, y: 0, z: 0);
266 m.rotate(angle, x: 0, y: 1, z: 0);
267 m.translate(x: 0, y: 0, z: -1);
268
269 shader->setUniformValue(location: shader->rotation, value: m);
270
271 float box[] = {
272 // lower
273 -1, 1, 0, 1, 1, 0,
274 -1, 1, 0, -1, -1, 0,
275 1, 1, 0, 1, -1, 0,
276 -1, -1, 0, 1, -1, 0,
277
278 // upper
279 -1, 1, 1, 1, 1, 1,
280 -1, 1, 1, -1, -1, 1,
281 1, 1, 1, 1, -1, 1,
282 -1, -1, 1, 1, -1, 1,
283
284 // sides
285 -1, -1, 0, -1, -1, 1,
286 1, -1, 0, 1, -1, 1,
287 -1, 1, 0, -1, 1, 1,
288 1, 1, 0, 1, 1, 1
289 };
290 m_funcs->glVertexAttribPointer(indx: 0, size: 3, GL_FLOAT, normalized: false, stride: 0, ptr: box);
291 m_funcs->glLineWidth(width: 2);
292 m_funcs->glDrawArrays(GL_LINES, first: 0, count: 24);
293
294 visualizeOverdraw_helper(node: m_renderer->m_nodes.value(akey: m_renderer->rootNode()));
295}
296
297void OpenGLVisualizer::visualize()
298{
299 if (m_visualizeMode == VisualizeNothing)
300 return;
301
302 if (!m_visualizeProgram) {
303 VisualizeShader *prog = new VisualizeShader();
304 QSGShaderSourceBuilder::initializeProgramFromFiles(
305 program: prog,
306 QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
307 QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
308 prog->bindAttributeLocation(name: "v", location: 0);
309 prog->link();
310 prog->bind();
311 prog->color = prog->uniformLocation(name: "color");
312 prog->pattern = prog->uniformLocation(name: "pattern");
313 prog->projection = prog->uniformLocation(name: "projection");
314 prog->matrix = prog->uniformLocation(name: "matrix");
315 prog->rotation = prog->uniformLocation(name: "rotation");
316 m_visualizeProgram = prog;
317 } else {
318 m_visualizeProgram->bind();
319 }
320 VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
321
322 m_funcs->glDisable(GL_DEPTH_TEST);
323 m_funcs->glEnable(GL_BLEND);
324 m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
325 m_funcs->glEnableVertexAttribArray(index: 0);
326
327 // Blacken out the actual rendered content...
328 float bgOpacity = 0.8f;
329 if (m_visualizeMode == VisualizeBatches)
330 bgOpacity = 1.0;
331 float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
332 shader->setUniformValue(location: shader->color, x: 0.0f, y: 0.0f, z: 0.0f, w: bgOpacity);
333 shader->setUniformValue(location: shader->matrix, value: QMatrix4x4());
334 shader->setUniformValue(location: shader->rotation, value: QMatrix4x4());
335 shader->setUniformValue(location: shader->pattern, value: 0.0f);
336 shader->setUniformValue(location: shader->projection, value: false);
337 m_funcs->glVertexAttribPointer(indx: 0, size: 2, GL_FLOAT, normalized: false, stride: 0, ptr: v);
338 m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, first: 0, count: 4);
339
340 if (m_visualizeMode == VisualizeBatches) {
341 srand(seed: 0); // To force random colors to be roughly the same every time..
342 for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i)
343 visualizeBatch(b: m_renderer->m_opaqueBatches.at(i));
344 for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i)
345 visualizeBatch(b: m_renderer->m_alphaBatches.at(i));
346 } else if (m_visualizeMode == VisualizeClipping) {
347 shader->setUniformValue(location: shader->pattern, value: 0.5f);
348 shader->setUniformValue(location: shader->color, x: 0.2f, y: 0.0f, z: 0.0f, w: 0.2f);
349 visualizeClipping(node: m_renderer->rootNode());
350 } else if (m_visualizeMode == VisualizeChanges) {
351 visualizeChanges(n: m_renderer->m_nodes.value(akey: m_renderer->rootNode()));
352 m_visualizeChangeSet.clear();
353 } else if (m_visualizeMode == VisualizeOverdraw) {
354 visualizeOverdraw();
355 }
356
357 // Reset state back to defaults..
358 m_funcs->glDisable(GL_BLEND);
359 m_funcs->glDisableVertexAttribArray(index: 0);
360 shader->release();
361}
362
363}
364
365QT_END_NAMESPACE
366

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp