summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Continuous Integration System <qt-info@nokia.com>2010-02-01 00:59:42 (GMT)
committerQt Continuous Integration System <qt-info@nokia.com>2010-02-01 00:59:42 (GMT)
commit8a2f4988afa8af09e4e8ed39d76c8eac0ba30390 (patch)
tree4dcb1b742145326f97b2f39ba9a295bf390ea9ef
parentac6bd40b501c63941a4dfc4c1ba500b0c14f74de (diff)
parent2f3bf7b546186b9415f2d0b97ae431fea1a2cc48 (diff)
downloadQt-8a2f4988afa8af09e4e8ed39d76c8eac0ba30390.zip
Qt-8a2f4988afa8af09e4e8ed39d76c8eac0ba30390.tar.gz
Qt-8a2f4988afa8af09e4e8ed39d76c8eac0ba30390.tar.bz2
Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-2: Add the QGLBuffer class to Qt 4.7 for VBO's
-rw-r--r--src/opengl/opengl.pro2
-rw-r--r--src/opengl/qglbuffer.cpp454
-rw-r--r--src/opengl/qglbuffer.h124
-rw-r--r--src/opengl/qglextensions.cpp26
-rw-r--r--src/opengl/qglextensions_p.h30
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/qglbuffer/qglbuffer.pro9
-rw-r--r--tests/auto/qglbuffer/tst_qglbuffer.cpp261
8 files changed, 892 insertions, 16 deletions
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
index 69cfd30..7338534 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -26,6 +26,7 @@ HEADERS += qgl.h \
qglframebufferobject_p.h \
qglextensions_p.h \
qglpaintdevice_p.h \
+ qglbuffer.h \
SOURCES += qgl.cpp \
@@ -34,6 +35,7 @@ SOURCES += qgl.cpp \
qglframebufferobject.cpp \
qglextensions.cpp \
qglpaintdevice.cpp \
+ qglbuffer.cpp \
!contains(QT_CONFIG, opengles2) {
diff --git a/src/opengl/qglbuffer.cpp b/src/opengl/qglbuffer.cpp
new file mode 100644
index 0000000..2018086
--- /dev/null
+++ b/src/opengl/qglbuffer.cpp
@@ -0,0 +1,454 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/private/qgl_p.h>
+#include <QtOpenGL/private/qglextensions_p.h>
+#include "qglbuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLBuffer
+ \brief The QGLBuffer class provides functions for creating and managing GL buffer objects.
+ \since 4.7
+ \ingroup painting-3D
+
+ Buffer objects are created in the GL server so that the
+ client application can avoid uploading vertices, indices,
+ texture image data, etc every time they are needed.
+*/
+
+/*!
+ \enum QGLBuffer::Type
+ This enum defines the type of GL buffer object to create with QGLBuffer.
+
+ \value VertexBuffer Vertex buffer object for use when specifying
+ vertex arrays.
+ \value IndexBuffer Index buffer object for use with \c{glDrawElements()}.
+ \value PixelPackBuffer Pixel pack buffer object for reading pixel
+ data from the GL server (for example, with \c{glReadPixels()}).
+ Not supported under OpenGL/ES.
+ \value PixelUnpackBuffer Pixel unpack buffer object for writing pixel
+ data to the GL server (for example, with \c{glTexImage2D()}).
+ Not supported under OpenGL/ES.
+*/
+
+/*!
+ \enum QGLBuffer::UsagePattern
+ This enum defines the usage pattern of a QGLBuffer object.
+
+ \value StreamDraw The data will be set once and used a few times
+ for drawing operations. Under OpenGL/ES 1.1 this is identical
+ to StaticDraw.
+ \value StreamRead The data will be set once and used a few times
+ for reading data back from the GL server. Not supported
+ under OpenGL/ES.
+ \value StreamCopy The data will be set once and used a few times
+ for reading data back from the GL server for use in further
+ drawing operations. Not supported under OpenGL/ES.
+ \value StaticDraw The data will be set once and used many times
+ for drawing operations.
+ \value StaticRead The data will be set once and used many times
+ for reading data back from the GL server. Not supported
+ under OpenGL/ES.
+ \value StaticCopy The data will be set once and used many times
+ for reading data back from the GL server for use in further
+ drawing operations. Not supported under OpenGL/ES.
+ \value DynamicDraw The data will be modified repeatedly and used
+ many times for drawing operations.
+ \value DynamicRead The data will be modified repeatedly and used
+ many times for reading data back from the GL server.
+ Not supported under OpenGL/ES.
+ \value DynamicCopy The data will be modified repeatedly and used
+ many times for reading data back from the GL server for
+ use in further drawing operations. Not supported under OpenGL/ES.
+*/
+
+/*!
+ \enum QGLBuffer::Access
+ This enum defines the access mode for QGLBuffer::map().
+
+ \value ReadOnly The buffer will be mapped for reading only.
+ \value WriteOnly The buffer will be mapped for writing only.
+ \value ReadWrite The buffer will be mapped for reading and writing.
+*/
+
+class QGLBufferPrivate
+{
+public:
+ QGLBufferPrivate(QGLBuffer::Type t)
+ : type(t),
+ guard(0),
+ usagePattern(QGLBuffer::StaticDraw),
+ actualUsagePattern(QGLBuffer::StaticDraw)
+ {
+ }
+
+ QGLBuffer::Type type;
+ QGLSharedResourceGuard guard;
+ QGLBuffer::UsagePattern usagePattern;
+ QGLBuffer::UsagePattern actualUsagePattern;
+};
+
+/*!
+ Constructs a new buffer object of \a type.
+
+ Note: this constructor just creates the QGLBuffer instance. The actual
+ buffer object in the GL server is not created until create() is called.
+
+ \sa create()
+*/
+QGLBuffer::QGLBuffer(QGLBuffer::Type type)
+ : d_ptr(new QGLBufferPrivate(type))
+{
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Destroys this buffer object, including the storage being
+ used in the GL server.
+*/
+QGLBuffer::~QGLBuffer()
+{
+ Q_D(QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ // Switch to the original creating context to destroy it.
+ QGLShareContextScope scope(d->guard.context());
+ glDeleteBuffers(1, &bufferId);
+ }
+}
+
+/*!
+ Returns the type of buffer represented by this object.
+*/
+QGLBuffer::Type QGLBuffer::type() const
+{
+ Q_D(const QGLBuffer);
+ return d->type;
+}
+
+/*!
+ Returns the usage pattern for this buffer object.
+ The default value is StaticDraw.
+
+ \sa setUsagePattern()
+*/
+QGLBuffer::UsagePattern QGLBuffer::usagePattern() const
+{
+ Q_D(const QGLBuffer);
+ return d->usagePattern;
+}
+
+/*!
+ Sets the usage pattern for this buffer object to \a value.
+ This function must be called before allocate() or write().
+
+ \sa usagePattern(), allocate(), write()
+*/
+void QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value)
+{
+ Q_D(QGLBuffer);
+#if defined(QT_OPENGL_ES_1)
+ // OpenGL/ES 1.1 does not support GL_STREAM_DRAW, so use GL_STATIC_DRAW.
+ // OpenGL/ES 2.0 does support GL_STREAM_DRAW.
+ d->usagePattern = value;
+ if (value == StreamDraw)
+ d->actualUsagePattern = StaticDraw;
+ else
+ d->actualUsagePattern = value;
+#else
+ d->usagePattern = d->actualUsagePattern = value;
+#endif
+}
+
+#undef ctx
+
+/*!
+ Creates the buffer object in the GL server. Returns true if
+ the object was created; false otherwise.
+
+ This function must be called with a current QGLContext.
+ The buffer will be bound to and can only be used in
+ that context (or any other context that is shared with it).
+
+ This function will return false if the GL implementation
+ does not support buffers, or there is no current QGLContext.
+
+ \sa isCreated(), allocate(), write()
+*/
+bool QGLBuffer::create()
+{
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ return true;
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx) {
+ if (!qt_resolve_buffer_extensions(const_cast<QGLContext *>(ctx)))
+ return false;
+ GLuint bufferId = 0;
+ glGenBuffers(1, &bufferId);
+ if (bufferId) {
+ d->guard.setContext(ctx);
+ d->guard.setId(bufferId);
+ return true;
+ }
+ }
+ return false;
+}
+
+#define ctx d->guard.context()
+
+/*!
+ Returns true if this buffer has been created; false otherwise.
+
+ \sa create()
+*/
+bool QGLBuffer::isCreated() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id() != 0;
+}
+
+/*!
+ Reads the \a size bytes in this buffer starting at \a offset
+ into \a data. Returns true on success; false if reading from
+ the buffer is not supported. Buffer reading is not supported
+ under OpenGL/ES.
+
+ It is assumed that this buffer has been bound to the current context.
+
+ \sa write(), bind()
+*/
+bool QGLBuffer::read(int offset, void *data, int size)
+{
+#if !defined(QT_OPENGL_ES)
+ Q_D(QGLBuffer);
+ if (!glGetBufferSubData || !d->guard.id())
+ return false;
+ while (glGetError() != GL_NO_ERROR) ; // Clear error state.
+ glGetBufferSubData(d->type, offset, size, data);
+ return glGetError() == GL_NO_ERROR;
+#else
+ Q_UNUSED(offset);
+ Q_UNUSED(data);
+ Q_UNUSED(size);
+ return false;
+#endif
+}
+
+/*!
+ Replaces the \a size bytes of this buffer starting at \a offset
+ with the contents of \a data. Any other bytes in the buffer
+ will be left unmodified.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), read(), allocate()
+*/
+void QGLBuffer::write(int offset, const void *data, int size)
+{
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ glBufferSubData(d->type, offset, size, data);
+}
+
+/*!
+ Allocates \a size bytes of space to the buffer, initialized to
+ the contents of \a data. Any previous contents will be removed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), read(), write()
+*/
+void QGLBuffer::allocate(const void *data, int size)
+{
+ Q_D(QGLBuffer);
+ if (d->guard.id())
+ glBufferData(d->type, size, data, d->actualUsagePattern);
+}
+
+/*!
+ \fn void QGLBuffer::allocate(int size)
+ \overload
+
+ Allocates \a size bytes of space to the buffer. Any previous
+ contents will be removed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ \sa create(), write()
+*/
+
+/*!
+ Binds the buffer associated with this object to the current
+ GL context. Returns false if binding was not possible, usually because
+ type() is not supported on this GL implementation.
+
+ The buffer must be bound to the same QGLContext current when create()
+ was called, or to another QGLContext that is sharing with it.
+ Otherwise, false will be returned from this function.
+
+ \sa release(), create()
+*/
+bool QGLBuffer::bind() const
+{
+ Q_D(const QGLBuffer);
+ GLuint bufferId = d->guard.id();
+ if (bufferId) {
+ if (!QGLContext::areSharing(QGLContext::currentContext(),
+ d->guard.context())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QGLBuffer::bind: buffer is not valid in the current context");
+#endif
+ return false;
+ }
+ glBindBuffer(d->type, bufferId);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*!
+ Releases the buffer associated with this object from the
+ current GL context.
+
+ This function must be called with the same QGLContext current
+ as when bind() was called on the buffer.
+
+ \sa bind()
+*/
+void QGLBuffer::release() const
+{
+ Q_D(const QGLBuffer);
+ if (d->guard.id())
+ glBindBuffer(d->type, 0);
+}
+
+/*!
+ Returns the GL identifier associated with this buffer; zero if
+ the buffer has not been created.
+
+ \sa isCreated()
+*/
+uint QGLBuffer::bufferId() const
+{
+ Q_D(const QGLBuffer);
+ return d->guard.id();
+}
+
+#ifndef GL_BUFFER_SIZE
+#define GL_BUFFER_SIZE 0x8764
+#endif
+
+/*!
+ Returns the size of the data in this buffer, for reading operations.
+ Returns -1 if fetching the buffer size is not supported, or the
+ buffer has not been created.
+
+ It is assumed that this buffer has been bound to the current context.
+
+ \sa isCreated(), bind()
+*/
+int QGLBuffer::size() const
+{
+ Q_D(const QGLBuffer);
+ if (!d->guard.id())
+ return -1;
+ GLint value = -1;
+ glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value);
+ return value;
+}
+
+/*!
+ Maps the contents of this buffer into the application's memory
+ space and returns a pointer to it. Returns null if memory
+ mapping is not possible. The \a access parameter indicates the
+ type of access to be performed.
+
+ It is assumed that create() has been called on this buffer and that
+ it has been bound to the current context.
+
+ This function is only supported under OpenGL/ES if the
+ \c{GL_OES_mapbuffer} extension is present.
+
+ \sa unmap(), create(), bind()
+*/
+void *QGLBuffer::map(QGLBuffer::Access access)
+{
+ Q_D(QGLBuffer);
+ if (!d->guard.id())
+ return 0;
+ if (!glMapBufferARB)
+ return 0;
+ return glMapBufferARB(d->type, access);
+}
+
+/*!
+ Unmaps the buffer after it was mapped into the application's
+ memory space with a previous call to map(). Returns true if
+ the unmap succeeded; false otherwise.
+
+ It is assumed that this buffer has been bound to the current context,
+ and that it was previously mapped with map().
+
+ This function is only supported under OpenGL/ES if the
+ \c{GL_OES_mapbuffer} extension is present.
+
+ \sa map()
+*/
+bool QGLBuffer::unmap()
+{
+ Q_D(QGLBuffer);
+ if (!d->guard.id())
+ return false;
+ if (!glUnmapBufferARB)
+ return false;
+ return glUnmapBufferARB(d->type) == GL_TRUE;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglbuffer.h b/src/opengl/qglbuffer.h
new file mode 100644
index 0000000..68ee56c
--- /dev/null
+++ b/src/opengl/qglbuffer.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLBUFFER_H
+#define QGLBUFFER_H
+
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLBufferPrivate;
+
+class Q_OPENGL_EXPORT QGLBuffer
+{
+public:
+ enum Type
+ {
+ VertexBuffer = 0x8892, // GL_ARRAY_BUFFER
+ IndexBuffer = 0x8893, // GL_ELEMENT_ARRAY_BUFFER
+ PixelPackBuffer = 0x88EB, // GL_PIXEL_PACK_BUFFER
+ PixelUnpackBuffer = 0x88EC // GL_PIXEL_UNPACK_BUFFER
+ };
+
+ explicit QGLBuffer(QGLBuffer::Type type);
+ ~QGLBuffer();
+
+ enum UsagePattern
+ {
+ StreamDraw = 0x88E0, // GL_STREAM_DRAW
+ StreamRead = 0x88E1, // GL_STREAM_READ
+ StreamCopy = 0x88E2, // GL_STREAM_COPY
+ StaticDraw = 0x88E4, // GL_STATIC_DRAW
+ StaticRead = 0x88E5, // GL_STATIC_READ
+ StaticCopy = 0x88E6, // GL_STATIC_COPY
+ DynamicDraw = 0x88E8, // GL_DYNAMIC_DRAW
+ DynamicRead = 0x88E9, // GL_DYNAMIC_READ
+ DynamicCopy = 0x88EA // GL_DYNAMIC_COPY
+ };
+
+ enum Access
+ {
+ ReadOnly = 0x88B8, // GL_READ_ONLY
+ WriteOnly = 0x88B9, // GL_WRITE_ONLY
+ ReadWrite = 0x88BA // GL_READ_WRITE
+ };
+
+ QGLBuffer::Type type() const;
+
+ QGLBuffer::UsagePattern usagePattern() const;
+ void setUsagePattern(QGLBuffer::UsagePattern value);
+
+ bool create();
+ bool isCreated() const;
+
+ bool bind() const;
+ void release() const;
+
+ uint bufferId() const;
+
+ int size() const;
+
+ bool read(int offset, void *data, int size);
+ void write(int offset, const void *data, int size);
+
+ void allocate(const void *data, int size);
+ inline void allocate(int size) { allocate(0, size); }
+
+ void *map(QGLBuffer::Access access);
+ bool unmap();
+
+private:
+ QScopedPointer<QGLBufferPrivate> d_ptr;
+
+ Q_DISABLE_COPY(QGLBuffer)
+ Q_DECLARE_PRIVATE(QGLBuffer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp
index c091191..03fca39 100644
--- a/src/opengl/qglextensions.cpp
+++ b/src/opengl/qglextensions.cpp
@@ -191,31 +191,35 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx)
bool qt_resolve_buffer_extensions(QGLContext *ctx)
{
- if (glMapBufferARB && glUnmapBufferARB
-#if !defined(QT_OPENGL_ES_2)
- && glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData
-#endif
- )
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ if (glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData
+ && glBufferSubData && glGetBufferParameteriv)
return true;
+#endif
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
glBindBuffer = (_glBindBuffer) qt_gl_getProcAddressARB(ctx, "glBindBuffer");
glDeleteBuffers = (_glDeleteBuffers) qt_gl_getProcAddressARB(ctx, "glDeleteBuffers");
glGenBuffers = (_glGenBuffers) qt_gl_getProcAddressARB(ctx, "glGenBuffers");
glBufferData = (_glBufferData) qt_gl_getProcAddressARB(ctx, "glBufferData");
+ glBufferSubData = (_glBufferSubData) qt_gl_getProcAddressARB(ctx, "glBufferSubData");
+ glGetBufferSubData = (_glGetBufferSubData) qt_gl_getProcAddressARB(ctx, "glGetBufferSubData");
+ glGetBufferParameteriv = (_glGetBufferParameteriv) qt_gl_getProcAddressARB(ctx, "glGetBufferParameteriv");
#endif
glMapBufferARB = (_glMapBufferARB) qt_gl_getProcAddressARB(ctx, "glMapBuffer");
glUnmapBufferARB = (_glUnmapBufferARB) qt_gl_getProcAddressARB(ctx, "glUnmapBuffer");
- return glMapBufferARB
- && glUnmapBufferARB
-#if !defined(QT_OPENGL_ES_2)
- && glBindBuffer
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
+ return glBindBuffer
&& glDeleteBuffers
&& glGenBuffers
&& glBufferData
+ && glBufferSubData
+ && glGetBufferParameteriv;
+ // glGetBufferSubData() is optional
+#else
+ return true;
#endif
- ;
}
bool qt_resolve_glsl_extensions(QGLContext *ctx)
diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
index 86096d2..116dfa6 100644
--- a/src/opengl/qglextensions_p.h
+++ b/src/opengl/qglextensions_p.h
@@ -71,6 +71,7 @@
#include <QtCore/qglobal.h>
#ifndef GL_ARB_vertex_buffer_object
+typedef ptrdiff_t GLintptrARB;
typedef ptrdiff_t GLsizeiptrARB;
#endif
@@ -78,13 +79,25 @@ typedef ptrdiff_t GLsizeiptrARB;
typedef char GLchar;
#endif
-// ARB_pixel_buffer_object
+// ARB_vertex_buffer_object
typedef void (APIENTRY *_glBindBuffer) (GLenum, GLuint);
typedef void (APIENTRY *_glDeleteBuffers) (GLsizei, const GLuint *);
typedef void (APIENTRY *_glGenBuffers) (GLsizei, GLuint *);
typedef void (APIENTRY *_glBufferData) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum);
+typedef void (APIENTRY *_glBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *);
+typedef void (APIENTRY *_glGetBufferSubData) (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *);
+typedef void (APIENTRY *_glGetBufferParameteriv) (GLenum, GLenum, GLint *);
typedef GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum);
typedef GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum);
+// We can call the buffer functions directly in OpenGL/ES 1.1 or higher,
+// but all other platforms need to resolve the extensions.
+#if defined(QT_OPENGL_ES)
+#if defined(GL_OES_VERSION_1_0) && !defined(GL_OES_VERSION_1_1)
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
+#else
+#define QGL_RESOLVE_BUFFER_FUNCS 1
+#endif
// ARB_fragment_program
typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
@@ -285,11 +298,14 @@ struct QGLExtensionFuncs
qt_glRenderbufferStorageMultisampleEXT = 0;
// Buffer objects:
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
qt_glBindBuffer = 0;
qt_glDeleteBuffers = 0;
qt_glGenBuffers = 0;
qt_glBufferData = 0;
+ qt_glBufferSubData = 0;
+ qt_glGetBufferSubData = 0;
+ qt_glGetBufferParameteriv = 0;
#endif
qt_glMapBufferARB = 0;
qt_glUnmapBufferARB = 0;
@@ -397,11 +413,14 @@ struct QGLExtensionFuncs
_glRenderbufferStorageMultisampleEXT qt_glRenderbufferStorageMultisampleEXT;
// Buffer objects
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
_glBindBuffer qt_glBindBuffer;
_glDeleteBuffers qt_glDeleteBuffers;
_glGenBuffers qt_glGenBuffers;
_glBufferData qt_glBufferData;
+ _glBufferSubData qt_glBufferSubData;
+ _glGetBufferSubData qt_glGetBufferSubData;
+ _glGetBufferParameteriv qt_glGetBufferParameteriv;
#endif
_glMapBufferARB qt_glMapBufferARB;
_glUnmapBufferARB qt_glUnmapBufferARB;
@@ -681,11 +700,14 @@ struct QGLExtensionFuncs
// Buffer objects
-#if !defined(QT_OPENGL_ES_2)
+#if defined(QGL_RESOLVE_BUFFER_FUNCS)
#define glBindBuffer QGLContextPrivate::extensionFuncs(ctx).qt_glBindBuffer
#define glDeleteBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glDeleteBuffers
#define glGenBuffers QGLContextPrivate::extensionFuncs(ctx).qt_glGenBuffers
#define glBufferData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferData
+#define glBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glBufferSubData
+#define glGetBufferSubData QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferSubData
+#define glGetBufferParameteriv QGLContextPrivate::extensionFuncs(ctx).qt_glGetBufferParameteriv
#endif
#define glMapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glMapBufferARB
#define glUnmapBufferARB QGLContextPrivate::extensionFuncs(ctx).qt_glUnmapBufferARB
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 3198a65..1abf9b7 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -462,7 +462,7 @@ SUBDIRS += \
rcc \
windowsmobile
-contains(QT_CONFIG,opengl):SUBDIRS += qgl
+contains(QT_CONFIG,opengl):SUBDIRS += qgl qglbuffer
contains(QT_CONFIG,qt3support):!wince*:SUBDIRS += $$Q3SUBDIRS
diff --git a/tests/auto/qglbuffer/qglbuffer.pro b/tests/auto/qglbuffer/qglbuffer.pro
new file mode 100644
index 0000000..07d05bb
--- /dev/null
+++ b/tests/auto/qglbuffer/qglbuffer.pro
@@ -0,0 +1,9 @@
+############################################################
+# Project file for autotest for file qglbuffer.h
+############################################################
+
+load(qttest_p4)
+requires(contains(QT_CONFIG,opengl))
+QT += opengl
+
+SOURCES += tst_qglbuffer.cpp
diff --git a/tests/auto/qglbuffer/tst_qglbuffer.cpp b/tests/auto/qglbuffer/tst_qglbuffer.cpp
new file mode 100644
index 0000000..b7d5821
--- /dev/null
+++ b/tests/auto/qglbuffer/tst_qglbuffer.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtOpenGL/qgl.h>
+#include <QtOpenGL/qglbuffer.h>
+
+class tst_QGLBuffer : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QGLBuffer() {}
+ ~tst_QGLBuffer() {}
+
+private slots:
+ void vertexBuffer_data();
+ void vertexBuffer();
+ void indexBuffer_data();
+ void indexBuffer();
+ void bufferSharing();
+
+private:
+ void testBuffer(QGLBuffer::Type type);
+};
+
+void tst_QGLBuffer::vertexBuffer_data()
+{
+ QTest::addColumn<int>("usagePattern");
+
+ QTest::newRow("StreamDraw") << int(QGLBuffer::StreamDraw);
+ QTest::newRow("StaticDraw") << int(QGLBuffer::StaticDraw);
+ QTest::newRow("DynamicDraw") << int(QGLBuffer::DynamicDraw);
+}
+
+void tst_QGLBuffer::vertexBuffer()
+{
+ testBuffer(QGLBuffer::VertexBuffer);
+}
+
+void tst_QGLBuffer::indexBuffer_data()
+{
+ vertexBuffer_data();
+}
+
+void tst_QGLBuffer::indexBuffer()
+{
+ testBuffer(QGLBuffer::IndexBuffer);
+}
+
+void tst_QGLBuffer::testBuffer(QGLBuffer::Type type)
+{
+ QFETCH(int, usagePattern);
+
+ QGLWidget w;
+ w.makeCurrent();
+
+ // Create the local object, but not the buffer in the server.
+ QGLBuffer buffer(type);
+ QVERIFY(buffer.usagePattern() == QGLBuffer::StaticDraw);
+ buffer.setUsagePattern(QGLBuffer::UsagePattern(usagePattern));
+
+ // Check the initial state.
+ QVERIFY(buffer.type() == type);
+ QVERIFY(!buffer.isCreated());
+ QVERIFY(buffer.bufferId() == 0);
+ QVERIFY(buffer.usagePattern() == QGLBuffer::UsagePattern(usagePattern));
+ QCOMPARE(buffer.size(), -1);
+
+ // Should not be able to bind it yet because it isn't created.
+ QVERIFY(!buffer.bind());
+
+ // Create the buffer - if this fails, then assume that the
+ // GL implementation does not support buffers at all.
+ if (!buffer.create())
+ QSKIP("Buffers are not supported on this platform", SkipAll);
+
+ // Should now have a buffer id.
+ QVERIFY(buffer.bufferId() != 0);
+
+ // Bind the buffer and upload some data.
+ QVERIFY(buffer.bind());
+ static GLfloat const data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ buffer.allocate(data, sizeof(data));
+
+ // Check the buffer size.
+ QCOMPARE(buffer.size(), int(sizeof(data)));
+
+ // Map the buffer and read back its contents.
+ bool haveMap = false;
+ GLfloat *mapped = reinterpret_cast<GLfloat *>
+ (buffer.map(QGLBuffer::ReadOnly));
+ if (mapped) {
+ for (int index = 0; index < 9; ++index)
+ QCOMPARE(mapped[index], data[index]);
+ buffer.unmap();
+ haveMap = true;
+ } else {
+ qWarning("QGLBuffer::map() is not supported on this platform");
+ }
+
+ // Read back the buffer contents using read().
+ bool haveRead = false;
+ GLfloat readdata[9];
+ if (buffer.read(0, readdata, sizeof(readdata))) {
+ for (int index = 0; index < 9; ++index)
+ QCOMPARE(readdata[index], data[index]);
+ haveRead = true;
+ } else {
+ qWarning("QGLBuffer::read() is not supported on this platform");
+ }
+
+ // Write some different data to a specific location and check it.
+ static GLfloat const diffdata[] = {11, 12, 13};
+ buffer.write(sizeof(GLfloat) * 3, diffdata, sizeof(diffdata));
+ if (haveMap) {
+ mapped = reinterpret_cast<GLfloat *>(buffer.map(QGLBuffer::ReadOnly));
+ QVERIFY(mapped != 0);
+ for (int index = 0; index < 9; ++index) {
+ if (index >= 3 && index <= 5)
+ QCOMPARE(mapped[index], diffdata[index - 3]);
+ else
+ QCOMPARE(mapped[index], data[index]);
+ }
+ buffer.unmap();
+ }
+ if (haveRead) {
+ QVERIFY(buffer.read(0, readdata, sizeof(readdata)));
+ for (int index = 0; index < 9; ++index) {
+ if (index >= 3 && index <= 5)
+ QCOMPARE(readdata[index], diffdata[index - 3]);
+ else
+ QCOMPARE(readdata[index], data[index]);
+ }
+ }
+
+ // Write to the buffer using the return value from map.
+ if (haveMap) {
+ mapped = reinterpret_cast<GLfloat *>(buffer.map(QGLBuffer::WriteOnly));
+ QVERIFY(mapped != 0);
+ mapped[6] = 14;
+ buffer.unmap();
+
+ mapped = reinterpret_cast<GLfloat *>(buffer.map(QGLBuffer::ReadOnly));
+ QVERIFY(mapped != 0);
+ static GLfloat const diff2data[] = {11, 12, 13, 14};
+ for (int index = 0; index < 9; ++index) {
+ if (index >= 3 && index <= 6)
+ QCOMPARE(mapped[index], diff2data[index - 3]);
+ else
+ QCOMPARE(mapped[index], data[index]);
+ }
+ buffer.unmap();
+ }
+
+ // Resize the buffer.
+ buffer.allocate(sizeof(GLfloat) * 20);
+ QCOMPARE(buffer.size(), int(sizeof(GLfloat) * 20));
+ buffer.allocate(0, sizeof(GLfloat) * 32);
+ QCOMPARE(buffer.size(), int(sizeof(GLfloat) * 32));
+
+ // Release the buffer.
+ buffer.release();
+}
+
+void tst_QGLBuffer::bufferSharing()
+{
+ QGLWidget *w1 = new QGLWidget();
+ w1->makeCurrent();
+
+ QGLWidget *w2 = new QGLWidget(0, w1);
+ if (!w2->isSharing()) {
+ delete w2;
+ delete w1;
+ QSKIP("Context sharing is not supported on this platform", SkipSingle);
+ }
+
+ // Bind the buffer in the first context and write some data to it.
+ QGLBuffer buffer(QGLBuffer::VertexBuffer);
+ if (!buffer.create())
+ QSKIP("Buffers are not supported on this platform", SkipSingle);
+ QVERIFY(buffer.isCreated());
+ QVERIFY(buffer.bind());
+ static GLfloat const data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ buffer.allocate(data, sizeof(data));
+ QCOMPARE(buffer.size(), int(sizeof(data)));
+ buffer.release();
+
+ // Bind the buffer in the second context and read back the data.
+ w2->makeCurrent();
+ QVERIFY(buffer.bind());
+ QCOMPARE(buffer.size(), int(sizeof(data)));
+ GLfloat readdata[9];
+ if (buffer.read(0, readdata, sizeof(readdata))) {
+ for (int index = 0; index < 9; ++index)
+ QCOMPARE(readdata[index], data[index]);
+ }
+ buffer.release();
+
+ // Delete the first context.
+ delete w1;
+
+ // Make the second context current again because deleting the first
+ // one will call doneCurrent() even though it wasn't current!
+ w2->makeCurrent();
+
+ // The buffer should still be valid in the second context.
+ QVERIFY(buffer.bufferId() != 0);
+ QVERIFY(buffer.isCreated());
+ QVERIFY(buffer.bind());
+ QCOMPARE(buffer.size(), int(sizeof(data)));
+ buffer.release();
+
+ // Delete the second context.
+ delete w2;
+
+ // The buffer should now be invalid.
+ QVERIFY(buffer.bufferId() == 0);
+ QVERIFY(!buffer.isCreated());
+}
+
+QTEST_MAIN(tst_QGLBuffer)
+
+#include "tst_qglbuffer.moc"