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:
parent
22e4391413
commit
bb760d9514
@ -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()
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user