summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2014-07-23 14:55:09 +0200
committerAllan Sandfeld Jensen <allan.jensen@digia.com>2014-07-30 20:03:09 +0200
commitd702ba20e5edbdd4454fecd4b6e33705927f08a0 (patch)
tree197b4c6dba1f0bde0cf1ac344d68e773d0d50793
parent0437e1cef6aa2ed27362fc5c7c8df6609f13c9ee (diff)
Support RGB30 formats in OpenGL framebuffers and paint engine
This patch adds support for binding RGB30 images as textures, and as internal format of framebuffer objects. Together with the QOpenGLPaintDevice this provides support for rendering to and from RGB30 in full precision. [ChangeLog][QtGui][QOpenGLFramebufferObject] Support 10-bit per color channels formats as the internal framebuffer format, making it possible to render in that precision. Change-Id: I06de2d12dfe1c1adc466d574fdffbc77f88f4f16 Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp35
-rw-r--r--src/gui/opengl/qopengltexturecache.cpp31
-rw-r--r--tests/auto/gui/qopengl/tst_qopengl.cpp110
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;