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>
This commit is contained in:
Allan Sandfeld Jensen 2014-07-23 14:55:09 +02:00
parent 0437e1cef6
commit d702ba20e5
3 changed files with 172 additions and 4 deletions

View File

@ -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

View File

@ -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:

View File

@ -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;