Add support for glMapBufferRange in the wrappers and resolvers

QOpenGLBuffer::map() and related helpers are becoming useless in
OpenGL ES 3.0 and up: instead of the old GL_OES_map_buffer,
glMapBufferRange, but not glMapBuffer, is now part of the standard.
On desktop GL_ARB_map_buffer_range is present by default in OpenGL 3.0
and newer.

[ChangeLog][QtGui] Added QOpenGLBuffer::mapBufferRange().

Task-number: QTBUG-38168
Change-Id: I4e9bbe8ced9ee4d535ac32849a8c08c26d79cb49
Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
This commit is contained in:
Laszlo Agocs 2014-06-13 16:36:27 +02:00
parent 22e4391413
commit bb760d9514
6 changed files with 191 additions and 9 deletions

View File

@ -128,6 +128,18 @@ QT_BEGIN_NAMESPACE
\value ReadWrite The buffer will be mapped for reading and writing.
*/
/*!
\enum QOpenGLBuffer::RangeAccessFlag
This enum defines the access mode bits for QOpenGLBuffer::mapRange().
\value RangeRead The buffer will be mapped for reading.
\value RangeWrite The buffer will be mapped for writing.
\value RangeInvalidate Discard the previous contents of the specified range.
\value RangeInvalidateBuffer Discard the previous contents of the entire buffer.
\value RangeFlushExplicit Indicates that modifications are to be flushed explicitly via \c glFlushMappedBufferRange.
\value RangeUnsynchronized Indicates that pending operations should not be synchronized before returning from mapRange().
*/
class QOpenGLBufferPrivate
{
public:
@ -514,10 +526,14 @@ int QOpenGLBuffer::size() const
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.
\note This function is only supported under OpenGL ES 2.0 or
earlier if the \c GL_OES_mapbuffer extension is present.
\sa unmap(), create(), bind()
\note On OpenGL ES 3.0 and newer, or, in case if desktop OpenGL,
if \c GL_ARB_map_buffer_range is supported, this function uses
\c glMapBufferRange instead of \c glMapBuffer.
\sa unmap(), create(), bind(), mapRange()
*/
void *QOpenGLBuffer::map(QOpenGLBuffer::Access access)
{
@ -528,7 +544,48 @@ void *QOpenGLBuffer::map(QOpenGLBuffer::Access access)
#endif
if (!d->guard || !d->guard->id())
return 0;
return d->funcs->glMapBuffer(d->type, access);
if (d->funcs->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange)) {
QOpenGLBuffer::RangeAccessFlags rangeAccess = 0;
switch (access) {
case QOpenGLBuffer::ReadOnly:
rangeAccess = QOpenGLBuffer::RangeRead;
break;
case QOpenGLBuffer::WriteOnly:
rangeAccess = QOpenGLBuffer::RangeWrite;
break;
case QOpenGLBuffer::ReadWrite:
rangeAccess = QOpenGLBuffer::RangeRead | QOpenGLBuffer::RangeWrite;
break;
}
return d->funcs->glMapBufferRange(d->type, 0, size(), rangeAccess);
} else {
return d->funcs->glMapBuffer(d->type, access);
}
}
/*!
Maps the range specified by \a offset and \a count of 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 specifies a combination of access flags.
It is assumed that create() has been called on this buffer and that
it has been bound to the current context.
\note This function is not available on OpenGL ES 2.0 and earlier.
\sa unmap(), create(), bind()
*/
void *QOpenGLBuffer::mapRange(int offset, int count, QOpenGLBuffer::RangeAccessFlags access)
{
Q_D(QOpenGLBuffer);
#ifndef QT_NO_DEBUG
if (!isCreated())
qWarning("QOpenGLBuffer::mapRange(): buffer not created");
#endif
if (!d->guard || !d->guard->id())
return 0;
return d->funcs->glMapBufferRange(d->type, offset, count, access);
}
/*!
@ -539,8 +596,8 @@ void *QOpenGLBuffer::map(QOpenGLBuffer::Access access)
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.
\note This function is only supported under OpenGL ES 2.0 and
earlier if the \c{GL_OES_mapbuffer} extension is present.
\sa map()
*/

View File

@ -92,6 +92,17 @@ public:
ReadWrite = 0x88BA // GL_READ_WRITE
};
enum RangeAccessFlag
{
RangeRead = 0x0001, // GL_MAP_READ_BIT
RangeWrite = 0x0002, // GL_MAP_WRITE_BIT
RangeInvalidate = 0x0004, // GL_MAP_INVALIDATE_RANGE_BIT
RangeInvalidateBuffer = 0x0008, // GL_MAP_INVALIDATE_BUFFER_BIT
RangeFlushExplicit = 0x0010, // GL_MAP_FLUSH_EXPLICIT_BIT
RangeUnsynchronized = 0x0020 // GL_MAP_UNSYNCHRONIZED_BIT
};
Q_DECLARE_FLAGS(RangeAccessFlags, RangeAccessFlag)
QOpenGLBuffer::Type type() const;
QOpenGLBuffer::UsagePattern usagePattern() const;
@ -118,6 +129,7 @@ public:
inline void allocate(int count) { allocate(0, count); }
void *map(QOpenGLBuffer::Access access);
void *mapRange(int offset, int count, QOpenGLBuffer::RangeAccessFlags access);
bool unmap();
private:
@ -126,6 +138,8 @@ private:
Q_DECLARE_PRIVATE(QOpenGLBuffer)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLBuffer::RangeAccessFlags)
QT_END_NAMESPACE
#endif // QT_NO_OPENGL

View File

@ -99,7 +99,8 @@ public:
Depth24 = 0x00010000,
SRGBFrameBuffer = 0x00020000,
MapBuffer = 0x00040000,
GeometryShaders = 0x00080000
GeometryShaders = 0x00080000,
MapBufferRange = 0x00100000
};
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
@ -107,6 +108,7 @@ public:
bool hasOpenGLExtension(QOpenGLExtensions::OpenGLExtension extension) const;
GLvoid *glMapBuffer(GLenum target, GLenum access);
GLvoid *glMapBufferRange(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr length, GLbitfield access);
GLboolean glUnmapBuffer(GLenum target);
void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
@ -131,6 +133,8 @@ public:
explicit QOpenGLExtensionsPrivate(QOpenGLContext *ctx);
GLvoid* (QOPENGLF_APIENTRYP MapBuffer)(GLenum target, GLenum access);
GLvoid* (QOPENGLF_APIENTRYP MapBufferRange)(GLenum target, qopengl_GLintptr offset,
qopengl_GLsizeiptr length, GLbitfield access);
GLboolean (QOPENGLF_APIENTRYP UnmapBuffer)(GLenum target);
void (QOPENGLF_APIENTRYP BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
@ -150,6 +154,16 @@ inline GLvoid *QOpenGLExtensions::glMapBuffer(GLenum target, GLenum access)
return result;
}
inline GLvoid *QOpenGLExtensions::glMapBufferRange(GLenum target, qopengl_GLintptr offset,
qopengl_GLsizeiptr length, GLbitfield access)
{
Q_D(QOpenGLExtensions);
Q_ASSERT(QOpenGLExtensions::isInitialized(d));
GLvoid *result = d->MapBufferRange(target, offset, length, access);
Q_OPENGL_FUNCTIONS_DEBUG
return result;
}
inline GLboolean QOpenGLExtensions::glUnmapBuffer(GLenum target)
{
Q_D(QOpenGLExtensions);

View File

@ -388,7 +388,8 @@ static int qt_gl_resolve_extensions()
if (format.majorVersion() >= 3)
extensions |= QOpenGLExtensions::PackedDepthStencil
| QOpenGLExtensions::Depth24
| QOpenGLExtensions::ElementIndexUint;
| QOpenGLExtensions::ElementIndexUint
| QOpenGLExtensions::MapBufferRange;
if (extensionMatcher.match("GL_OES_mapbuffer"))
extensions |= QOpenGLExtensions::MapBuffer;
@ -429,6 +430,8 @@ static int qt_gl_resolve_extensions()
if (extensionMatcher.match("GL_EXT_packed_depth_stencil"))
extensions |= QOpenGLExtensions::PackedDepthStencil;
}
if (extensionMatcher.match("GL_ARB_map_buffer_range"))
extensions |= QOpenGLExtensions::MapBufferRange;
}
if (format.renderableType() == QSurfaceFormat::OpenGL && format.version() >= qMakePair(3, 2))
@ -3182,11 +3185,36 @@ static void QOPENGLF_APIENTRY qopenglfResolveVertexAttribPointer(GLuint indx, GL
static GLvoid *QOPENGLF_APIENTRY qopenglfResolveMapBuffer(GLenum target, GLenum access)
{
#ifdef QT_OPENGL_ES_3
// It is possible that GL_OES_map_buffer is present, but then having to
// differentiate between glUnmapBufferOES and glUnmapBuffer causes extra
// headache. QOpenGLBuffer::map() will handle this automatically, while direct
// calls are better off with migrating to the standard glMapBufferRange.
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3) {
qWarning("QOpenGLFunctions: glMapBuffer is not available in OpenGL ES 3.0 and up. Use glMapBufferRange instead.");
return 0;
} else
#endif
RESOLVE_FUNC(GLvoid *, ResolveOES, MapBuffer)(target, access);
}
static GLvoid *QOPENGLF_APIENTRY qopenglfResolveMapBufferRange(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr length, GLbitfield access)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
return ::glMapBufferRange(target, offset, length, access);
else
#endif
RESOLVE_FUNC(GLvoid *, 0, MapBufferRange)(target, offset, length, access);
}
static GLboolean QOPENGLF_APIENTRY qopenglfResolveUnmapBuffer(GLenum target)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
return ::glUnmapBuffer(target);
else
#endif
RESOLVE_FUNC(GLboolean, ResolveOES, UnmapBuffer)(target);
}
@ -3455,6 +3483,7 @@ QOpenGLExtensionsPrivate::QOpenGLExtensionsPrivate(QOpenGLContext *ctx)
: QOpenGLFunctionsPrivate(ctx)
{
MapBuffer = qopenglfResolveMapBuffer;
MapBufferRange = qopenglfResolveMapBufferRange;
UnmapBuffer = qopenglfResolveUnmapBuffer;
BlitFramebuffer = qopenglfResolveBlitFramebuffer;
RenderbufferStorageMultisample = qopenglfResolveRenderbufferStorageMultisample;

View File

@ -11,4 +11,4 @@ SOURCES += tst_qopengl.cpp
win32-msvc2010:contains(QT_CONFIG, angle):CONFIG += insignificant_test # QTBUG-31611
linux:contains(QT_CONFIG, xcb-glx):contains(QT_CONFIG, xcb-xlib):!contains(QT_CONFIG, opengles2): DEFINES += USE_GLX
linux:contains(QT_CONFIG, xcb-glx):contains(QT_CONFIG, xcb-xlib):!contains(QT_CONFIG, egl): DEFINES += USE_GLX

View File

@ -45,6 +45,7 @@
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLFunctions_4_2_Core>
#include <QtGui/QOpenGLVertexArrayObject>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QOpenGLPaintDevice>
#include <QtGui/QPainter>
#include <QtGui/QScreen>
@ -54,6 +55,7 @@
#include <QtGui/QMatrix4x4>
#include <QtGui/private/qopengltextureblitter_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qopenglextensions_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
@ -113,6 +115,8 @@ private slots:
#endif
void vaoCreate();
void bufferCreate();
void bufferMapRange();
};
struct SharedResourceTracker
@ -1243,6 +1247,70 @@ void tst_QOpenGL::vaoCreate()
ctx->doneCurrent();
}
void tst_QOpenGL::bufferCreate()
{
QScopedPointer<QSurface> surface(createSurface(QSurface::Window));
QOpenGLContext *ctx = new QOpenGLContext;
ctx->create();
ctx->makeCurrent(surface.data());
QOpenGLBuffer buf;
QVERIFY(!buf.isCreated());
QVERIFY(buf.create());
QVERIFY(buf.isCreated());
QCOMPARE(buf.type(), QOpenGLBuffer::VertexBuffer);
buf.bind();
buf.allocate(128);
QCOMPARE(buf.size(), 128);
buf.release();
buf.destroy();
QVERIFY(!buf.isCreated());
ctx->doneCurrent();
}
void tst_QOpenGL::bufferMapRange()
{
QScopedPointer<QSurface> surface(createSurface(QSurface::Window));
QOpenGLContext *ctx = new QOpenGLContext;
ctx->create();
ctx->makeCurrent(surface.data());
QOpenGLExtensions funcs(ctx);
if (!funcs.hasOpenGLExtension(QOpenGLExtensions::MapBufferRange))
QSKIP("glMapBufferRange not supported");
QOpenGLBuffer buf;
QVERIFY(buf.create());
buf.bind();
const char data[] = "some data";
buf.allocate(data, sizeof(data));
char *p = (char *) buf.mapRange(0, sizeof(data), QOpenGLBuffer::RangeRead | QOpenGLBuffer::RangeWrite);
QVERIFY(p);
QVERIFY(!strcmp(p, data));
p[1] = 'O';
buf.unmap();
p = (char *) buf.mapRange(1, 2, QOpenGLBuffer::RangeWrite);
QVERIFY(p);
p[1] = 'M';
buf.unmap();
p = (char *) buf.mapRange(0, sizeof(data), QOpenGLBuffer::RangeRead);
QVERIFY(!strcmp(p, "sOMe data"));
buf.unmap();
buf.destroy();
ctx->doneCurrent();
}
QTEST_MAIN(tst_QOpenGL)
#include "tst_qopengl.moc"