rename QSGGeometry::stride() to sizeOfVertex for symetry with index
[profile/ivi/qtdeclarative.git] / src / declarative / scenegraph / coreapi / qsgrenderer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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::DirtyFlags flags)
329 {
330     Q_UNUSED(node);
331     Q_UNUSED(flags);
332
333     if (flags & QSGNode::DirtyNodeAdded)
334         addNodesToPreprocess(node);
335     if (flags & QSGNode::DirtyNodeRemoved)
336         removeNodesToPreprocess(node);
337
338     if (!m_changed_emitted && !m_is_rendering) {
339         // Premature overoptimization to avoid excessive signal emissions
340         m_changed_emitted = true;
341         emit sceneGraphChanged();
342     }
343 }
344
345 void QSGRenderer::materialChanged(QSGGeometryNode *, QSGMaterial *, QSGMaterial *)
346 {
347 }
348
349 void QSGRenderer::preprocess()
350 {
351     Q_ASSERT(m_root_node);
352
353     // We need to take a copy here, in case any of the preprocess calls deletes a node that
354     // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
355     // For the default case, when this does not happen, the cost is neglishible.
356     QSet<QSGNode *> items = m_nodes_to_preprocess;
357
358     for (QSet<QSGNode *>::const_iterator it = items.constBegin();
359          it != items.constEnd(); ++it) {
360         QSGNode *n = *it;
361         if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) {
362             n->preprocess();
363         }
364     }
365
366 #ifdef QSG_RENDERER_TIMING
367     if (qsg_render_timing)
368         preprocessTime = frameTimer.elapsed();
369 #endif
370
371     nodeUpdater()->setToplevelOpacity(context()->renderAlpha());
372     nodeUpdater()->updateStates(m_root_node);
373
374 #ifdef QSG_RENDERER_TIMING
375     if (qsg_render_timing)
376         updatePassTime = frameTimer.elapsed();
377 #endif
378
379 }
380
381 void QSGRenderer::addNodesToPreprocess(QSGNode *node)
382 {
383     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
384         addNodesToPreprocess(c);
385     if (node->flags() & QSGNode::UsePreprocess)
386         m_nodes_to_preprocess.insert(node);
387 }
388
389 void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
390 {
391     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
392         removeNodesToPreprocess(c);
393     if (node->flags() & QSGNode::UsePreprocess)
394         m_nodes_to_preprocess.remove(node);
395 }
396
397
398 /*!
399     Convenience function to set up the stencil buffer for clipping based on \a clip.
400
401     If the clip is a pixel aligned rectangle, this function will use glScissor instead
402     of stencil.
403  */
404
405 QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
406 {
407     if (!clip) {
408         glDisable(GL_STENCIL_TEST);
409         glDisable(GL_SCISSOR_TEST);
410         return NoClip;
411     }
412
413     bool stencilEnabled = false;
414     bool scissorEnabled = false;
415
416     glDisable(GL_SCISSOR_TEST);
417
418     int clipDepth = 0;
419     QRect clipRect;
420     while (clip) {
421         QMatrix4x4 m = m_current_projection_matrix;
422         if (clip->matrix())
423             m *= *clip->matrix();
424
425         // TODO: Check for multisampling and pixel grid alignment.
426         bool isRectangleWithNoPerspective = clip->isRectangular()
427                 && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
428         bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
429         bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
430
431         if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
432             QRectF bbox = clip->clipRect();
433             qreal invW = 1 / m(3, 3);
434             qreal fx1, fy1, fx2, fy2;
435             if (noRotate) {
436                 fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
437                 fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
438                 fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
439                 fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
440             } else {
441                 Q_ASSERT(isRotate90);
442                 fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
443                 fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
444                 fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
445                 fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
446             }
447
448             if (fx1 > fx2)
449                 qSwap(fx1, fx2);
450             if (fy1 > fy2)
451                 qSwap(fy1, fy2);
452
453             GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5));
454             GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5));
455             GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5));
456             GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5));
457
458             if (!scissorEnabled) {
459                 clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
460                 glEnable(GL_SCISSOR_TEST);
461                 scissorEnabled = true;
462             } else {
463                 clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
464             }
465
466             glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
467         } else {
468             if (!stencilEnabled) {
469                 if (!m_clip_program.isLinked()) {
470                     m_clip_program.addShaderFromSourceCode(QOpenGLShader::Vertex,
471                         "attribute highp vec4 vCoord;       \n"
472                         "uniform highp mat4 matrix;         \n"
473                         "void main() {                      \n"
474                         "    gl_Position = matrix * vCoord; \n"
475                         "}");
476                     m_clip_program.addShaderFromSourceCode(QOpenGLShader::Fragment,
477                         "void main() {                                   \n"
478                         "    gl_FragColor = vec4(0.81, 0.83, 0.12, 1.0); \n" // Trolltech green ftw!
479                         "}");
480                     m_clip_program.bindAttributeLocation("vCoord", 0);
481                     m_clip_program.link();
482                     m_clip_matrix_id = m_clip_program.uniformLocation("matrix");
483                 }
484
485                 glStencilMask(0xff); // write mask
486                 glClearStencil(0);
487                 glClear(GL_STENCIL_BUFFER_BIT);
488                 glEnable(GL_STENCIL_TEST);
489                 glDisable(GL_DEPTH_TEST);
490                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
491                 glDepthMask(GL_FALSE);
492
493                 m_clip_program.bind();
494                 m_clip_program.enableAttributeArray(0);
495
496                 stencilEnabled = true;
497             }
498
499             glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
500             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
501
502             const QSGGeometry *g = clip->geometry();
503             Q_ASSERT(g->attributeCount() > 0);
504             const QSGGeometry::Attribute *a = g->attributes();
505             glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData());
506
507             m_clip_program.setUniformValue(m_clip_matrix_id, m);
508             if (g->indexCount()) {
509                 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
510             } else {
511                 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
512             }
513
514             ++clipDepth;
515         }
516
517         clip = clip->clipList();
518     }
519
520     if (stencilEnabled) {
521         m_clip_program.disableAttributeArray(0);
522         glEnable(GL_DEPTH_TEST);
523         glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
524         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
525         glStencilMask(0); // write mask
526         bindable()->reactivate();
527         //glDepthMask(GL_TRUE); // must be reset correctly by caller.
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         QOpenGLFunctions *func = QOpenGLContext::currentContext()->functions();
572         if (vertexBuffer)
573             func->glDeleteBuffers(1, &vertexBuffer);
574         if (indexBuffer)
575             func->glDeleteBuffers(1, &indexBuffer);
576     }
577
578     GLuint vertexBuffer;
579     GLuint indexBuffer;
580
581     static QSGRendererVBOGeometryData *get(const QSGGeometry *g) {
582         QSGRendererVBOGeometryData *gd = static_cast<QSGRendererVBOGeometryData *>(QSGGeometryData::data(g));
583         if (!gd) {
584             gd = new QSGRendererVBOGeometryData;
585             QSGGeometryData::install(g, gd);
586         }
587         return gd;
588     }
589
590 };
591
592 static inline GLenum qt_drawTypeForPattern(QSGGeometry::DataPattern p)
593 {
594     Q_ASSERT(p > 0 && p <= 3);
595     static GLenum drawTypes[] = { 0,
596                                   GL_STREAM_DRAW,
597                                   GL_DYNAMIC_DRAW,
598                                   GL_STATIC_DRAW
599                             };
600     return drawTypes[p];
601 }
602
603
604 /*!
605     Issues the GL draw call for the geometry \a g using the material \a shader.
606
607     The function assumes that attributes have been bound and set up prior
608     to making this call.
609
610     \internal
611  */
612
613 void QSGRenderer::draw(const QSGMaterialShader *shader, const QSGGeometry *g)
614 {
615     // ### remove before final release...
616     static bool use_vbo = !QGuiApplication::arguments().contains(QLatin1String("--no-vbo"));
617
618     const void *vertexData;
619     int vertexByteSize = g->vertexCount() * g->sizeOfVertex();
620     if (use_vbo && g->vertexDataPattern() != QSGGeometry::AlwaysUploadPattern && vertexByteSize > 1024) {
621
622         // The base pointer for a VBO is 0
623         vertexData = 0;
624
625         bool updateData = QSGGeometryData::hasDirtyVertexData(g);
626         QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
627         if (!gd->vertexBuffer) {
628             glGenBuffers(1, &gd->vertexBuffer);
629             updateData = true;
630         }
631
632         glBindBuffer(GL_ARRAY_BUFFER, gd->vertexBuffer);
633         m_vertex_buffer_bound = true;
634
635         if (updateData) {
636             glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(),
637                          qt_drawTypeForPattern(g->vertexDataPattern()));
638             QSGGeometryData::clearDirtyVertexData(g);
639         }
640
641     } else {
642         if (m_vertex_buffer_bound) {
643             glBindBuffer(GL_ARRAY_BUFFER, 0);
644             m_vertex_buffer_bound = false;
645         }
646         vertexData = g->vertexData();
647     }
648
649     // Bind the vertices to attributes...
650     char const *const *attrNames = shader->attributeNames();
651     int offset = 0;
652     for (int j = 0; attrNames[j]; ++j) {
653         if (!*attrNames[j])
654             continue;
655         Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material");
656         const QSGGeometry::Attribute &a = g->attributes()[j];
657         Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions");
658
659 #if defined(QT_OPENGL_ES_2)
660         GLboolean normalize = a.type != GL_FLOAT;
661 #else
662         GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
663 #endif
664         glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (char *) vertexData + offset);
665         offset += a.tupleSize * size_of_type(a.type);
666     }
667
668     // Set up the indices...
669     const void *indexData;
670     if (use_vbo && g->indexDataPattern() != QSGGeometry::AlwaysUploadPattern && g->indexCount() > 512) {
671
672         // Base pointer for a VBO is 0
673         indexData = 0;
674
675         bool updateData = QSGGeometryData::hasDirtyIndexData(g);
676         QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
677         if (!gd->indexBuffer) {
678             glGenBuffers(1, &gd->indexBuffer);
679             updateData = true;
680         }
681
682         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gd->indexBuffer);
683         m_index_buffer_bound = true;
684
685         if (updateData) {
686             glBufferData(GL_ELEMENT_ARRAY_BUFFER,
687                          g->indexCount() * g->sizeOfIndex(),
688                          g->indexData(),
689                          qt_drawTypeForPattern(g->indexDataPattern()));
690             QSGGeometryData::clearDirtyIndexData(g);
691         }
692
693     } else {
694         if (m_index_buffer_bound) {
695             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
696             m_index_buffer_bound = false;
697         }
698         indexData = g->indexData();
699     }
700
701
702     // draw the stuff...
703     if (g->indexCount()) {
704         glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), indexData);
705     } else {
706         glDrawArrays(g->drawingMode(), 0, g->vertexCount());
707     }
708
709     // We leave buffers bound for now... They will be reset by bind on next draw() or
710     // set back to 0 if next draw is not using VBOs
711
712 }
713
714 /*!
715     \class QSGNodeDumper
716     \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console.
717
718     This class is solely for debugging purposes.
719
720     \internal
721  */
722
723 void QSGNodeDumper::dump(QSGNode *n)
724 {
725     QSGNodeDumper dump;
726     dump.visitNode(n);
727 }
728
729 void QSGNodeDumper::visitNode(QSGNode *n)
730 {
731     qDebug() << QString(m_indent * 2, QLatin1Char(' ')) << n;
732     QSGNodeVisitor::visitNode(n);
733 }
734
735 void QSGNodeDumper::visitChildren(QSGNode *n)
736 {
737     ++m_indent;
738     QSGNodeVisitor::visitChildren(n);
739     --m_indent;
740 }
741
742
743 QT_END_NAMESPACE