diff options
-rw-r--r-- | src/opengl/opengl.pro | 2 | ||||
-rw-r--r-- | src/opengl/qglbuffer.cpp | 454 | ||||
-rw-r--r-- | src/opengl/qglbuffer.h | 124 | ||||
-rw-r--r-- | src/opengl/qglextensions.cpp | 26 | ||||
-rw-r--r-- | src/opengl/qglextensions_p.h | 30 | ||||
-rw-r--r-- | tests/auto/auto.pro | 2 | ||||
-rw-r--r-- | tests/auto/qglbuffer/qglbuffer.pro | 9 | ||||
-rw-r--r-- | tests/auto/qglbuffer/tst_qglbuffer.cpp | 261 |
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" |