diff options
-rw-r--r-- | src/gui/opengl/qopenglframebufferobject.cpp | 35 | ||||
-rw-r--r-- | src/gui/opengl/qopengltexturecache.cpp | 31 | ||||
-rw-r--r-- | tests/auto/gui/qopengl/tst_qopengl.cpp | 110 |
3 files changed, 172 insertions, 4 deletions
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 0c05b61e76..16094804c2 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -104,10 +104,18 @@ QT_BEGIN_NAMESPACE #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 @@ -116,6 +124,10 @@ QT_BEGIN_NAMESPACE #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 @@ -539,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(); @@ -550,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, @@ -1142,6 +1158,14 @@ static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool includ 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(); @@ -1152,6 +1176,10 @@ static QImage qt_gl_read_framebuffer(const QSize &size, GLenum 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: @@ -1177,7 +1205,8 @@ Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, 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 diff --git a/src/gui/opengl/qopengltexturecache.cpp b/src/gui/opengl/qopengltexturecache.cpp index 4a4a36d7e5..055974d5a4 100644 --- a/src/gui/opengl/qopengltexturecache.cpp +++ b/src/gui/opengl/qopengltexturecache.cpp @@ -49,6 +49,10 @@ QT_BEGIN_NAMESPACE +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + #ifndef GL_BGR #define GL_BGR 0x80E0 #endif @@ -61,6 +65,10 @@ QT_BEGIN_NAMESPACE #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: @@ -228,6 +236,29 @@ GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, con } 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: diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 972c2a7ea4..6d83defdeb 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -43,6 +43,7 @@ #include <QtGui/private/qopenglcontext_p.h> #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLFunctions_4_2_Core> #include <QtGui/QOpenGLVertexArrayObject> #include <QtGui/QOpenGLPaintDevice> #include <QtGui/QPainter> @@ -88,6 +89,8 @@ private slots: void fboTextureOwnership(); void fboRendering_data(); void fboRendering(); + void fboRenderingRGB30_data(); + void fboRenderingRGB30(); void fboHandleNulledAfterContextDestroyed(); void openGLPaintDevice_data(); void openGLPaintDevice(); @@ -563,7 +566,7 @@ void tst_QOpenGL::fboRendering() QOpenGLFramebufferObjectFormat fboFormat; fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - // Uncomplicate things by using NPOT: + // Uncomplicate things by using POT: const QSize size(256, 128); QOpenGLFramebufferObject fbo(size, fboFormat); @@ -587,6 +590,111 @@ void tst_QOpenGL::fboRendering() qt_opengl_check_test_pattern(fb); } +void tst_QOpenGL::fboRenderingRGB30_data() +{ + common_data(); +} + +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + +#ifndef GL_FRAMEBUFFER_RENDERABLE +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#endif + +#ifndef GL_FULL_SUPPORT +#define GL_FULL_SUPPORT 0x82B7 +#endif + +void tst_QOpenGL::fboRenderingRGB30() +{ +#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__) + QSKIP("QTBUG-22617"); +#endif + + QFETCH(int, surfaceClass); + QScopedPointer<QSurface> surface(createSurface(surfaceClass)); + + QOpenGLContext ctx; + QVERIFY(ctx.create()); + + QVERIFY(ctx.makeCurrent(surface.data())); + + if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QOpenGLFramebufferObject not supported on this platform"); + + if (ctx.format().majorVersion() < 3) + QSKIP("An internal RGB30_A2 format is not guaranteed on this platform"); + +#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) + // NVidia currently only supports RGB30 and RGB30_A2 in their Quadro drivers, + // but they do provide an extension for querying the support. We use the query + // in case they implement the required formats later. + if (!ctx.isOpenGLES() && ctx.format().majorVersion() >= 4) { + GLint value = -1; + QOpenGLFunctions_4_2_Core* vFuncs = ctx.versionFunctions<QOpenGLFunctions_4_2_Core>(); + if (vFuncs && vFuncs->initializeOpenGLFunctions()) { + vFuncs->glGetInternalformativ(GL_TEXTURE_2D, GL_RGB10_A2, GL_FRAMEBUFFER_RENDERABLE, 1, &value); + if (value != GL_FULL_SUPPORT) + QSKIP("The required RGB30_A2 format is not supported by this driver"); + } + } +#endif + + // No multisample with combined depth/stencil attachment: + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + fboFormat.setInternalTextureFormat(GL_RGB10_A2); + + // Uncomplicate things by using POT: + const QSize size(256, 128); + QOpenGLFramebufferObject fbo(size, fboFormat); + + if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil) + QSKIP("FBOs missing combined depth~stencil support"); + + QVERIFY(fbo.bind()); + + QPainter fboPainter; + QOpenGLPaintDevice device(fbo.width(), fbo.height()); + bool painterBegun = fboPainter.begin(&device); + QVERIFY(painterBegun); + + qt_opengl_draw_test_pattern(&fboPainter, fbo.width(), fbo.height()); + + fboPainter.end(); + + QImage fb = fbo.toImage(); + QCOMPARE(fb.format(), QImage::Format_A2BGR30_Premultiplied); + QCOMPARE(fb.size(), size); + + qt_opengl_check_test_pattern(fb); + + // Check rendering can handle color values below 1/256. + fboPainter.begin(&device); + fboPainter.fillRect(QRect(0, 0, 256, 128), QColor::fromRgbF(1.0/512, 1.0/512, 1.0/512)); + fboPainter.end(); + fb = fbo.toImage(); + uint pixel = ((uint*)fb.bits())[0]; + QVERIFY((pixel & 0x3f) > 0); + QVERIFY(((pixel >> 10) & 0x3f) > 0); + QVERIFY(((pixel >> 20) & 0x3f) > 0); + + pixel = (3U << 30) | (2U << 20) | (2U << 10) | 2U; + fb.fill(pixel); + + fboPainter.begin(&device); + fboPainter.setCompositionMode(QPainter::CompositionMode_Source); + fboPainter.drawImage(0, 0, fb); + fboPainter.end(); + fb = fbo.toImage(); + pixel = ((uint*)fb.bits())[0]; + QVERIFY((pixel & 0x3f) > 0); + QVERIFY(((pixel >> 10) & 0x3f) > 0); + QVERIFY(((pixel >> 20) & 0x3f) > 0); +} + void tst_QOpenGL::fboHandleNulledAfterContextDestroyed() { QWindow window; |