Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / coreapi / qsgrenderer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgrenderer_p.h"
43 #include "qsgnode.h"
44 #include "qsgmaterial.h"
45 #include "qsgnodeupdater_p.h"
46 #include "qsggeometry_p.h"
47
48 #include <private/qsgadaptationlayer_p.h>
49
50 #include <QOpenGLShaderProgram>
51 #include <qopenglframebufferobject.h>
52 #include <QtGui/qguiapplication.h>
53
54 #include <qdatetime.h>
55
56 QT_BEGIN_NAMESPACE
57
58 //#define RENDERER_DEBUG
59 //#define QT_GL_NO_SCISSOR_TEST
60
61
62
63 #define QSG_RENDERER_TIMING
64 #ifdef QSG_RENDERER_TIMING
65 static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty();
66 static QTime frameTimer;
67 static int preprocessTime;
68 static int updatePassTime;
69 #endif
70
71 void QSGBindable::clear(QSGRenderer::ClearMode mode) const
72 {
73     GLuint bits = 0;
74     if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
75     if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
76     if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
77     glClear(bits);
78 }
79
80 // Reactivate the color buffer after switching to the stencil.
81 void QSGBindable::reactivate() const
82 {
83     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
84 }
85
86 QSGBindableFbo::QSGBindableFbo(QOpenGLFramebufferObject *fbo) : m_fbo(fbo)
87 {
88 }
89
90
91 void QSGBindableFbo::bind() const
92 {
93     m_fbo->bind();
94 }
95
96 /*!
97     \class QSGRenderer
98     \brief The renderer class is the abstract baseclass use for rendering the
99     QML scene graph.
100
101     The renderer is not tied to any particular surface. It expects a context to
102     be current and will render into that surface according to how the device rect,
103     viewport rect and projection transformation are set up.
104
105     Rendering is a sequence of steps initiated by calling renderScene(). This will
106     effectively draw the scene graph starting at the root node. The QSGNode::preprocess()
107     function will be called for all the nodes in the graph, followed by an update
108     pass which updates all matrices, opacity, clip states and similar in the graph.
109     Because the update pass is called after preprocess, it is safe to modify the graph
110     during preprocess. To run a custom update pass over the graph, install a custom
111     QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated,
112     the virtual render() function is called.
113
114     The render() function is implemented by QSGRenderer subclasses to render the graph
115     in the most optimal way for a given hardware.
116
117     The renderer can make use of stencil, depth and color buffers in addition to the
118     scissor rect.
119
120     \internal
121  */
122
123
124 QSGRenderer::QSGRenderer(QSGContext *context)
125     : QObject()
126     , m_clear_color(Qt::transparent)
127     , m_clear_mode(ClearColorBuffer | ClearDepthBuffer)
128     , m_current_opacity(1)
129     , m_context(context)
130     , m_root_node(0)
131     , m_node_updater(0)
132     , m_bindable(0)
133     , m_changed_emitted(false)
134     , m_mirrored(false)
135     , m_is_rendering(false)
136     , m_vertex_buffer_bound(false)
137     , m_index_buffer_bound(false)
138 {
139     initializeGLFunctions();
140 }
141
142
143 QSGRenderer::~QSGRenderer()
144 {
145     setRootNode(0);
146     delete m_node_updater;
147 }
148
149 /*!
150     Returns the scene graph context for this renderer.
151
152     \internal
153  */
154
155 QSGContext *QSGRenderer::context()
156 {
157     return m_context;
158 }
159
160
161
162
163 /*!
164     Returns the node updater that this renderer uses to update states in the
165     scene graph.
166
167     If no updater is specified a default one is constructed.
168  */
169
170 QSGNodeUpdater *QSGRenderer::nodeUpdater() const
171 {
172     if (!m_node_updater)
173         const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater();
174     return m_node_updater;
175 }
176
177
178 /*!
179     Sets the node updater that this renderer uses to update states in the
180     scene graph.
181
182     This will delete and override any existing node updater
183   */
184 void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
185 {
186     if (m_node_updater)
187         delete m_node_updater;
188     m_node_updater = updater;
189 }
190
191
192 void QSGRenderer::setRootNode(QSGRootNode *node)
193 {
194     if (m_root_node == node)
195         return;
196     if (m_root_node) {
197         m_root_node->m_renderers.removeOne(this);
198         nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved);
199     }
200     m_root_node = node;
201     if (m_root_node) {
202         Q_ASSERT(!m_root_node->m_renderers.contains(this));
203         m_root_node->m_renderers << this;
204         nodeChanged(m_root_node, QSGNode::DirtyNodeAdded);
205     }
206 }
207
208
209 void QSGRenderer::renderScene()
210 {
211     class B : public QSGBindable
212     {
213     public:
214         void bind() const { QOpenGLFramebufferObject::bindDefault(); }
215     } b;
216     renderScene(b);
217 }
218
219 void QSGRenderer::renderScene(const QSGBindable &bindable)
220 {
221     if (!m_root_node)
222         return;
223
224     m_is_rendering = true;
225
226
227 #ifdef QSG_RENDERER_TIMING
228     if (qsg_render_timing)
229         frameTimer.start();
230     int bindTime;
231     int renderTime;
232 #endif
233
234     m_bindable = &bindable;
235     preprocess();
236
237     bindable.bind();
238 #ifdef QSG_RENDERER_TIMING
239     if (qsg_render_timing)
240         bindTime = frameTimer.elapsed();
241 #endif
242
243 #ifndef QT_NO_DEBUG
244     // Sanity check that attribute registers are disabled
245     {
246         GLint count;
247         glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count);
248         GLint enabled;
249         for (int i=0; i<count; ++i) {
250             glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
251             if (enabled) {
252                 qWarning("QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i);
253             }
254         }
255     }
256 #endif
257
258     render();
259 #ifdef QSG_RENDERER_TIMING
260     if (qsg_render_timing)
261         renderTime = frameTimer.elapsed();
262 #endif
263
264     glDisable(GL_SCISSOR_TEST);
265     m_is_rendering = false;
266     m_changed_emitted = false;
267     m_bindable = 0;
268
269     if (m_vertex_buffer_bound) {
270         glBindBuffer(GL_ARRAY_BUFFER, 0);
271         m_vertex_buffer_bound = false;
272     }
273
274     if (m_index_buffer_bound) {
275         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
276         m_index_buffer_bound = false;
277     }
278
279 #ifdef QSG_RENDERER_TIMING
280     if (qsg_render_timing) {
281         printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n",
282                preprocessTime,
283                updatePassTime - preprocessTime,
284                bindTime - updatePassTime,
285                renderTime - bindTime,
286                renderTime);
287     }
288 #endif
289 }
290
291 void QSGRenderer::setProjectionMatrixToDeviceRect()
292 {
293     setProjectionMatrixToRect(m_device_rect);
294 }
295
296 void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect)
297 {
298     QMatrix4x4 matrix;
299     matrix.ortho(rect.x(),
300                  rect.x() + rect.width(),
301                  rect.y() + rect.height(),
302                  rect.y(),
303                  qreal(0.01),
304                  -1);
305     setProjectionMatrix(matrix);
306 }
307
308 void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
309 {
310     m_projection_matrix = matrix;
311     // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
312     m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
313 }
314
315 void QSGRenderer::setClearColor(const QColor &color)
316 {
317     m_clear_color = color;
318 }
319
320 /*!
321     Updates internal data structures and emits the sceneGraphChanged() signal.
322
323     If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be
324     in the process of being destroyed. It is then not safe to downcast the node
325     pointer.
326 */
327
328 void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
329 {
330     if (state & QSGNode::DirtyNodeAdded)
331         addNodesToPreprocess(node);
332     if (state & QSGNode::DirtyNodeRemoved)
333         removeNodesToPreprocess(node);
334     if (state & QSGNode::DirtyUsePreprocess) {
335         if (node->flags() & QSGNode::UsePreprocess)
336             m_nodes_to_preprocess.insert(node);
337         else
338             m_nodes_to_preprocess.remove(node);
339     }
340
341     if (!m_changed_emitted && !m_is_rendering) {
342         // Premature overoptimization to avoid excessive signal emissions
343         m_changed_emitted = true;
344         emit sceneGraphChanged();
345     }
346 }
347
348 void QSGRenderer::materialChanged(QSGGeometryNode *, QSGMaterial *, QSGMaterial *)
349 {
350 }
351
352 void QSGRenderer::preprocess()
353 {
354     Q_ASSERT(m_root_node);
355
356     // We need to take a copy here, in case any of the preprocess calls deletes a node that
357     // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
358     // For the default case, when this does not happen, the cost is neglishible.
359     QSet<QSGNode *> items = m_nodes_to_preprocess;
360
361     for (QSet<QSGNode *>::const_iterator it = items.constBegin();
362          it != items.constEnd(); ++it) {
363         QSGNode *n = *it;
364         if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) {
365             n->preprocess();
366         }
367     }
368
369 #ifdef QSG_RENDERER_TIMING
370     if (qsg_render_timing)
371         preprocessTime = frameTimer.elapsed();
372 #endif
373
374     nodeUpdater()->setToplevelOpacity(context()->renderAlpha());
375     nodeUpdater()->updateStates(m_root_node);
376
377 #ifdef QSG_RENDERER_TIMING
378     if (qsg_render_timing)
379         updatePassTime = frameTimer.elapsed();
380 #endif
381
382 }
383
384 void QSGRenderer::addNodesToPreprocess(QSGNode *node)
385 {
386     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
387         addNodesToPreprocess(c);
388     if (node->flags() & QSGNode::UsePreprocess)
389         m_nodes_to_preprocess.insert(node);
390 }
391
392 void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
393 {
394     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
395         removeNodesToPreprocess(c);
396     if (node->flags() & QSGNode::UsePreprocess)
397         m_nodes_to_preprocess.remove(node);
398 }
399
400
401 /*!
402     Convenience function to set up the stencil buffer for clipping based on \a clip.
403
404     If the clip is a pixel aligned rectangle, this function will use glScissor instead
405     of stencil.
406  */
407
408 QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
409 {
410     if (!clip) {
411         glDisable(GL_STENCIL_TEST);
412         glDisable(GL_SCISSOR_TEST);
413         return NoClip;
414     }
415
416     bool stencilEnabled = false;
417     bool scissorEnabled = false;
418
419     glDisable(GL_SCISSOR_TEST);
420
421     int clipDepth = 0;
422     QRect clipRect;
423     while (clip) {
424         QMatrix4x4 m = m_current_projection_matrix;
425         if (clip->matrix())
426             m *= *clip->matrix();
427
428         // TODO: Check for multisampling and pixel grid alignment.
429         bool isRectangleWithNoPerspective = clip->isRectangular()
430                 && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
431         bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
432         bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
433
434         if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
435             QRectF bbox = clip->clipRect();
436             qreal invW = 1 / m(3, 3);
437             qreal fx1, fy1, fx2, fy2;
438             if (noRotate) {
439                 fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
440                 fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
441                 fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
442                 fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
443             } else {
444                 Q_ASSERT(isRotate90);
445                 fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
446                 fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
447                 fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
448                 fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
449             }
450
451             if (fx1 > fx2)
452                 qSwap(fx1, fx2);
453             if (fy1 > fy2)
454                 qSwap(fy1, fy2);
455
456             GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5));
457             GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5));
458             GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5));
459             GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5));
460
461             if (!scissorEnabled) {
462                 clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
463                 glEnable(GL_SCISSOR_TEST);
464                 scissorEnabled = true;
465             } else {
466                 clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
467             }
468
469             glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
470         } else {
471             if (!stencilEnabled) {
472                 if (!m_clip_program.isLinked()) {
473                     m_clip_program.addShaderFromSourceCode(QOpenGLShader::Vertex,
474                         "attribute highp vec4 vCoord;       \n"
475                         "uniform highp mat4 matrix;         \n"
476                         "void main() {                      \n"
477                         "    gl_Position = matrix * vCoord; \n"
478                         "}");
479                     m_clip_program.addShaderFromSourceCode(QOpenGLShader::Fragment,
480                         "void main() {                                   \n"
481                         "    gl_FragColor = vec4(0.81, 0.83, 0.12, 1.0); \n" // Trolltech green ftw!
482                         "}");
483                     m_clip_program.bindAttributeLocation("vCoord", 0);
484                     m_clip_program.link();
485                     m_clip_matrix_id = m_clip_program.uniformLocation("matrix");
486                 }
487
488                 glStencilMask(0xff); // write mask
489                 glClearStencil(0);
490                 glClear(GL_STENCIL_BUFFER_BIT);
491                 glEnable(GL_STENCIL_TEST);
492                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
493                 glDepthMask(GL_FALSE);
494
495                 m_clip_program.bind();
496                 m_clip_program.enableAttributeArray(0);
497
498                 stencilEnabled = true;
499             }
500
501             glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
502             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
503
504             const QSGGeometry *g = clip->geometry();
505             Q_ASSERT(g->attributeCount() > 0);
506             const QSGGeometry::Attribute *a = g->attributes();
507             glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData());
508
509             m_clip_program.setUniformValue(m_clip_matrix_id, m);
510             if (g->indexCount()) {
511                 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
512             } else {
513                 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
514             }
515
516             ++clipDepth;
517         }
518
519         clip = clip->clipList();
520     }
521
522     if (stencilEnabled) {
523         m_clip_program.disableAttributeArray(0);
524         glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
525         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
526         glStencilMask(0); // write mask
527         bindable()->reactivate();
528     } else {
529         glDisable(GL_STENCIL_TEST);
530     }
531
532     if (!scissorEnabled)
533         glDisable(GL_SCISSOR_TEST);
534
535     return stencilEnabled ? StencilClip : ScissorClip;
536 }
537
538
539
540 static inline int size_of_type(GLenum type)
541 {
542     static int sizes[] = {
543         sizeof(char),
544         sizeof(unsigned char),
545         sizeof(short),
546         sizeof(unsigned short),
547         sizeof(int),
548         sizeof(unsigned int),
549         sizeof(float),
550         2,
551         3,
552         4,
553         sizeof(double)
554     };
555     Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
556     return sizes[type - GL_BYTE];
557 }
558
559
560 class QSGRendererVBOGeometryData : public QSGGeometryData
561 {
562 public:
563     QSGRendererVBOGeometryData()
564         : vertexBuffer(0)
565         , indexBuffer(0)
566     {
567     }
568
569     ~QSGRendererVBOGeometryData()
570     {
571         QOpenGLContext *ctx = QOpenGLContext::currentContext();
572         if (!ctx)
573             return;
574         QOpenGLFunctions *func = ctx->functions();
575         if (vertexBuffer)
576             func->glDeleteBuffers(1, &vertexBuffer);
577         if (indexBuffer)
578             func->glDeleteBuffers(1, &indexBuffer);
579     }
580
581     GLuint vertexBuffer;
582     GLuint indexBuffer;
583
584     static QSGRendererVBOGeometryData *get(const QSGGeometry *g) {
585         QSGRendererVBOGeometryData *gd = static_cast<QSGRendererVBOGeometryData *>(QSGGeometryData::data(g));
586         if (!gd) {
587             gd = new QSGRendererVBOGeometryData;
588             QSGGeometryData::install(g, gd);
589         }
590         return gd;
591     }
592
593 };
594
595 static inline GLenum qt_drawTypeForPattern(QSGGeometry::DataPattern p)
596 {
597     Q_ASSERT(p > 0 && p <= 3);
598     static GLenum drawTypes[] = { 0,
599                                   GL_STREAM_DRAW,
600                                   GL_DYNAMIC_DRAW,
601                                   GL_STATIC_DRAW
602                             };
603     return drawTypes[p];
604 }
605
606
607 /*!
608     Issues the GL draw call for the geometry \a g using the material \a shader.
609
610     The function assumes that attributes have been bound and set up prior
611     to making this call.
612
613     \internal
614  */
615
616 void QSGRenderer::draw(const QSGMaterialShader *shader, const QSGGeometry *g)
617 {
618     // ### remove before final release...
619     static bool use_vbo = !QGuiApplication::arguments().contains(QLatin1String("--no-vbo"));
620
621     const void *vertexData;
622     int vertexByteSize = g->vertexCount() * g->sizeOfVertex();
623     if (use_vbo && g->vertexDataPattern() != QSGGeometry::AlwaysUploadPattern && vertexByteSize > 1024) {
624
625         // The base pointer for a VBO is 0
626         vertexData = 0;
627
628         bool updateData = QSGGeometryData::hasDirtyVertexData(g);
629         QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
630         if (!gd->vertexBuffer) {
631             glGenBuffers(1, &gd->vertexBuffer);
632             updateData = true;
633         }
634
635         glBindBuffer(GL_ARRAY_BUFFER, gd->vertexBuffer);
636         m_vertex_buffer_bound = true;
637
638         if (updateData) {
639             glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(),
640                          qt_drawTypeForPattern(g->vertexDataPattern()));
641             QSGGeometryData::clearDirtyVertexData(g);
642         }
643
644     } else {
645         if (m_vertex_buffer_bound) {
646             glBindBuffer(GL_ARRAY_BUFFER, 0);
647             m_vertex_buffer_bound = false;
648         }
649         vertexData = g->vertexData();
650     }
651
652     // Bind the vertices to attributes...
653     char const *const *attrNames = shader->attributeNames();
654     int offset = 0;
655     for (int j = 0; attrNames[j]; ++j) {
656         if (!*attrNames[j])
657             continue;
658         Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material");
659         const QSGGeometry::Attribute &a = g->attributes()[j];
660         Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions");
661
662 #if defined(QT_OPENGL_ES_2)
663         GLboolean normalize = a.type != GL_FLOAT;
664 #else
665         GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
666 #endif
667         glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (char *) vertexData + offset);
668         offset += a.tupleSize * size_of_type(a.type);
669     }
670
671     // Set up the indices...
672     const void *indexData;
673     if (use_vbo && g->indexDataPattern() != QSGGeometry::AlwaysUploadPattern && g->indexCount() > 512) {
674
675         // Base pointer for a VBO is 0
676         indexData = 0;
677
678         bool updateData = QSGGeometryData::hasDirtyIndexData(g);
679         QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
680         if (!gd->indexBuffer) {
681             glGenBuffers(1, &gd->indexBuffer);
682             updateData = true;
683         }
684
685         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gd->indexBuffer);
686         m_index_buffer_bound = true;
687
688         if (updateData) {
689             glBufferData(GL_ELEMENT_ARRAY_BUFFER,
690                          g->indexCount() * g->sizeOfIndex(),
691                          g->indexData(),
692                          qt_drawTypeForPattern(g->indexDataPattern()));
693             QSGGeometryData::clearDirtyIndexData(g);
694         }
695
696     } else {
697         if (m_index_buffer_bound) {
698             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
699             m_index_buffer_bound = false;
700         }
701         indexData = g->indexData();
702     }
703
704     // Set the line width if applicable
705     if (g->drawingMode() == GL_LINES || g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP) {
706         glLineWidth(g->lineWidth());
707     }
708
709     // draw the stuff...
710     if (g->indexCount()) {
711         glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), indexData);
712     } else {
713         glDrawArrays(g->drawingMode(), 0, g->vertexCount());
714     }
715
716     // We leave buffers bound for now... They will be reset by bind on next draw() or
717     // set back to 0 if next draw is not using VBOs
718
719 }
720
721 /*!
722     \class QSGNodeDumper
723     \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console.
724
725     This class is solely for debugging purposes.
726
727     \internal
728  */
729
730 void QSGNodeDumper::dump(QSGNode *n)
731 {
732     QSGNodeDumper dump;
733     dump.visitNode(n);
734 }
735
736 void QSGNodeDumper::visitNode(QSGNode *n)
737 {
738     qDebug() << QString(m_indent * 2, QLatin1Char(' ')) << n;
739     QSGNodeVisitor::visitNode(n);
740 }
741
742 void QSGNodeDumper::visitChildren(QSGNode *n)
743 {
744     ++m_indent;
745     QSGNodeVisitor::visitChildren(n);
746     --m_indent;
747 }
748
749
750 QT_END_NAMESPACE