aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2011-08-09 14:50:07 +0200
committerYoann Lopes <yoann.lopes@nokia.com>2011-08-11 09:28:45 +0200
commit2b28e1f18c3d9f8f7c116669049aaffaa220804a (patch)
treefdb6f51c5e7fd9feea779c07719769e6835d12b7
parenta297bdfc574b757e2f6cebb897a4dddafd6e15a6 (diff)
Implement VBO caching of larger geometries
Change-Id: If87b70b191a06448287b47d252a288b441af6302 Reviewed-on: http://codereview.qt.nokia.com/2784 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Yoann Lopes <yoann.lopes@nokia.com>
-rw-r--r--src/declarative/items/qsgtextnode.cpp13
-rw-r--r--src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp3
-rw-r--r--src/declarative/scenegraph/coreapi/qsggeometry.cpp113
-rw-r--r--src/declarative/scenegraph/coreapi/qsggeometry.h28
-rw-r--r--src/declarative/scenegraph/coreapi/qsggeometry_p.h32
-rw-r--r--src/declarative/scenegraph/coreapi/qsgrenderer.cpp187
-rw-r--r--src/declarative/scenegraph/coreapi/qsgrenderer_p.h12
-rw-r--r--src/declarative/scenegraph/scenegraph.pri4
8 files changed, 351 insertions, 41 deletions
diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp
index d36db1b6b6..03a558fe3d 100644
--- a/src/declarative/items/qsgtextnode.cpp
+++ b/src/declarative/items/qsgtextnode.cpp
@@ -166,9 +166,20 @@ QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &g
node->update();
- if (node != prevNode)
+ // A new node, add it to the graph.
+ if (node != prevNode) {
appendChildNode(node);
+ /* We flag the geometry as static, but we never call markVertexDataDirty
+ or markIndexDataDirty on them. This is because all text nodes are
+ discarded when a change occurs. If we start appending/removing from
+ existing geometry, then we also need to start marking the geometry as
+ dirty.
+ */
+ node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
+ node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
+ }
+
return node;
}
diff --git a/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp
index 8af626e286..ce05e76969 100644
--- a/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp
+++ b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp
@@ -510,8 +510,7 @@ void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list)
//glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale);
const QSGGeometry *g = geomNode->geometry();
- bindGeometry(program, g);
- draw(geomNode);
+ draw(program, g);
#ifdef RENDERER_DEBUG
geometryNodesDrawn++;
diff --git a/src/declarative/scenegraph/coreapi/qsggeometry.cpp b/src/declarative/scenegraph/coreapi/qsggeometry.cpp
index 71b5cb63f1..6b622afd8d 100644
--- a/src/declarative/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/declarative/scenegraph/coreapi/qsggeometry.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qsggeometry.h"
+#include "qsggeometry_p.h"
QT_BEGIN_NAMESPACE
@@ -122,7 +123,10 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
, m_attributes(attributes)
, m_data(0)
, m_index_data_offset(-1)
+ , m_server_data(0)
, m_owns_data(false)
+ , m_index_usage_pattern(AlwaysUploadPattern)
+ , m_vertex_usage_pattern(AlwaysUploadPattern)
{
Q_ASSERT(m_attributes.count > 0);
Q_ASSERT(m_attributes.stride > 0);
@@ -136,6 +140,9 @@ QSGGeometry::~QSGGeometry()
{
if (m_owns_data)
qFree(m_data);
+
+ if (m_server_data)
+ delete m_server_data;
}
/*!
@@ -250,6 +257,15 @@ void QSGGeometry::allocate(int vertexCount, int indexCount)
m_owns_data = true;
}
+ // If we have associated vbo data we could potentially crash later if
+ // the old buffers are used with the new vertex and index count, so we force
+ // an update in the renderer in that case. This is really the users responsibility
+ // but it is cheap for us to enforce this, so why not...
+ if (m_server_data) {
+ markIndexDataDirty();
+ markVertexDataDirty();
+ }
+
}
/*!
@@ -307,4 +323,101 @@ void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect,
v[3].ty = textureRect.bottom();
}
+
+
+/*!
+ \enum QSGGeometry::DataPattern
+
+ The DataPattern enum is used to specify the use pattern for the vertex
+ and index data in a geometry object.
+
+ \value AlwaysUploadPattern The data is always uploaded. This means that
+ the user does not need to explicitly mark index and vertex data as
+ dirty after changing it. This is the default.
+
+ \value DynamicPattern The data is modified repeatedly and drawn many times.
+ This is a hint that may provide better performance. When set
+ the user must make sure to mark the data as dirty after changing it.
+
+ \value StaticPattern The data is modified once and drawn many times. This is
+ a hint that may provide better performance. When set the user must make sure
+ to mark the data as dirty after changing it.
+ */
+
+
+/*!
+ \fn QSGGeometry::DataPattern QSGGeometry::indexDataPattern() const
+
+ Returns the usage pattern for indices in this geometry. The default
+ pattern is AlwaysUploadPattern.
+ */
+
+/*!
+ Sets the usage pattern for indices to \a p.
+
+ The default is AlwaysUploadPattern. When set to anything other than
+ the default, the user must call markIndexDataDirty() after changing
+ the index data.
+ */
+
+void QSGGeometry::setIndexDataPattern(DataPattern p)
+{
+ m_index_usage_pattern = p;
+}
+
+
+
+
+/*!
+ \fn QSGGeometry::DataPattern QSGGeometry::vertexDataPattern() const
+
+ Returns the usage pattern for vertices in this geometry. The default
+ pattern is AlwaysUploadPattern.
+ */
+
+/*!
+ Sets the usage pattern for vertices to \a p.
+
+ The default is AlwaysUploadPattern. When set to anything other than
+ the default, the user must call markVertexDataDirty() after changing
+ the vertex data.
+ */
+
+void QSGGeometry::setVertexDataPattern(DataPattern p)
+{
+ m_vertex_usage_pattern = p;
+}
+
+
+
+
+/*!
+ Mark that the vertices in this geometry has changed and must be uploaded
+ again.
+
+ This function only has an effect when the usage pattern for vertices is
+ StaticData and the renderer that renders this geometry uploads the geometry
+ into Vertex Buffer Objects (VBOs).
+ */
+void QSGGeometry::markIndexDataDirty()
+{
+ m_dirty_index_data = true;
+}
+
+
+
+/*!
+ Mark that the vertices in this geometry has changed and must be uploaded
+ again.
+
+ This function only has an effect when the usage pattern for vertices is
+ StaticData and the renderer that renders this geometry uploads the geometry
+ into Vertex Buffer Objects (VBOs).
+ */
+void QSGGeometry::markVertexDataDirty()
+{
+ m_dirty_vertex_data = true;
+}
+
+
QT_END_NAMESPACE
diff --git a/src/declarative/scenegraph/coreapi/qsggeometry.h b/src/declarative/scenegraph/coreapi/qsggeometry.h
index 432503085d..f99eee3c4b 100644
--- a/src/declarative/scenegraph/coreapi/qsggeometry.h
+++ b/src/declarative/scenegraph/coreapi/qsggeometry.h
@@ -50,6 +50,8 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
+class QSGGeometryData;
+
class Q_DECLARATIVE_EXPORT QSGGeometry
{
public:
@@ -92,6 +94,13 @@ public:
static const AttributeSet &defaultAttributes_TexturedPoint2D();
static const AttributeSet &defaultAttributes_ColoredPoint2D();
+ enum DataPattern {
+ AlwaysUploadPattern = 0,
+ StreamPattern = 1,
+ DynamicPattern = 2,
+ StaticPattern = 3
+ };
+
QSGGeometry(const QSGGeometry::AttributeSet &attribs,
int vertexCount,
int indexCount = 0,
@@ -134,7 +143,18 @@ public:
static void updateRectGeometry(QSGGeometry *g, const QRectF &rect);
static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect);
+ void setIndexDataPattern(DataPattern p);
+ DataPattern indexDataPattern() const { return (DataPattern) m_index_usage_pattern; }
+
+ void setVertexDataPattern(DataPattern p);
+ DataPattern vertexDataPattern() const { return (DataPattern) m_vertex_usage_pattern; }
+
+ void markIndexDataDirty();
+ void markVertexDataDirty();
+
private:
+ friend class QSGGeometryData;
+
int m_drawing_mode;
int m_vertex_count;
int m_index_count;
@@ -143,10 +163,14 @@ private:
void *m_data;
int m_index_data_offset;
- void *m_reserved_pointer;
+ QSGGeometryData *m_server_data;
uint m_owns_data : 1;
- uint m_reserved_bits : 31;
+ uint m_index_usage_pattern : 2;
+ uint m_vertex_usage_pattern : 2;
+ uint m_dirty_index_data : 1;
+ uint m_dirty_vertex_data : 1;
+ uint m_reserved_bits : 27;
float m_prealloc[16];
};
diff --git a/src/declarative/scenegraph/coreapi/qsggeometry_p.h b/src/declarative/scenegraph/coreapi/qsggeometry_p.h
new file mode 100644
index 0000000000..2fba155000
--- /dev/null
+++ b/src/declarative/scenegraph/coreapi/qsggeometry_p.h
@@ -0,0 +1,32 @@
+#ifndef QSGGEOMETRY_P_H
+#define QSGGEOMETRY_P_H
+
+#include "qsggeometry.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGGeometryData
+{
+public:
+ virtual ~QSGGeometryData() {}
+
+ static inline QSGGeometryData *data(const QSGGeometry *g) {
+ return g->m_server_data;
+ }
+
+ static inline void install(const QSGGeometry *g, QSGGeometryData *data) {
+ Q_ASSERT(!g->m_server_data);
+ const_cast<QSGGeometry *>(g)->m_server_data = data;
+ }
+
+ static bool inline hasDirtyVertexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; }
+ static void inline clearDirtyVertexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_vertex_data = false; }
+
+ static bool inline hasDirtyIndexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; }
+ static void inline clearDirtyIndexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_index_data = false; }
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGGEOMETRY_P_H
diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp
index 78551b139b..5a137adfe5 100644
--- a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp
@@ -43,6 +43,7 @@
#include "qsgnode.h"
#include "qsgmaterial.h"
#include "qsgnodeupdater_p.h"
+#include "qsggeometry_p.h"
#include "private/qsgadaptationlayer_p.h"
@@ -130,6 +131,8 @@ QSGRenderer::QSGRenderer(QSGContext *context)
, m_changed_emitted(false)
, m_mirrored(false)
, m_is_rendering(false)
+ , m_vertex_buffer_bound(false)
+ , m_index_buffer_bound(false)
{
initializeGLFunctions();
}
@@ -254,6 +257,16 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
m_changed_emitted = false;
m_bindable = 0;
+ if (m_vertex_buffer_bound) {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_vertex_buffer_bound = false;
+ }
+
+ if (m_index_buffer_bound) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ m_index_buffer_bound = false;
+ }
+
#ifdef QSG_RENDERER_TIMING
printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n",
preprocessTime,
@@ -459,14 +472,17 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
- const QSGGeometry *geometry = clip->geometry();
- Q_ASSERT(geometry->attributeCount() > 0);
- const QSGGeometry::Attribute *a = geometry->attributes();
-
- glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, geometry->stride(), geometry->vertexData());
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() > 0);
+ const QSGGeometry::Attribute *a = g->attributes();
+ glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->stride(), g->vertexData());
m_clip_program.setUniformValue(m_clip_matrix_id, m);
- draw(clip);
+ if (g->indexCount()) {
+ glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ } else {
+ glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ }
++clipDepth;
}
@@ -493,25 +509,6 @@ QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
}
-/*!
- Issues the GL draw call for \a geometryNode.
-
- The function assumes that attributes have been bound and set up prior
- to making this call.
-
- \internal
- */
-
-void QSGRenderer::draw(const QSGBasicGeometryNode *node)
-{
- const QSGGeometry *g = node->geometry();
- if (g->indexCount()) {
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- } else {
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
- }
-}
-
static inline int size_of_type(GLenum type)
{
@@ -528,19 +525,102 @@ static inline int size_of_type(GLenum type)
4,
sizeof(double)
};
+ Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
return sizes[type - GL_BYTE];
}
+
+class QSGRendererVBOGeometryData : public QSGGeometryData
+{
+public:
+ QSGRendererVBOGeometryData()
+ : vertexBuffer(0)
+ , indexBuffer(0)
+ {
+ }
+
+ ~QSGRendererVBOGeometryData()
+ {
+ QGLFunctions *func = QGLContext::currentContext()->functions();
+ if (vertexBuffer)
+ func->glDeleteBuffers(1, &vertexBuffer);
+ if (indexBuffer)
+ func->glDeleteBuffers(1, &indexBuffer);
+ }
+
+ GLuint vertexBuffer;
+ GLuint indexBuffer;
+
+ static QSGRendererVBOGeometryData *get(const QSGGeometry *g) {
+ QSGRendererVBOGeometryData *gd = static_cast<QSGRendererVBOGeometryData *>(QSGGeometryData::data(g));
+ if (!gd) {
+ gd = new QSGRendererVBOGeometryData;
+ QSGGeometryData::install(g, gd);
+ }
+ return gd;
+ }
+
+};
+
+static inline GLenum qt_drawTypeForPattern(QSGGeometry::DataPattern p)
+{
+ Q_ASSERT(p > 0 && p <= 3);
+ static GLenum drawTypes[] = { 0,
+ GL_STREAM_DRAW,
+ GL_DYNAMIC_DRAW,
+ GL_STATIC_DRAW
+ };
+ return drawTypes[p];
+}
+
+
/*!
- Convenience function to set up and bind the vertex data in \a g to the
- required attribute positions defined in \a material.
+ Issues the GL draw call for the geometry \a g using the material \a shader.
+
+ The function assumes that attributes have been bound and set up prior
+ to making this call.
\internal
*/
-void QSGRenderer::bindGeometry(QSGMaterialShader *material, const QSGGeometry *g)
+void QSGRenderer::draw(const QSGMaterialShader *shader, const QSGGeometry *g)
{
- char const *const *attrNames = material->attributeNames();
+ // ### remove before final release...
+ static bool use_vbo = !QApplication::arguments().contains("--no-vbo");
+
+ const void *vertexData;
+ int vertexByteSize = g->vertexCount() * g->stride();
+ if (use_vbo && g->vertexDataPattern() != QSGGeometry::AlwaysUploadPattern && vertexByteSize > 1024) {
+
+ // The base pointer for a VBO is 0
+ vertexData = 0;
+
+ bool updateData = QSGGeometryData::hasDirtyVertexData(g);
+ QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
+ if (!gd->vertexBuffer) {
+ glGenBuffers(1, &gd->vertexBuffer);
+ updateData = true;
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, gd->vertexBuffer);
+ m_vertex_buffer_bound = true;
+
+ if (updateData) {
+ glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(),
+ qt_drawTypeForPattern(g->vertexDataPattern()));
+ QSGGeometryData::clearDirtyVertexData(g);
+ }
+
+ } else {
+ if (m_vertex_buffer_bound) {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_vertex_buffer_bound = false;
+ }
+ vertexData = g->vertexData();
+ }
+
+ // Bind the vertices to attributes...
+ char const *const *attrNames = shader->attributeNames();
int offset = 0;
for (int j = 0; attrNames[j]; ++j) {
if (!*attrNames[j])
@@ -548,15 +628,62 @@ void QSGRenderer::bindGeometry(QSGMaterialShader *material, const QSGGeometry *g
Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material");
const QSGGeometry::Attribute &a = g->attributes()[j];
Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions");
+
#if defined(QT_OPENGL_ES_2)
GLboolean normalize = a.type != GL_FLOAT;
#else
GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
#endif
- glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->stride(), (char *) g->vertexData() + offset);
+ glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->stride(), (char *) vertexData + offset);
offset += a.tupleSize * size_of_type(a.type);
}
+
+ // Set up the indices...
+ const void *indexData;
+ if (use_vbo && g->indexDataPattern() != QSGGeometry::AlwaysUploadPattern && g->indexCount() > 512) {
+
+ // Base pointer for a VBO is 0
+ indexData = 0;
+
+ bool updateData = QSGGeometryData::hasDirtyIndexData(g);
+ QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g);
+ if (!gd->indexBuffer) {
+ glGenBuffers(1, &gd->indexBuffer);
+ updateData = true;
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gd->indexBuffer);
+ m_index_buffer_bound = true;
+
+ if (updateData) {
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ g->indexCount() * (g->indexType() == GL_UNSIGNED_SHORT ? 2 : 4),
+ g->indexData(),
+ qt_drawTypeForPattern(g->indexDataPattern()));
+ QSGGeometryData::clearDirtyIndexData(g);
+ }
+
+ } else {
+ if (m_index_buffer_bound) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ m_index_buffer_bound = false;
+ }
+ indexData = g->indexData();
+ }
+
+
+ // draw the stuff...
+ if (g->indexCount()) {
+ glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), indexData);
+ } else {
+ glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ }
+
+ // We leave buffers bound for now... They will be reset by bind on next draw() or
+ // set back to 0 if next draw is not using VBOs
+
}
+
QT_END_NAMESPACE
diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer_p.h b/src/declarative/scenegraph/coreapi/qsgrenderer_p.h
index 3fdcf06a08..842286b47c 100644
--- a/src/declarative/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/declarative/scenegraph/coreapi/qsgrenderer_p.h
@@ -137,8 +137,7 @@ signals:
void sceneGraphChanged(); // Add, remove, ChangeFlags changes...
protected:
- void draw(const QSGBasicGeometryNode *geometry);
- void bindGeometry(QSGMaterialShader *material, const QSGGeometry *g);
+ void draw(const QSGMaterialShader *material, const QSGGeometry *g);
virtual void render() = 0;
QSGRenderer::ClipType updateStencilClip(const QSGClipNode *clip);
@@ -174,9 +173,12 @@ private:
const QSGBindable *m_bindable;
- bool m_changed_emitted : 1;
- bool m_mirrored : 1;
- bool m_is_rendering : 1;
+ uint m_changed_emitted : 1;
+ uint m_mirrored : 1;
+ uint m_is_rendering : 1;
+
+ uint m_vertex_buffer_bound : 1;
+ uint m_index_buffer_bound : 1;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode)
diff --git a/src/declarative/scenegraph/scenegraph.pri b/src/declarative/scenegraph/scenegraph.pri
index aa9d2bc6b2..6fea47be05 100644
--- a/src/declarative/scenegraph/scenegraph.pri
+++ b/src/declarative/scenegraph/scenegraph.pri
@@ -11,7 +11,9 @@ HEADERS += \
$$PWD/coreapi/qsgmaterial.h \
$$PWD/coreapi/qsgnode.h \
$$PWD/coreapi/qsgnodeupdater_p.h \
- $$PWD/coreapi/qsgrenderer_p.h
+ $$PWD/coreapi/qsgrenderer_p.h \
+ $$PWD/coreapi/qsggeometry_p.h
+
SOURCES += \
$$PWD/coreapi/qsgdefaultrenderer.cpp \
$$PWD/coreapi/qsggeometry.cpp \