diff options
Diffstat (limited to 'src/gui/opengl')
-rw-r--r-- | src/gui/opengl/opengl.pri | 1 | ||||
-rw-r--r-- | src/gui/opengl/qopengl.cpp | 11 | ||||
-rw-r--r-- | src/gui/opengl/qopengl.h | 32 | ||||
-rw-r--r-- | src/gui/opengl/qopenglextensions_p.h | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopenglframebufferobject.cpp | 250 | ||||
-rw-r--r-- | src/gui/opengl/qopenglframebufferobject.h | 1 | ||||
-rw-r--r-- | src/gui/opengl/qopenglfunctions.cpp | 52 | ||||
-rw-r--r-- | src/gui/opengl/qopenglpaintdevice.cpp | 12 | ||||
-rw-r--r-- | src/gui/opengl/qopenglpaintengine.cpp | 14 | ||||
-rw-r--r-- | src/gui/opengl/qopenglpaintengine_p.h | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexture.cpp | 397 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexture.h | 14 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureblitter.cpp | 21 | ||||
-rw-r--r-- | src/gui/opengl/qopengltextureblitter_p.h | 1 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache.cpp | 180 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache_p.h | 18 | ||||
-rw-r--r-- | src/gui/opengl/qopenglversionfunctions.cpp | 16 | ||||
-rw-r--r-- | src/gui/opengl/qopenglvertexarrayobject.cpp | 82 | ||||
-rw-r--r-- | src/gui/opengl/qopenglvertexarrayobject.h | 2 | ||||
-rw-r--r-- | src/gui/opengl/qopenglvertexarrayobject_p.h | 119 |
20 files changed, 1040 insertions, 187 deletions
diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index cadba26797..f82401c973 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -2,7 +2,6 @@ contains(QT_CONFIG, opengl):CONFIG += opengl contains(QT_CONFIG, opengles2):CONFIG += opengles2 -contains(QT_CONFIG, egl):CONFIG += egl contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) { diff --git a/src/gui/opengl/qopengl.cpp b/src/gui/opengl/qopengl.cpp index b452c605e3..9dc8f45495 100644 --- a/src/gui/opengl/qopengl.cpp +++ b/src/gui/opengl/qopengl.cpp @@ -55,16 +55,17 @@ QOpenGLExtensionMatcher::QOpenGLExtensionMatcher() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); QOpenGLFunctions *funcs = ctx->functions(); - const char *extensionStr = reinterpret_cast<const char *>(funcs->glGetString(GL_EXTENSIONS)); + const char *extensionStr = 0; + + if (ctx->isOpenGLES() || ctx->format().majorVersion() < 3) + extensionStr = reinterpret_cast<const char *>(funcs->glGetString(GL_EXTENSIONS)); if (extensionStr) { QByteArray ba(extensionStr); QList<QByteArray> extensions = ba.split(' '); m_extensions = extensions.toSet(); -#if !defined(QT_OPENGL_3) - } -#else } else { +#ifdef QT_OPENGL_3 // clear error state while (funcs->glGetError()) {} @@ -82,8 +83,8 @@ QOpenGLExtensionMatcher::QOpenGLExtensionMatcher() m_extensions.insert(str); } } +#endif // QT_OPENGL_3 } -#endif } QT_END_NAMESPACE diff --git a/src/gui/opengl/qopengl.h b/src/gui/opengl/qopengl.h index ef5ab9aa65..f0f19a5a85 100644 --- a/src/gui/opengl/qopengl.h +++ b/src/gui/opengl/qopengl.h @@ -67,8 +67,13 @@ #if defined(QT_OPENGL_ES_2) # if defined(Q_OS_MAC) // iOS -# include <OpenGLES/ES2/gl.h> -# include <OpenGLES/ES2/glext.h> +# if defined(QT_OPENGL_ES_3) +# include <OpenGLES/ES3/gl.h> +# include <OpenGLES/ES3/glext.h> +# else +# include <OpenGLES/ES2/gl.h> +# include <OpenGLES/ES2/glext.h> +# endif /* OES_EGL_image_external is not included in the Apple provided @@ -79,7 +84,28 @@ typedef void* GLeglImageOES; # else // "uncontrolled" ES2 platforms -# include <GLES2/gl2.h> + +// In "es2" builds (QT_OPENGL_ES_2) additional defines indicate if ES +// 3.0 or higher is available. In this case include the corresponding +// header. These are backwards compatible and it should be safe to +// include headers on top of each other, meaning that applications can +// include gl2.h even if gl31.h gets included here. + +// This compile time differentation is important inside Qt because, +// unlike desktop GL, GLES is different when it comes to versioning +// and extensions: Standard functions that are new in a given version +// are always available in a version-specific header and are not +// guaranteed to be dynamically resolvable via eglGetProcAddress (and +// are typically not available as extensions even if they were part of +// an extension for a previous version). + +# if defined(QT_OPENGL_ES_3_1) +# include <GLES3/gl31.h> +# elif defined(QT_OPENGL_ES_3) +# include <GLES3/gl3.h> +# else +# include <GLES2/gl2.h> +#endif /* Some GLES2 implementations (like the one on Harmattan) are missing the diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h index 265771ce1b..065cbb0e57 100644 --- a/src/gui/opengl/qopenglextensions_p.h +++ b/src/gui/opengl/qopenglextensions_p.h @@ -106,8 +106,6 @@ public: OpenGLExtensions openGLExtensions(); bool hasOpenGLExtension(QOpenGLExtensions::OpenGLExtension extension) const; - void initializeGLExtensions(); - GLvoid *glMapBuffer(GLenum target, GLenum access); GLboolean glUnmapBuffer(GLenum target); diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 3102e1ecd2..16094804c2 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -88,6 +88,10 @@ QT_BEGIN_NAMESPACE #define GL_DEPTH_COMPONENT24 0x81A6 #endif +#ifndef GL_DEPTH_COMPONENT24_OES +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#endif + #ifndef GL_READ_FRAMEBUFFER #define GL_READ_FRAMEBUFFER 0x8CA8 #endif @@ -96,6 +100,35 @@ QT_BEGIN_NAMESPACE #define GL_DRAW_FRAMEBUFFER 0x8CA9 #endif +#ifndef GL_RGB8 +#define GL_RGB8 0x8051 +#endif + +#ifndef GL_RGB10 +#define GL_RGB10 0x8052 +#endif + +#ifndef GL_RGBA8 +#define GL_RGBA8 0x8058 +#endif + +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif + +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif + + /*! \class QOpenGLFramebufferObjectFormat \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL @@ -423,18 +456,21 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) return; - // Fall back to using a normal non-msaa FBO if we don't have support for MSAA if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) { samples = 0; } -#ifndef QT_OPENGL_ES_2 - GLint maxSamples; - funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); - samples = qBound(0, int(samples), int(maxSamples)); -#endif + // On GLES 2.0 multisampled framebuffers are available through vendor-specific extensions + const bool msaaES2 = ctx->isOpenGLES() && (ctx->hasExtension("GL_ANGLE_framebuffer_multisample") + || ctx->hasExtension("GL_NV_framebuffer_multisample")); + + if (!ctx->isOpenGLES() || msaaES2) { + GLint maxSamples; + funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + samples = qBound(0, int(samples), int(maxSamples)); + } samples = qMax(0, samples); requestedSamples = samples; @@ -456,11 +492,9 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi } else { GLenum storageFormat = internal_format; #ifdef GL_RGBA8_OES - // Correct the internal format used by the render buffer when using ANGLE - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES && internal_format == GL_RGBA - && strstr((const char *)funcs.glGetString(GL_RENDERER), "ANGLE") != 0) { + // Correct the internal format used by the render buffer when using ES with extensions + if (msaaES2 && internal_format == GL_RGBA) storageFormat = GL_RGBA8_OES; - } #endif mipmap = false; @@ -488,7 +522,6 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi initAttachments(ctx, attachment); - funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); if (valid) { fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc); } else { @@ -518,8 +551,12 @@ void QOpenGLFramebufferObjectPrivate::initTexture(GLenum target, GLenum internal funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + GLuint pixelType = GL_UNSIGNED_BYTE; + if (internal_format == GL_RGB10_A2 || internal_format == GL_RGB10) + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + funcs.glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GL_RGBA, pixelType, NULL); if (mipmap) { int width = size.width(); int height = size.height(); @@ -529,7 +566,7 @@ void QOpenGLFramebufferObjectPrivate::initTexture(GLenum target, GLenum internal height = qMax(1, height >> 1); ++level; funcs.glTexImage2D(target, level, internal_format, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GL_RGBA, pixelType, NULL); } } funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -975,8 +1012,6 @@ bool QOpenGLFramebufferObject::bind() d->valid = d->checkFramebufferStatus(current); else d->initTexture(d->format.textureTarget(), d->format.internalTextureFormat(), d->size, d->format.mipmap()); - if (d->valid && current) - current->d_func()->current_fbo = d->fbo(); return d->valid; } @@ -1004,10 +1039,8 @@ bool QOpenGLFramebufferObject::release() qWarning("QOpenGLFramebufferObject::release() called from incompatible context"); #endif - if (current) { - current->d_func()->current_fbo = current->defaultFramebufferObject(); - d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo); - } + if (current) + d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject()); return true; } @@ -1054,7 +1087,7 @@ GLuint QOpenGLFramebufferObject::takeTexture() GLuint id = 0; if (isValid() && d->texture_guard) { QOpenGLContext *current = QOpenGLContext::currentContext(); - if (current && current->shareGroup() == d->fbo_guard->group() && current->d_func()->current_fbo == d->fbo()) + if (current && current->shareGroup() == d->fbo_guard->group() && isBound()) release(); id = d->texture_guard->id(); // Do not call free() on texture_guard, just null it out. @@ -1097,47 +1130,114 @@ QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const return d->format; } -Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool include_alpha, QOpenGLContext *context) { - int w = size.width(); - int h = size.height(); - - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - while (funcs->glGetError()); + QOpenGLFunctions *funcs = context->functions(); + const int w = size.width(); + const int h = size.height(); + bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); + if (isOpenGL12orBetter) { + QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); + funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, img.bits()); + return img; + } #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - QImage img(size, (alpha_format && include_alpha) ? QImage::Format_ARGB32_Premultiplied - : QImage::Format_RGB32); -#ifdef QT_OPENGL_ES - GLint fmt = GL_BGRA_EXT; -#else - GLint fmt = GL_BGRA; -#endif - funcs->glReadPixels(0, 0, w, h, fmt, GL_UNSIGNED_BYTE, img.bits()); - if (!funcs->glGetError()) - return img.mirrored(); + // Without GL_UNSIGNED_INT_8_8_8_8_REV, GL_BGRA only makes sense on little endian. + const bool supports_bgra = context->isOpenGLES() + ? context->hasExtension(QByteArrayLiteral("GL_EXT_read_format_bgra")) + : context->hasExtension(QByteArrayLiteral("GL_EXT_bgra")); + if (supports_bgra) { + QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); + funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, img.bits()); + return img; + } #endif - - QImage rgbaImage(size, (alpha_format && include_alpha) ? QImage::Format_RGBA8888_Premultiplied - : QImage::Format_RGBX8888); + QImage rgbaImage(size, include_alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888); funcs->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.bits()); - return rgbaImage.mirrored(); + return rgbaImage; +} + +static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool include_alpha, QOpenGLContext *context) +{ + // We assume OpenGL 1.2+ or ES 3.0+ here. + QImage img(size, include_alpha ? QImage::Format_A2BGR30_Premultiplied : QImage::Format_BGR30); + context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, img.bits()); + return img; +} + +static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLFunctions *funcs = ctx->functions(); + while (funcs->glGetError()); + + switch (internal_format) { + case GL_RGB: + case GL_RGB8: + return qt_gl_read_framebuffer_rgba8(size, false, ctx).mirrored(false, flip); + case GL_RGB10: + return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip); + case GL_RGB10_A2: + return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip); + case GL_RGBA: + case GL_RGBA8: + default: + return qt_gl_read_framebuffer_rgba8(size, include_alpha, ctx).mirrored(false, flip); + } + + Q_UNREACHABLE(); + return QImage(); +} + +Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +{ + return qt_gl_read_framebuffer(size, alpha_format ? GL_RGBA : GL_RGB, include_alpha, true); } /*! - \fn QImage QOpenGLFramebufferObject::toImage() const + \fn QImage QOpenGLFramebufferObject::toImage(bool flipped) const Returns the contents of this framebuffer object as a QImage. + If \a flipped is true the image is flipped from OpenGL coordinates to raster coordinates. + If used together with QOpenGLPaintDevice, \a flipped should be the opposite of the value + of QOpenGLPaintDevice::paintFlipped(). + Will try to return a premultiplied ARBG32 or RGB32 image. Since 5.2 it will fall back to - a premultiplied RGBA8888 or RGBx8888 image when reading to ARGB32 is not supported. + a premultiplied RGBA8888 or RGBx8888 image when reading to ARGB32 is not supported. Since 5.4 an + A2BGR30 image is returned if the internal format is RGB10_A2. + + For multisampled framebuffer objects the samples are resolved using the + \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents + of the returned image is undefined. + + For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is + a potentially expensive and inefficient operation. Therefore it is recommended that + this function is used as seldom as possible. + + \sa QOpenGLPaintDevice::paintFlipped() */ -QImage QOpenGLFramebufferObject::toImage() const + +QImage QOpenGLFramebufferObject::toImage(bool flipped) const { Q_D(const QOpenGLFramebufferObject); if (!d->valid) return QImage(); + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("QOpenGLFramebufferObject::toImage() called without a current context"); + return QImage(); + } + + GLuint prevFbo = 0; + ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); + + if (prevFbo != d->fbo()) + const_cast<QOpenGLFramebufferObject *>(this)->bind(); + + QImage image; // qt_gl_read_framebuffer doesn't work on a multisample FBO if (format().samples() != 0) { QOpenGLFramebufferObject temp(size(), QOpenGLFramebufferObjectFormat()); @@ -1145,20 +1245,31 @@ QImage QOpenGLFramebufferObject::toImage() const QRect rect(QPoint(0, 0), size()); blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect); - return temp.toImage(); + image = temp.toImage(flipped); + } else { + image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat(), true, flipped); } - bool wasBound = isBound(); - if (!wasBound) - const_cast<QOpenGLFramebufferObject *>(this)->bind(); - QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true); - if (!wasBound) - const_cast<QOpenGLFramebufferObject *>(this)->release(); + if (prevFbo != d->fbo()) + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); return image; } /*! + \fn QImage QOpenGLFramebufferObject::toImage() const + \overload + + Returns the contents of this framebuffer object as a QImage. This method flips + the image from OpenGL coordinates to raster coordinates. +*/ +// ### Qt 6: Remove this method and make it a default argument instead. +QImage QOpenGLFramebufferObject::toImage() const +{ + return toImage(true); +} + +/*! \fn bool QOpenGLFramebufferObject::bindDefault() Switches rendering back to the default, windowing system provided @@ -1170,16 +1281,13 @@ QImage QOpenGLFramebufferObject::toImage() const bool QOpenGLFramebufferObject::bindDefault() { QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); - QOpenGLFunctions functions(ctx); - if (ctx) { - ctx->d_func()->current_fbo = ctx->defaultFramebufferObject(); - functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); + if (ctx) + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); #ifdef QT_DEBUG - } else { + else qWarning("QOpenGLFramebufferObject::bindDefault() called without current context."); #endif - } return ctx != 0; } @@ -1192,7 +1300,7 @@ bool QOpenGLFramebufferObject::bindDefault() */ bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects() { - return QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::Framebuffers); + return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers); } /*! @@ -1228,6 +1336,8 @@ QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() cons This can be used to free or reattach the depth and stencil buffer attachments as needed. + + \note This function alters the current framebuffer binding. */ void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment) { @@ -1243,20 +1353,21 @@ void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachmen #endif d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); d->initAttachments(current, attachment); - if (current->d_func()->current_fbo != d->fbo()) - d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo); } /*! - Returns \c true if the framebuffer object is currently bound to a context, + Returns \c true if the framebuffer object is currently bound to the current context, otherwise false is returned. */ - bool QOpenGLFramebufferObject::isBound() const { Q_D(const QOpenGLFramebufferObject); - QOpenGLContext *current = QOpenGLContext::currentContext(); - return current ? current->d_func()->current_fbo == d->fbo() : false; + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + GLint fbo = 0; + ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); + return GLuint(fbo) == d->fbo(); } /*! @@ -1310,6 +1421,9 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, If \a source or \a target is 0, the default framebuffer will be used instead of a framebuffer object as source or target respectively. + This function will have no effect unless hasOpenGLFramebufferBlit() returns + true. + The \a buffers parameter should be a mask consisting of any combination of \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both @@ -1326,10 +1440,7 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, have different sizes. The sizes must also be the same if any of the framebuffer objects are multisample framebuffers. - Note that the scissor test will restrict the blit area if enabled. - - This function will have no effect unless hasOpenGLFramebufferBlit() returns - true. + \note The scissor test will restrict the blit area if enabled. \sa hasOpenGLFramebufferBlit() */ @@ -1346,6 +1457,9 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) return; + GLuint prevFbo = 0; + ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); + const int sx0 = sourceRect.left(); const int sx1 = sourceRect.left() + sourceRect.width(); const int sy0 = sourceRect.top(); @@ -1363,7 +1477,7 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, tx0, ty0, tx1, ty1, buffers, filter); - extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW } QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h index 3df929c210..0b1aaae36d 100644 --- a/src/gui/opengl/qopenglframebufferobject.h +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -94,6 +94,7 @@ public: GLuint takeTexture(); QSize size() const; QImage toImage() const; + QImage toImage(bool flipped) const; Attachment attachment() const; void setAttachment(Attachment attachment); diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index bc4a714c6c..f3d3d0b47b 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -126,6 +126,18 @@ QT_BEGIN_NAMESPACE glFuncs.glActiveTexture(GL_TEXTURE1); \endcode + An alternative approach is to query the context's associated + QOpenGLFunctions instance. This is somewhat faster than the previous + approach due to avoiding the creation of a new instance, but the difference + is fairly small since the internal data structures are shared, and function + resolving happens only once for a given context, regardless of the number of + QOpenGLFunctions instances initialized for it. + + \code + QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions(); + glFuncs->glActiveTexture(GL_TEXTURE1); + \endcode + QOpenGLFunctions provides wrappers for all OpenGL ES 2.0 functions, including the common subset of OpenGL 1.x and ES 2.0. While such functions, for example glClear() or @@ -372,6 +384,12 @@ static int qt_gl_resolve_extensions() if (ctx->isOpenGLES()) { if (format.majorVersion() >= 2) extensions |= QOpenGLExtensions::GenerateMipmap; + + if (format.majorVersion() >= 3) + extensions |= QOpenGLExtensions::PackedDepthStencil + | QOpenGLExtensions::Depth24 + | QOpenGLExtensions::ElementIndexUint; + if (extensionMatcher.match("GL_OES_mapbuffer")) extensions |= QOpenGLExtensions::MapBuffer; if (extensionMatcher.match("GL_OES_packed_depth_stencil")) @@ -387,6 +405,12 @@ static int qt_gl_resolve_extensions() extensions |= QOpenGLExtensions::FramebufferBlit; if (extensionMatcher.match("GL_ANGLE_framebuffer_multisample")) extensions |= QOpenGLExtensions::FramebufferMultisample; + if (extensionMatcher.match("GL_NV_framebuffer_blit")) + extensions |= QOpenGLExtensions::FramebufferBlit; + if (extensionMatcher.match("GL_NV_framebuffer_multisample")) + extensions |= QOpenGLExtensions::FramebufferMultisample; + if (format.majorVersion() >= 3) + extensions |= QOpenGLExtensions::FramebufferBlit | QOpenGLExtensions::FramebufferMultisample; } else { extensions |= QOpenGLExtensions::ElementIndexUint | QOpenGLExtensions::MapBuffer; @@ -2027,7 +2051,9 @@ namespace { enum ResolvePolicy { ResolveOES = 0x1, - ResolveEXT = 0x2 + ResolveEXT = 0x2, + ResolveANGLE = 0x4, + ResolveNV = 0x8 }; template <typename Base, typename FuncType, int Policy, typename ReturnType> @@ -2149,6 +2175,12 @@ private: if ((Policy & ResolveEXT) && !(funcs->*funcPointerName)) \ funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "EXT"); \ \ + if ((Policy & ResolveANGLE) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "ANGLE"); \ + \ + if ((Policy & ResolveNV) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "NV"); \ + \ if (!alternateFuncName.isEmpty() && !(funcs->*funcPointerName)) { \ funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName); \ \ @@ -2160,6 +2192,12 @@ private: \ if ((Policy & ResolveEXT) && !(funcs->*funcPointerName)) \ funcs->*funcPointerName = (FuncType)context->getProcAddress(alternateFuncName + "EXT"); \ + \ + if ((Policy & ResolveANGLE) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "ANGLE"); \ + \ + if ((Policy & ResolveNV) && !(funcs->*funcPointerName)) \ + funcs->*funcPointerName = (FuncType)context->getProcAddress(funcName + "NV"); \ } #define RESOLVER_COMMON_NON_VOID \ @@ -3156,16 +3194,24 @@ static void QOPENGLF_APIENTRY qopenglfResolveBlitFramebuffer(GLint srcX0, GLint GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { - RESOLVE_FUNC_VOID_WITH_ALTERNATE(ResolveEXT, BlitFramebuffer, BlitFramebufferANGLE) +#ifdef QT_OPENGL_ES_3 + ::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +#else + RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, BlitFramebuffer) (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +#endif } static void QOPENGLF_APIENTRY qopenglfResolveRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height) { - RESOLVE_FUNC_VOID_WITH_ALTERNATE(ResolveEXT, RenderbufferStorageMultisample, RenderbufferStorageMultisampleANGLE) +#ifdef QT_OPENGL_ES_3 + ::glRenderbufferStorageMultisample(target, samples, internalFormat, width, height); +#else + RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, RenderbufferStorageMultisample) (target, samples, internalFormat, width, height); +#endif } static void QOPENGLF_APIENTRY qopenglfResolveGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data) diff --git a/src/gui/opengl/qopenglpaintdevice.cpp b/src/gui/opengl/qopenglpaintdevice.cpp index 6750458f83..96fc992b45 100644 --- a/src/gui/opengl/qopenglpaintdevice.cpp +++ b/src/gui/opengl/qopenglpaintdevice.cpp @@ -63,9 +63,9 @@ QT_BEGIN_NAMESPACE \ingroup painting-3D - The QOpenGLPaintDevice uses the current QOpenGL context to render - QPainter draw commands. It requires OpenGL (ES) 2.0 support or - higher. + The QOpenGLPaintDevice uses the \b current QOpenGL context to render + QPainter draw commands. The context is captured upon construction. It + requires support for OpenGL (ES) 2.0 or higher. \section1 Performance @@ -359,9 +359,9 @@ bool QOpenGLPaintDevice::paintFlipped() const } /*! - This virtual method is provided as a callback to allow re-binding a - target frame buffer object when different QOpenGLPaintDevice instances - are issuing draw calls alternately on the same OpenGL context. + This virtual method is provided as a callback to allow re-binding a target + frame buffer object or context when different QOpenGLPaintDevice instances + are issuing draw calls alternately. QPainter::beginNativePainting will also trigger this method. */ diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index 81a0d82c99..d0590ca521 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -154,8 +154,8 @@ void QOpenGL2PaintEngineExPrivate::setBrush(const QBrush& brush) Q_ASSERT(newStyle != Qt::NoBrush); currentBrush = brush; - if (!currentBrushPixmap.isNull()) - currentBrushPixmap = QPixmap(); + if (!currentBrushImage.isNull()) + currentBrushImage = QImage(); brushUniformsDirty = true; // All brushes have at least one uniform if (newStyle > Qt::SolidPattern) @@ -214,11 +214,11 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture() updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform); } else if (style == Qt::TexturePattern) { - currentBrushPixmap = currentBrush.texture(); + currentBrushImage = currentBrush.textureImage(); int max_texture_size = ctx->d_func()->maxTextureSize(); - if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size) - currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + if (currentBrushImage.width() > max_texture_size || currentBrushImage.height() > max_texture_size) + currentBrushImage = currentBrushImage.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); GLuint wrapMode = GL_REPEAT; if (QOpenGLContext::currentContext()->isOpenGLES()) { @@ -229,7 +229,7 @@ void QOpenGL2PaintEngineExPrivate::updateBrushTexture() } funcs.glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); - QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, currentBrushPixmap); + QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, currentBrushImage); updateTextureFilter(GL_TEXTURE_2D, wrapMode, q->state()->renderHints & QPainter::SmoothPixmapTransform); textureInvertedY = false; } @@ -1972,6 +1972,8 @@ bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev) if (!d->device) return false; + d->device->ensureActiveTarget(); + if (d->device->context() != QOpenGLContext::currentContext()) { qWarning("QPainter::begin(): QOpenGLPaintDevice's context needs to be current"); return false; diff --git a/src/gui/opengl/qopenglpaintengine_p.h b/src/gui/opengl/qopenglpaintengine_p.h index 4f0e2e52a4..89eb7e87b2 100644 --- a/src/gui/opengl/qopenglpaintengine_p.h +++ b/src/gui/opengl/qopenglpaintengine_p.h @@ -288,7 +288,7 @@ public: QBrush currentBrush; // May not be the state's brush! const QBrush noBrush; - QPixmap currentBrushPixmap; + QImage currentBrushImage; QOpenGL2PEXVertexArray vertexCoordinateArray; QOpenGL2PEXVertexArray textureCoordinateArray; diff --git a/src/gui/opengl/qopengltexture.cpp b/src/gui/opengl/qopengltexture.cpp index e0e1c72a6c..811505cecb 100644 --- a/src/gui/opengl/qopengltexture.cpp +++ b/src/gui/opengl/qopengltexture.cpp @@ -67,7 +67,7 @@ QOpenGLTexturePrivate::QOpenGLTexturePrivate(QOpenGLTexture::Target textureTarge mipLevels(-1), layers(1), faces(1), - samples(1), + samples(0), fixedSamplePositions(true), baseLevel(0), maxLevel(1000), @@ -200,7 +200,7 @@ void QOpenGLTexturePrivate::destroy() mipLevels = -1; layers = 1; faces = 1; - samples = 1; + samples = 0; fixedSamplePositions = true, baseLevel = 0; maxLevel = 1000; @@ -314,8 +314,239 @@ void QOpenGLTexturePrivate::allocateStorage() allocateMutableStorage(); } +static QOpenGLTexture::PixelFormat pixelFormatCompatibleWithInternalFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return QOpenGLTexture::NoSourceFormat; + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + case QOpenGLTexture::RGB9E5: + case QOpenGLTexture::RG11B10F: + case QOpenGLTexture::RG3B2: + case QOpenGLTexture::R5G6B5: + case QOpenGLTexture::RGB5A1: + case QOpenGLTexture::RGBA4: + case QOpenGLTexture::RGB10A2: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + return QOpenGLTexture::Depth; + + case QOpenGLTexture::D24S8: + case QOpenGLTexture::D32FS8X24: + return QOpenGLTexture::DepthStencil; + + case QOpenGLTexture::S8: + return QOpenGLTexture::Stencil; + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::DepthFormat: + return QOpenGLTexture::Depth; + + case QOpenGLTexture::AlphaFormat: + return QOpenGLTexture::Alpha; + + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::LuminanceFormat: + return QOpenGLTexture::Luminance; + + case QOpenGLTexture::LuminanceAlphaFormat: + return QOpenGLTexture::LuminanceAlpha; + } + + Q_UNREACHABLE(); + return QOpenGLTexture::NoSourceFormat; +} + +static QOpenGLTexture::PixelType pixelTypeCompatibleWithInternalFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return QOpenGLTexture::NoPixelType; + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + case QOpenGLTexture::RGB9E5: + case QOpenGLTexture::RG11B10F: + case QOpenGLTexture::RG3B2: + case QOpenGLTexture::R5G6B5: + case QOpenGLTexture::RGB5A1: + case QOpenGLTexture::RGBA4: + case QOpenGLTexture::RGB10A2: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::D24S8: + return QOpenGLTexture::UInt32_D24S8; + + case QOpenGLTexture::D32FS8X24: + return QOpenGLTexture::Float32_D32_UInt32_S8_X24; + + case QOpenGLTexture::S8: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::DepthFormat: + case QOpenGLTexture::AlphaFormat: + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + case QOpenGLTexture::LuminanceFormat: + case QOpenGLTexture::LuminanceAlphaFormat: + return QOpenGLTexture::UInt8; + } + + Q_UNREACHABLE(); + return QOpenGLTexture::NoPixelType; +} + void QOpenGLTexturePrivate::allocateMutableStorage() { + const QOpenGLTexture::PixelFormat pixelFormat = pixelFormatCompatibleWithInternalFormat(format); + const QOpenGLTexture::PixelType pixelType = pixelTypeCompatibleWithInternalFormat(format); + switch (target) { case QOpenGLTexture::TargetBuffer: // Buffer textures get their storage from an external OpenGL buffer @@ -328,7 +559,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() texFuncs->glTextureImage1D(textureId, target, bindingTarget, level, format, mipLevelSize(level, dimensions[0]), 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } else { qWarning("1D textures are not supported"); return; @@ -343,7 +574,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[0]), layers, 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } else { qWarning("1D array textures are not supported"); return; @@ -357,7 +588,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[0]), mipLevelSize(level, dimensions[1]), 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); break; case QOpenGLTexture::TargetCubeMap: { @@ -377,7 +608,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[0]), mipLevelSize(level, dimensions[1]), 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } } break; @@ -391,7 +622,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[1]), layers, 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } else { qWarning("Array textures are not supported"); return; @@ -407,7 +638,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[1]), 6 * layers, 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } else { qWarning("Cubemap Array textures are not supported"); return; @@ -422,7 +653,7 @@ void QOpenGLTexturePrivate::allocateMutableStorage() mipLevelSize(level, dimensions[1]), mipLevelSize(level, dimensions[2]), 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, 0); + pixelFormat, pixelType, 0); } else { qWarning("3D textures are not supported"); return; @@ -1289,6 +1520,7 @@ QOpenGLTexture *QOpenGLTexturePrivate::createTextureView(QOpenGLTexture::Target \value D32 Equivalent to GL_DEPTH_COMPONENT32 \value D32F Equivalent to GL_DEPTH_COMPONENT32F \value D32FS8X24 Equivalent to GL_DEPTH32F_STENCIL8 + \value S8 Equivalent to GL_STENCIL_INDEX8. Introduced in Qt 5.4 \value RGB_DXT1 Equivalent to GL_COMPRESSED_RGB_S3TC_DXT1_EXT \value RGBA_DXT1 Equivalent to GL_COMPRESSED_RGBA_S3TC_DXT1_EXT @@ -1348,6 +1580,7 @@ QOpenGLTexture *QOpenGLTexturePrivate::createTextureView(QOpenGLTexture::Target \value BGR_Integer Equivalent to GL_BGR_INTEGER \value RGBA_Integer Equivalent to GL_RGBA_INTEGER \value BGRA_Integer Equivalent to GL_BGRA_INTEGER + \value Stencil Equivalent to GL_STENCIL_INDEX. Introduced in Qt 5.4 \value Depth Equivalent to GL_DEPTH_COMPONENT \value DepthStencil Equivalent to GL_DEPTH_STENCIL \value Alpha Equivalent to GL_ALPHA (OpenGL ES 2 only) @@ -1380,8 +1613,12 @@ QOpenGLTexture *QOpenGLTexturePrivate::createTextureView(QOpenGLTexture::Target \value UInt16_R5G6B5_Rev Equivalent to GL_UNSIGNED_SHORT_5_6_5_REV \value UInt16_RGBA4 Equivalent to GL_UNSIGNED_SHORT_4_4_4_4 \value UInt16_RGBA4_Rev Equivalent to GL_UNSIGNED_SHORT_4_4_4_4_REV + \value UInt32_RGBA8 Equivalent to GL_UNSIGNED_INT_8_8_8_8 + \value UInt32_RGBA8_Rev Equivalent to GL_UNSIGNED_INT_8_8_8_8_REV \value UInt32_RGB10A2 Equivalent to GL_UNSIGNED_INT_10_10_10_2 \value UInt32_RGB10A2_Rev Equivalent to GL_UNSIGNED_INT_2_10_10_10_REV + \value UInt32_D24S8 Equivalent to GL_UNSIGNED_INT_24_8. Introduced in Qt 5.4 + \value Float32_D32_UInt32_S8_X24 Equivalent to GL_FLOAT_32_UNSIGNED_INT_24_8_REV. Introduced in Qt 5.4 */ /*! @@ -1483,6 +1720,17 @@ QOpenGLTexture::~QOpenGLTexture() } /*! + Returns the binding target of this texture. + + \since 5.4 +*/ +QOpenGLTexture::Target QOpenGLTexture::target() const +{ + Q_D(const QOpenGLTexture); + return d->target; +} + +/*! Creates the underlying OpenGL texture object. This requires a current valid OpenGL context. If the texture object already exists, this function does nothing. @@ -1820,6 +2068,7 @@ void QOpenGLTexture::setFormat(TextureFormat format) case D32: case D32F: case D32FS8X24: + case S8: case DepthFormat: case AlphaFormat: case RGBFormat: @@ -2063,6 +2312,125 @@ int QOpenGLTexture::faces() const } /*! + Sets the number of \a samples to allocate storage for when rendering to + a multisample capable texture target. This function should + be called before storage is allocated for the texture. + + For targets that do not support multisampling this function has + no effect. + + \sa samples(), isStorageAllocated() +*/ +void QOpenGLTexture::setSamples(int samples) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set sample count on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setSamples()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + d->samples = samples; + break; + + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + + qWarning("Texture target does not support multisampling"); + break; + } +} + +/*! + Returns the number of multisample sample points for this texture. + If storage has not yet been allocated for this texture then + this function returns the requested number of samples. + + For texture targets that do not support multisampling this + will return 0. + + \sa setSamples(), isStorageAllocated() +*/ +int QOpenGLTexture::samples() const +{ + Q_D(const QOpenGLTexture); + return d->samples; +} + +/*! + Sets whether the sample positions and number of samples used with + a multisample capable texture target to \a fixed. If set to \c true + the sample positions and number of samples used are the same for + all texels in the image and will not depend upon the image size or + internal format. This function should be called before storage is allocated + for the texture. + + For targets that do not support multisampling this function has + no effect. + + The default value is \c true. + + \sa isFixedSamplePositions(), isStorageAllocated() +*/ +void QOpenGLTexture::setFixedSamplePositions(bool fixed) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set sample positions on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setFixedSamplePositions()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + d->fixedSamplePositions = fixed; + break; + + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + + qWarning("Texture target does not support multisampling"); + break; + } +} + +/*! + Returns whether this texture uses a fixed pattern of multisample + samples. If storage has not yet been allocated for this texture then + this function returns the requested fixed sample position setting. + + For texture targets that do not support multisampling this + will return \c true. + + \sa setFixedSamplePositions(), isStorageAllocated() +*/ +bool QOpenGLTexture::isFixedSamplePositions() const +{ + Q_D(const QOpenGLTexture); + return d->fixedSamplePositions; +} + +/*! Allocates server-side storage for this texture object taking into account, the format, dimensions, mipmap levels, array layers and cubemap faces. @@ -2298,7 +2666,16 @@ void QOpenGLTexture::setData(PixelFormat sourceFormat, PixelType sourceType, */ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) { - setFormat(QOpenGLTexture::RGBA8_UNorm); + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) { + qWarning("QOpenGLTexture::setData() requires a valid current context"); + return; + } + if (context->isOpenGLES() && context->format().majorVersion() < 3) + setFormat(QOpenGLTexture::RGBAFormat); + else + setFormat(QOpenGLTexture::RGBA8_UNorm); + setSize(image.width(), image.height()); setMipLevels(genMipMaps == GenerateMipMaps ? maximumMipLevels() : 1); allocateStorage(); diff --git a/src/gui/opengl/qopengltexture.h b/src/gui/opengl/qopengltexture.h index 0c272456f6..1f4e0c048f 100644 --- a/src/gui/opengl/qopengltexture.h +++ b/src/gui/opengl/qopengltexture.h @@ -100,6 +100,8 @@ public: explicit QOpenGLTexture(const QImage& image, MipMapGeneration genMipMaps = GenerateMipMaps); ~QOpenGLTexture(); + Target target() const; + // Creation and destruction bool create(); void destroy(); @@ -201,6 +203,7 @@ public: D32 = 0x81A7, // GL_DEPTH_COMPONENT32 D32F = 0x8CAC, // GL_DEPTH_COMPONENT32F D32FS8X24 = 0x8CAD, // GL_DEPTH32F_STENCIL8 + S8 = 0x8D48, // GL_STENCIL_INDEX8 // Compressed formats RGB_DXT1 = 0x83F0, // GL_COMPRESSED_RGB_S3TC_DXT1_EXT @@ -272,6 +275,10 @@ public: void setLayers(int layers); int layers() const; int faces() const; + void setSamples(int samples); + int samples() const; + void setFixedSamplePositions(bool fixed); + bool isFixedSamplePositions() const; void allocateStorage(); bool isStorageAllocated() const; @@ -304,6 +311,7 @@ public: BGR_Integer = 0x8D9A, // GL_BGR_INTEGER RGBA_Integer = 0x8D99, // GL_RGBA_INTEGER BGRA_Integer = 0x8D9B, // GL_BGRA_INTEGER + Stencil = 0x1901, // GL_STENCIL_INDEX Depth = 0x1902, // GL_DEPTH_COMPONENT DepthStencil = 0x84F9, // GL_DEPTH_STENCIL Alpha = 0x1906, // GL_ALPHA @@ -332,8 +340,12 @@ public: UInt16_R5G6B5_Rev = 0x8364, // GL_UNSIGNED_SHORT_5_6_5_REV UInt16_RGBA4 = 0x8033, // GL_UNSIGNED_SHORT_4_4_4_4 UInt16_RGBA4_Rev = 0x8365, // GL_UNSIGNED_SHORT_4_4_4_4_REV + UInt32_RGBA8 = 0x8035, // GL_UNSIGNED_INT_8_8_8_8 + UInt32_RGBA8_Rev = 0x8367, // GL_UNSIGNED_INT_8_8_8_8_REV UInt32_RGB10A2 = 0x8036, // GL_UNSIGNED_INT_10_10_10_2 - UInt32_RGB10A2_Rev = 0x8368 // GL_UNSIGNED_INT_2_10_10_10_REV + UInt32_RGB10A2_Rev = 0x8368, // GL_UNSIGNED_INT_2_10_10_10_REV + UInt32_D24S8 = 0x84FA, // GL_UNSIGNED_INT_24_8 + Float32_D32_UInt32_S8_X24 = 0x8DAD // GL_FLOAT_32_UNSIGNED_INT_24_8_REV }; // Pixel transfer diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index e6dbb70618..ef548188c8 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -67,8 +67,10 @@ static const char fragment_shader150[] = "out vec4 fragcolor;" "uniform sampler2D textureSampler;" "uniform bool swizzle;" + "uniform float opacity;" "void main() {" " vec4 tmpFragColor = texture(textureSampler, uv);" + " tmpFragColor.a *= opacity;" " fragcolor = swizzle ? tmpFragColor.bgra : tmpFragColor;" "}"; @@ -87,8 +89,10 @@ static const char fragment_shader[] = "varying highp vec2 uv;" "uniform sampler2D textureSampler;" "uniform bool swizzle;" + "uniform highp float opacity;" "void main() {" " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" + " tmpFragColor.a *= opacity;" " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" "}"; @@ -140,6 +144,8 @@ public: , textureTransformUniformPos(0) , swizzle(false) , swizzleOld(false) + , opacity(1.0f) + , opacityOld(0.0f) , textureMatrixUniformState(User) , vao(new QOpenGLVertexArrayObject()) { } @@ -165,6 +171,11 @@ public: program->setUniformValue(swizzleUniformPos, swizzle); swizzleOld = swizzle; } + + if (opacity != opacityOld) { + program->setUniformValue(opacityUniformPos, opacity); + opacityOld = opacity; + } } QOpenGLBuffer vertexBuffer; @@ -175,8 +186,11 @@ public: GLuint textureCoordAttribPos; GLuint textureTransformUniformPos; GLuint swizzleUniformPos; + GLuint opacityUniformPos; bool swizzle; bool swizzleOld; + float opacity; + float opacityOld; TextureMatrixUniform textureMatrixUniformState; QScopedPointer<QOpenGLVertexArrayObject> vao; }; @@ -274,6 +288,7 @@ bool QOpenGLTextureBlitter::create() d->textureCoordAttribPos = d->program->attributeLocation("textureCoord"); d->textureTransformUniformPos = d->program->uniformLocation("textureTransform"); d->swizzleUniformPos = d->program->uniformLocation("swizzle"); + d->opacityUniformPos = d->program->uniformLocation("opacity"); d->program->setUniformValue(d->swizzleUniformPos,false); @@ -329,6 +344,12 @@ void QOpenGLTextureBlitter::setSwizzleRB(bool swizzle) d->swizzle = swizzle; } +void QOpenGLTextureBlitter::setOpacity(float opacity) +{ + Q_D(QOpenGLTextureBlitter); + d->opacity = opacity; +} + void QOpenGLTextureBlitter::blit(GLuint texture, const QMatrix4x4 &targetTransform, Origin sourceOrigin) diff --git a/src/gui/opengl/qopengltextureblitter_p.h b/src/gui/opengl/qopengltextureblitter_p.h index c1dcaf5700..b4310e5f94 100644 --- a/src/gui/opengl/qopengltextureblitter_p.h +++ b/src/gui/opengl/qopengltextureblitter_p.h @@ -69,6 +69,7 @@ public: void release(); void setSwizzleRB(bool swizzle); + void setOpacity(float opacity); void blit(GLuint texture, const QMatrix4x4 &targetTransform, Origin sourceOrigin); void blit(GLuint texture, const QMatrix4x4 &targetTransform, const QMatrix3x3 &sourceTransform); diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp index f4aa29ac0f..055974d5a4 100644 --- a/src/gui/opengl/qopengltexturecache.cpp +++ b/src/gui/opengl/qopengltexturecache.cpp @@ -40,13 +40,35 @@ ****************************************************************************/ #include "qopengltexturecache_p.h" +#include <qmath.h> #include <qopenglfunctions.h> #include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> #include <private/qimagepixmapcleanuphooks_p.h> #include <qpa/qplatformpixmap.h> QT_BEGIN_NAMESPACE +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif + +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif + class QOpenGLTextureCacheWrapper { public: @@ -106,7 +128,7 @@ QOpenGLTextureCache::~QOpenGLTextureCache() { } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, BindOptions options) { if (pixmap.isNull()) return 0; @@ -116,34 +138,20 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap & // A QPainter is active on the image - take the safe route and replace the texture. if (!pixmap.paintingActive()) { QOpenGLCachedTexture *entry = m_cache.object(key); - if (entry) { + if (entry && entry->options() == options) { context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); return entry->id(); } } - GLuint id = bindTexture(context, key, pixmap.toImage()); + GLuint id = bindTexture(context, key, pixmap.toImage(), options); if (id > 0) QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); return id; } -// returns the highest number closest to v, which is a power of 2 -// NB! assumes 32 bit ints -static int qt_next_power_of_two(int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - return v; -} - -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options) { if (image.isNull()) return 0; @@ -153,7 +161,7 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i // A QPainter is active on the image - take the safe route and replace the texture. if (!image.paintingActive()) { QOpenGLCachedTexture *entry = m_cache.object(key); - if (entry) { + if (entry && entry->options() == options) { context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); return entry->id(); } @@ -164,33 +172,149 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &i // Scale the pixmap if needed. GL textures needs to have the // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL // 2.0 or use the GL_TEXTURE_RECTANGLE texture target - int tx_w = qt_next_power_of_two(image.width()); - int tx_h = qt_next_power_of_two(image.height()); + int tx_w = qNextPowerOfTwo(image.width() - 1); + int tx_h = qNextPowerOfTwo(image.height() - 1); if (tx_w != image.width() || tx_h != image.height()) { img = img.scaled(tx_w, tx_h); } } - GLuint id = bindTexture(context, key, img); + GLuint id = bindTexture(context, key, img, options); if (id > 0) QImagePixmapCleanupHooks::enableCleanupHooks(image); return id; } -GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image) +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, BindOptions options) { GLuint id; QOpenGLFunctions *funcs = context->functions(); funcs->glGenTextures(1, &id); funcs->glBindTexture(GL_TEXTURE_2D, id); - QImage tx = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + QImage tx; + GLenum externalFormat; + GLenum internalFormat; + GLuint pixelType; + QImage::Format targetFormat = QImage::Format_Invalid; + const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); + + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + if (isOpenGL12orBetter) { + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; + } else { +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian. + break; +#endif + if (static_cast<QOpenGLExtensions*>(context->functions())->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat)) { + // GL_EXT_bgra or GL_EXT_texture_format_BGRA8888 extensions. + if (context->isOpenGLES()) { + // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. + externalFormat = internalFormat = GL_BGRA; + } else { + // OpenGL BGRA/BGR format is not allowed as an internal format + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + } + pixelType = GL_UNSIGNED_BYTE; + } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { + // Is only allowed as an external format like OpenGL. + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + } else { + // No support for direct ARGB32 upload. + break; + } + } + targetFormat = image.format(); + break; + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } + break; + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + if (isOpenGL12orBetter) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_BGRA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } else if (context->isOpenGLES() && context->format().majorVersion() >= 3) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + targetFormat = QImage::Format_A2BGR30_Premultiplied; + } + break; + case QImage::Format_RGB444: + case QImage::Format_RGB555: + case QImage::Format_RGB16: + if (isOpenGL12orBetter || context->isOpenGLES()) { + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_SHORT_5_6_5; + targetFormat = QImage::Format_RGB16; + } + break; + case QImage::Format_RGB666: + case QImage::Format_RGB888: + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_RGB888; + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBA8888_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + break; + default: + break; + } + + if (targetFormat == QImage::Format_Invalid) { + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + if (!image.hasAlphaChannel()) + targetFormat = QImage::Format_RGBX8888; + else + targetFormat = QImage::Format_RGBA8888; + } + + if (options & PremultipliedAlphaBindOption) { + if (targetFormat == QImage::Format_ARGB32) + targetFormat = QImage::Format_ARGB32_Premultiplied; + else if (targetFormat == QImage::Format_RGBA8888) + targetFormat = QImage::Format_RGBA8888_Premultiplied; + } else { + if (targetFormat == QImage::Format_ARGB32_Premultiplied) + targetFormat = QImage::Format_ARGB32; + else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) + targetFormat = QImage::Format_RGBA8888; + } + + if (image.format() != targetFormat) + tx = image.convertToFormat(targetFormat); + else + tx = image; - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tx.width(), tx.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, const_cast<const QImage &>(tx).bits()); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, const_cast<const QImage &>(tx).bits()); - int cost = tx.width() * tx.height() * 4 / 1024; - m_cache.insert(key, new QOpenGLCachedTexture(id, context), cost); + int cost = tx.width() * tx.height() * tx.depth() / (1024 * 8); + m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost); return id; } @@ -216,7 +340,7 @@ static void freeTexture(QOpenGLFunctions *funcs, GLuint id) funcs->glDeleteTextures(1, &id); } -QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLContext *context) +QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, int options, QOpenGLContext *context) : m_options(options) { m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture); } diff --git a/src/gui/opengl/qopengltexturecache_p.h b/src/gui/opengl/qopengltexturecache_p.h index 2e82d5f373..d81115fefc 100644 --- a/src/gui/opengl/qopengltexturecache_p.h +++ b/src/gui/opengl/qopengltexturecache_p.h @@ -64,13 +64,15 @@ QT_BEGIN_NAMESPACE class QOpenGLCachedTexture { public: - QOpenGLCachedTexture(GLuint id, QOpenGLContext *context); + QOpenGLCachedTexture(GLuint id, int options, QOpenGLContext *context); ~QOpenGLCachedTexture() { m_resource->free(); } GLuint id() const { return m_resource->id(); } + int options() const { return m_options; } private: QOpenGLSharedResourceGuard *m_resource; + int m_options; }; class Q_GUI_EXPORT QOpenGLTextureCache : public QOpenGLSharedResource @@ -81,8 +83,14 @@ public: QOpenGLTextureCache(QOpenGLContext *); ~QOpenGLTextureCache(); - GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap); - GLuint bindTexture(QOpenGLContext *context, const QImage &image); + enum BindOption { + NoBindOption = 0x0000, + PremultipliedAlphaBindOption = 0x0001, + }; + Q_DECLARE_FLAGS(BindOptions, BindOption) + + GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption); + GLuint bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureCache::BindOptions options = PremultipliedAlphaBindOption); void invalidate(qint64 key); @@ -90,12 +98,14 @@ public: void freeResource(QOpenGLContext *ctx); private: - GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image); + GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureCache::BindOptions options); QMutex m_mutex; QCache<quint64, QOpenGLCachedTexture> m_cache; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureCache::BindOptions) + QT_END_NAMESPACE #endif diff --git a/src/gui/opengl/qopenglversionfunctions.cpp b/src/gui/opengl/qopenglversionfunctions.cpp index 5949eb6391..3335a88cbb 100644 --- a/src/gui/opengl/qopenglversionfunctions.cpp +++ b/src/gui/opengl/qopenglversionfunctions.cpp @@ -227,7 +227,9 @@ QOpenGLFunctions_1_0_CoreBackend::QOpenGLFunctions_1_0_CoreBackend(QOpenGLContex { // OpenGL 1.0 core functions #if defined(Q_OS_WIN) - HMODULE handle = GetModuleHandleA("opengl32.dll"); + HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle()); + if (!handle) + handle = GetModuleHandleA("opengl32.dll"); Viewport = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLint , GLint , GLsizei , GLsizei )>(GetProcAddress(handle, "glViewport")); DepthRange = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLdouble , GLdouble )>(GetProcAddress(handle, "glDepthRange")); IsEnabled = reinterpret_cast<GLboolean (QOPENGLF_APIENTRYP)(GLenum )>(GetProcAddress(handle, "glIsEnabled")); @@ -339,7 +341,9 @@ QOpenGLFunctions_1_1_CoreBackend::QOpenGLFunctions_1_1_CoreBackend(QOpenGLContex { // OpenGL 1.1 core functions #if defined(Q_OS_WIN) - HMODULE handle = GetModuleHandleA("opengl32.dll"); + HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle()); + if (!handle) + handle = GetModuleHandleA("opengl32.dll"); Indexubv = reinterpret_cast<void (QOPENGLF_APIENTRYP)(const GLubyte *)>(GetProcAddress(handle, "glIndexubv")); Indexub = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLubyte )>(GetProcAddress(handle, "glIndexub")); IsTexture = reinterpret_cast<GLboolean (QOPENGLF_APIENTRYP)(GLuint )>(GetProcAddress(handle, "glIsTexture")); @@ -991,7 +995,9 @@ QOpenGLFunctions_1_0_DeprecatedBackend::QOpenGLFunctions_1_0_DeprecatedBackend(Q { // OpenGL 1.0 deprecated functions #if defined(Q_OS_WIN) - HMODULE handle = GetModuleHandleA("opengl32.dll"); + HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle()); + if (!handle) + handle = GetModuleHandleA("opengl32.dll"); Translatef = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLfloat , GLfloat , GLfloat )>(GetProcAddress(handle, "glTranslatef")); Translated = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLdouble , GLdouble , GLdouble )>(GetProcAddress(handle, "glTranslated")); Scalef = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLfloat , GLfloat , GLfloat )>(GetProcAddress(handle, "glScalef")); @@ -1523,7 +1529,9 @@ QOpenGLFunctions_1_1_DeprecatedBackend::QOpenGLFunctions_1_1_DeprecatedBackend(Q { // OpenGL 1.1 deprecated functions #if defined(Q_OS_WIN) - HMODULE handle = GetModuleHandleA("opengl32.dll"); + HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle()); + if (!handle) + handle = GetModuleHandleA("opengl32.dll"); PushClientAttrib = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLbitfield )>(GetProcAddress(handle, "glPushClientAttrib")); PopClientAttrib = reinterpret_cast<void (QOPENGLF_APIENTRYP)()>(GetProcAddress(handle, "glPopClientAttrib")); PrioritizeTextures = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , const GLuint *, const GLfloat *)>(GetProcAddress(handle, "glPrioritizeTextures")); diff --git a/src/gui/opengl/qopenglvertexarrayobject.cpp b/src/gui/opengl/qopenglvertexarrayobject.cpp index 5ebfc9a9c8..3b106fecf5 100644 --- a/src/gui/opengl/qopenglvertexarrayobject.cpp +++ b/src/gui/opengl/qopenglvertexarrayobject.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sean Harmer <sean.harmer@kdab.com> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -49,55 +49,49 @@ #include <QtGui/qopenglfunctions_3_0.h> #include <QtGui/qopenglfunctions_3_2_core.h> +#include <private/qopenglvertexarrayobject_p.h> + QT_BEGIN_NAMESPACE class QOpenGLFunctions_3_0; class QOpenGLFunctions_3_2_Core; -class QVertexArrayObjectHelper +void qtInitializeVertexArrayObjectHelper(QOpenGLVertexArrayObjectHelper *helper, QOpenGLContext *context) { -public: - QVertexArrayObjectHelper(QOpenGLContext *context) - { - Q_ASSERT(context); - if (context->isOpenGLES()) { - GenVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , GLuint *)>(context->getProcAddress(QByteArrayLiteral("glGenVertexArraysOES"))); - DeleteVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , const GLuint *)>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArraysOES"))); - BindVertexArray = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint )>(context->getProcAddress(QByteArrayLiteral("glBindVertexArrayOES"))); - } else { - if (context->hasExtension(QByteArrayLiteral("GL_APPLE_vertex_array_object"))) { - GenVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , GLuint *)>(context->getProcAddress(QByteArrayLiteral("glGenVertexArraysAPPLE"))); - DeleteVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , const GLuint *)>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArraysAPPLE"))); - BindVertexArray = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint )>(context->getProcAddress(QByteArrayLiteral("glBindVertexArrayAPPLE"))); - } else { - GenVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , GLuint *)>(context->getProcAddress(QByteArrayLiteral("glGenVertexArrays"))); - DeleteVertexArrays = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , const GLuint *)>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArrays"))); - BindVertexArray = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint )>(context->getProcAddress(QByteArrayLiteral("glBindVertexArray"))); - } - } - } + Q_ASSERT(helper); + Q_ASSERT(context); - inline void glGenVertexArrays(GLsizei n, GLuint *arrays) - { - GenVertexArrays(n, arrays); - } + bool tryARB = true; - inline void glDeleteVertexArrays(GLsizei n, const GLuint *arrays) - { - DeleteVertexArrays(n, arrays); + if (context->isOpenGLES()) { +#ifdef QT_OPENGL_ES_3 + if (context->format().majorVersion() >= 3) { + helper->GenVertexArrays = ::glGenVertexArrays; + helper->DeleteVertexArrays = ::glDeleteVertexArrays; + helper->BindVertexArray = ::glBindVertexArray; + tryARB = false; + } else +#endif + if (context->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) { + helper->GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glGenVertexArraysOES"))); + helper->DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArraysOES"))); + helper->BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress(QByteArrayLiteral("glBindVertexArrayOES"))); + tryARB = false; + } + } else if (context->hasExtension(QByteArrayLiteral("GL_APPLE_vertex_array_object")) && + !context->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) { + helper->GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glGenVertexArraysAPPLE"))); + helper->DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArraysAPPLE"))); + helper->BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress(QByteArrayLiteral("glBindVertexArrayAPPLE"))); + tryARB = false; } - inline void glBindVertexArray(GLuint array) - { - BindVertexArray(array); + if (tryARB && context->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) { + helper->GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glGenVertexArrays"))); + helper->DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArrays"))); + helper->BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress(QByteArrayLiteral("glBindVertexArray"))); } - -private: - // Function signatures are equivalent between desktop core, ARB, APPLE and ES 2 extensions - void (QOPENGLF_APIENTRYP GenVertexArrays)(GLsizei n, GLuint *arrays); - void (QOPENGLF_APIENTRYP DeleteVertexArrays)(GLsizei n, const GLuint *arrays); - void (QOPENGLF_APIENTRYP BindVertexArray)(GLuint array); -}; +} class QOpenGLVertexArrayObjectPrivate : public QObjectPrivate { @@ -128,7 +122,7 @@ public: union { QOpenGLFunctions_3_0 *core_3_0; QOpenGLFunctions_3_2_Core *core_3_2; - QVertexArrayObjectHelper *helper; + QOpenGLVertexArrayObjectHelper *helper; } vaoFuncs; enum { NotSupported, @@ -160,8 +154,8 @@ bool QOpenGLVertexArrayObjectPrivate::create() QObject::connect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed())); if (ctx->isOpenGLES()) { - if (ctx->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) { - vaoFuncs.helper = new QVertexArrayObjectHelper(ctx); + if (ctx->format().majorVersion() >= 3 || ctx->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) { + vaoFuncs.helper = new QOpenGLVertexArrayObjectHelper(ctx); vaoFuncsType = OES; vaoFuncs.helper->glGenVertexArrays(1, &vao); } @@ -183,11 +177,11 @@ bool QOpenGLVertexArrayObjectPrivate::create() } else #endif if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) { - vaoFuncs.helper = new QVertexArrayObjectHelper(ctx); + vaoFuncs.helper = new QOpenGLVertexArrayObjectHelper(ctx); vaoFuncsType = ARB; vaoFuncs.helper->glGenVertexArrays(1, &vao); } else if (ctx->hasExtension(QByteArrayLiteral("GL_APPLE_vertex_array_object"))) { - vaoFuncs.helper = new QVertexArrayObjectHelper(ctx); + vaoFuncs.helper = new QOpenGLVertexArrayObjectHelper(ctx); vaoFuncsType = APPLE; vaoFuncs.helper->glGenVertexArrays(1, &vao); } diff --git a/src/gui/opengl/qopenglvertexarrayobject.h b/src/gui/opengl/qopenglvertexarrayobject.h index 8369497660..19da85bffd 100644 --- a/src/gui/opengl/qopenglvertexarrayobject.h +++ b/src/gui/opengl/qopenglvertexarrayobject.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sean Harmer <sean.harmer@kdab.com> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. diff --git a/src/gui/opengl/qopenglvertexarrayobject_p.h b/src/gui/opengl/qopenglvertexarrayobject_p.h new file mode 100644 index 0000000000..161a388b1a --- /dev/null +++ b/src/gui/opengl/qopenglvertexarrayobject_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sean Harmer <sean.harmer@kdab.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLVERTEXARRAYOBJECT_P_H +#define QOPENGLVERTEXARRAYOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Qt OpenGL classes. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_OPENGL + +#include <QtGui/qopengl.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLVertexArrayObjectHelper; +class QOpenGLContext; + +void Q_GUI_EXPORT qtInitializeVertexArrayObjectHelper(QOpenGLVertexArrayObjectHelper *helper, QOpenGLContext *context); + +class QOpenGLVertexArrayObjectHelper +{ + Q_DISABLE_COPY(QOpenGLVertexArrayObjectHelper) + +public: + explicit inline QOpenGLVertexArrayObjectHelper(QOpenGLContext *context) + : GenVertexArrays(Q_NULLPTR) + , DeleteVertexArrays(Q_NULLPTR) + , BindVertexArray(Q_NULLPTR) + { + qtInitializeVertexArrayObjectHelper(this, context); + } + + inline bool isValid() const + { + return GenVertexArrays && DeleteVertexArrays && BindVertexArray; + } + + inline void glGenVertexArrays(GLsizei n, GLuint *arrays) const + { + GenVertexArrays(n, arrays); + } + + inline void glDeleteVertexArrays(GLsizei n, const GLuint *arrays) const + { + DeleteVertexArrays(n, arrays); + } + + inline void glBindVertexArray(GLuint array) const + { + BindVertexArray(array); + } + +private: + friend void Q_GUI_EXPORT qtInitializeVertexArrayObjectHelper(QOpenGLVertexArrayObjectHelper *helper, QOpenGLContext *context); + + // Function signatures are equivalent between desktop core, ARB, APPLE, ES 3 and ES 2 extensions + typedef void (QOPENGLF_APIENTRYP qt_GenVertexArrays_t)(GLsizei n, GLuint *arrays); + typedef void (QOPENGLF_APIENTRYP qt_DeleteVertexArrays_t)(GLsizei n, const GLuint *arrays); + typedef void (QOPENGLF_APIENTRYP qt_BindVertexArray_t)(GLuint array); + + qt_GenVertexArrays_t GenVertexArrays; + qt_DeleteVertexArrays_t DeleteVertexArrays; + qt_BindVertexArray_t BindVertexArray; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QOPENGLVERTEXARRAYOBJECT_P_H |