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