****************************************************************************/
#include "qsggeometry.h"
+#include "qsggeometry_p.h"
QT_BEGIN_NAMESPACE
, 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);
{
if (m_owns_data)
qFree(m_data);
+
+ if (m_server_data)
+ delete m_server_data;
}
/*!
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();
+ }
+
}
/*!
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
#include "qsgnode.h"
#include "qsgmaterial.h"
#include "qsgnodeupdater_p.h"
+#include "qsggeometry_p.h"
#include "private/qsgadaptationlayer_p.h"
, m_changed_emitted(false)
, m_mirrored(false)
, m_is_rendering(false)
+ , m_vertex_buffer_bound(false)
+ , m_index_buffer_bound(false)
{
initializeGLFunctions();
}
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,
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;
}
}
-/*!
- 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)
{
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])
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