summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h28
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp32
-rw-r--r--src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp15
-rw-r--r--src/opengl/opengl.pro9
-rw-r--r--src/opengl/qgl.cpp25
-rw-r--r--src/opengl/qgl.h1
-rw-r--r--src/opengl/qgl_egl.cpp27
-rw-r--r--src/opengl/qgl_p.h3
-rw-r--r--src/opengl/qgl_qpa.cpp4
-rw-r--r--src/opengl/qgl_symbian.cpp162
-rw-r--r--src/opengl/qgltexturepool.cpp244
-rw-r--r--src/opengl/qgltexturepool_p.h147
-rw-r--r--src/opengl/qgraphicssystem_gl.cpp11
-rw-r--r--src/opengl/qgraphicssystem_gl_p.h4
-rw-r--r--src/opengl/qpixmapdata_gl_p.h51
-rw-r--r--src/opengl/qpixmapdata_poolgl.cpp934
-rw-r--r--src/opengl/qwindowsurface_gl.cpp124
-rw-r--r--src/opengl/qwindowsurface_gl_p.h2
19 files changed, 1668 insertions, 157 deletions
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 16a7d66..7cc9dc3 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -259,7 +259,7 @@ static const GLuint QT_PMV_MATRIX_3_ATTR = 5;
class QGLEngineShaderProg;
-class QGLEngineSharedShaders
+class Q_OPENGL_EXPORT QGLEngineSharedShaders
{
Q_GADGET
public:
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 1aacc96..fc8b9ef 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -148,7 +148,7 @@ static const char* const qglslAffinePositionWithPatternBrushVertexShader
= qglslPositionWithPatternBrushVertexShader;
static const char* const qglslPatternBrushSrcFragmentShader = "\n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
uniform lowp vec4 patternColor; \n\
varying highp vec2 patternTexCoords;\n\
lowp vec4 srcPixel() \n\
@@ -183,7 +183,7 @@ static const char* const qglslAffinePositionWithLinearGradientBrushVertexShader
= qglslPositionWithLinearGradientBrushVertexShader;
static const char* const qglslLinearGradientBrushSrcFragmentShader = "\n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
varying mediump float index; \n\
lowp vec4 srcPixel() \n\
{ \n\
@@ -218,7 +218,7 @@ static const char* const qglslAffinePositionWithConicalGradientBrushVertexShader
static const char* const qglslConicalGradientBrushSrcFragmentShader = "\n\
#define INVERSE_2PI 0.1591549430918953358 \n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
uniform mediump float angle; \n\
varying highp vec2 A; \n\
lowp vec4 srcPixel() \n\
@@ -260,7 +260,7 @@ static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader
= qglslPositionWithRadialGradientBrushVertexShader;
static const char* const qglslRadialGradientBrushSrcFragmentShader = "\n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
uniform highp float fmp2_m_radius2; \n\
uniform highp float inverse_2_fmp2_m_radius2; \n\
varying highp float b; \n\
@@ -304,14 +304,14 @@ static const char* const qglslAffinePositionWithTextureBrushVertexShader
// TODO: Special case POT textures which don't need this emulation
static const char* const qglslTextureBrushSrcFragmentShader = "\n\
varying highp vec2 brushTextureCoords; \n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
lowp vec4 srcPixel() { \n\
return texture2D(brushTexture, fract(brushTextureCoords)); \n\
}\n";
#else
static const char* const qglslTextureBrushSrcFragmentShader = "\n\
varying highp vec2 brushTextureCoords; \n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
return texture2D(brushTexture, brushTextureCoords); \n\
@@ -321,7 +321,7 @@ static const char* const qglslTextureBrushSrcFragmentShader = "\n\
static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\n\
varying highp vec2 brushTextureCoords; \n\
uniform lowp vec4 patternColor; \n\
- uniform lowp sampler2D brushTexture; \n\
+ uniform sampler2D brushTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \n\
@@ -337,7 +337,7 @@ static const char* const qglslSolidBrushSrcFragmentShader = "\n\
static const char* const qglslImageSrcFragmentShader = "\n\
varying highp vec2 textureCoords; \n\
- uniform lowp sampler2D imageTexture; \n\
+ uniform sampler2D imageTexture; \n\
lowp vec4 srcPixel() \n\
{ \n"
"return texture2D(imageTexture, textureCoords); \n"
@@ -345,7 +345,7 @@ static const char* const qglslImageSrcFragmentShader = "\n\
static const char* const qglslCustomSrcFragmentShader = "\n\
varying highp vec2 textureCoords; \n\
- uniform lowp sampler2D imageTexture; \n\
+ uniform sampler2D imageTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
return customShader(imageTexture, textureCoords); \n\
@@ -354,7 +354,7 @@ static const char* const qglslCustomSrcFragmentShader = "\n\
static const char* const qglslImageSrcWithPatternFragmentShader = "\n\
varying highp vec2 textureCoords; \n\
uniform lowp vec4 patternColor; \n\
- uniform lowp sampler2D imageTexture; \n\
+ uniform sampler2D imageTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
return patternColor * (1.0 - texture2D(imageTexture, textureCoords).r); \n\
@@ -362,7 +362,7 @@ static const char* const qglslImageSrcWithPatternFragmentShader = "\n\
static const char* const qglslNonPremultipliedImageSrcFragmentShader = "\n\
varying highp vec2 textureCoords; \n\
- uniform lowp sampler2D imageTexture; \n\
+ uniform sampler2D imageTexture; \n\
lowp vec4 srcPixel() \n\
{ \n\
lowp vec4 sample = texture2D(imageTexture, textureCoords); \n\
@@ -454,7 +454,7 @@ static const char* const qglslMainFragmentShader = "\n\
static const char* const qglslMaskFragmentShader = "\n\
varying highp vec2 textureCoords;\n\
- uniform lowp sampler2D maskTexture;\n\
+ uniform sampler2D maskTexture;\n\
lowp vec4 applyMask(lowp vec4 src) \n\
{\n\
lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\
@@ -478,7 +478,7 @@ static const char* const qglslMaskFragmentShader = "\n\
static const char* const qglslRgbMaskFragmentShaderPass1 = "\n\
varying highp vec2 textureCoords;\n\
- uniform lowp sampler2D maskTexture;\n\
+ uniform sampler2D maskTexture;\n\
lowp vec4 applyMask(lowp vec4 src) \n\
{ \n\
lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\
@@ -487,7 +487,7 @@ static const char* const qglslRgbMaskFragmentShaderPass1 = "\n\
static const char* const qglslRgbMaskFragmentShaderPass2 = "\n\
varying highp vec2 textureCoords;\n\
- uniform lowp sampler2D maskTexture;\n\
+ uniform sampler2D maskTexture;\n\
lowp vec4 applyMask(lowp vec4 src) \n\
{ \n\
lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index fa38b5d..ad77d7f 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -90,10 +90,6 @@
QT_BEGIN_NAMESPACE
-#if defined(Q_OS_SYMBIAN)
-#define QT_GL_NO_SCISSOR_TEST
-#endif
-
#if defined(Q_WS_WIN)
extern Q_GUI_EXPORT bool qt_cleartype_enabled;
#endif
@@ -1349,11 +1345,14 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c
ensureActive();
d->transferMode(ImageDrawingMode);
+ QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption|QGLContext::CanFlipNativePixmapBindOption;
+#ifdef QGL_USE_TEXTURE_POOL
+ bindOptions |= QGLContext::TemporarilyCachedBindOption;
+#endif
+
glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
QGLTexture *texture =
- ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA,
- QGLContext::InternalBindOption
- | QGLContext::CanFlipNativePixmapBindOption);
+ ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions);
GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top();
GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom();
@@ -1365,6 +1364,12 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c
d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
+
+ if (texture->options&QGLContext::TemporarilyCachedBindOption) {
+ // pixmap was temporarily cached as a QImage texture by pooling system
+ // and should be destroyed immediately
+ QGLTextureCache::instance()->remove(ctx, texture->id);
+ }
}
void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
@@ -1389,12 +1394,23 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const
glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
- QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
+ QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption;
+#ifdef QGL_USE_TEXTURE_POOL
+ bindOptions |= QGLContext::TemporarilyCachedBindOption;
+#endif
+
+ QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions);
GLuint id = texture->id;
d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
state()->renderHints & QPainter::SmoothPixmapTransform, id);
d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
+
+ if (texture->options&QGLContext::TemporarilyCachedBindOption) {
+ // image was temporarily cached by texture pooling system
+ // and should be destroyed immediately
+ QGLTextureCache::instance()->remove(ctx, texture->id);
+ }
}
void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem)
diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
index 4362c0a..9e8e828 100644
--- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
+++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp
@@ -339,8 +339,19 @@ void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed sub
// by converting it to a format with four bytes per pixel. Another is to copy one line at a
// time.
- for (int i = 0; i < maskHeight; ++i)
- glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
+ if (!ctx->d_ptr->workaround_brokenAlphaTexSubImage_init) {
+ // don't know which driver versions exhibit this bug, so be conservative for now
+ const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+ ctx->d_ptr->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0;
+ ctx->d_ptr->workaround_brokenAlphaTexSubImage_init = true;
+ }
+
+ if (ctx->d_ptr->workaround_brokenAlphaTexSubImage) {
+ for (int i = 0; i < maskHeight; ++i)
+ glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits());
+ }
}
}
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
index 08ae774..5774bef 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -157,11 +157,16 @@ embedded {
}
symbian {
+ DEFINES += QGL_USE_TEXTURE_POOL QGL_NO_PRESERVED_SWAP
+ SOURCES -= qpixmapdata_gl.cpp
SOURCES += qgl_symbian.cpp \
+ qpixmapdata_poolgl.cpp \
qglpixelbuffer_egl.cpp \
- qgl_egl.cpp
+ qgl_egl.cpp \
+ qgltexturepool.cpp
- HEADERS += qgl_egl_p.h
+ HEADERS += qgl_egl_p.h \
+ qgltexturepool_p.h
contains(QT_CONFIG, freetype) {
DEFINES += QT_NO_FONTCONFIG
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 76621e9..19858e7 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -97,9 +97,12 @@
#include "qlibrary.h"
#include <qmutex.h>
-#ifdef QT_OPENGL_ES
+#if defined(QT_OPENGL_ES) && !defined(QT_NO_EGL)
#include <EGL/egl.h>
#endif
+#ifdef QGL_USE_TEXTURE_POOL
+#include <private/qgltexturepool_p.h>
+#endif
// #define QT_GL_CONTEXT_RESOURCE_DEBUG
@@ -1735,6 +1738,9 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format)
workaround_brokenTextureFromPixmap = false;
workaround_brokenTextureFromPixmap_init = false;
+ workaround_brokenAlphaTexSubImage = false;
+ workaround_brokenAlphaTexSubImage_init = false;
+
for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
vertexAttributeArraysEnabledState[i] = false;
}
@@ -2033,6 +2039,10 @@ struct DDSFormat {
the pixmap/image that it stems from, e.g. installing destruction
hooks in them.
+ \omitvalue TemporarilyCachedBindOption Used by paint engines on some
+ platforms to indicate that the pixmap or image texture is possibly
+ cached only temporarily and must be destroyed immediately after the use.
+
\omitvalue InternalBindOption
*/
@@ -2537,8 +2547,18 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G
#endif
const QImage &constRef = img; // to avoid detach in bits()...
+#ifdef QGL_USE_TEXTURE_POOL
+ QGLTexturePool::instance()->createPermanentTexture(tx_id,
+ target,
+ 0, internalFormat,
+ img.width(), img.height(),
+ externalFormat,
+ pixel_type,
+ constRef.bits());
+#else
glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat,
pixel_type, constRef.bits());
+#endif
#if defined(QT_OPENGL_ES_2)
if (genMipmap)
glGenerateMipmap(target);
@@ -2577,7 +2597,6 @@ QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum targe
return 0;
}
-
/*! \internal */
QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, QGLContext::BindOptions options)
{
@@ -3310,8 +3329,10 @@ bool QGLContext::create(const QGLContext* shareContext)
QWidgetPrivate *wd = qt_widget_private(static_cast<QWidget *>(d->paintDevice));
wd->usesDoubleBufferedGLContext = d->glFormat.doubleBuffer();
}
+#ifndef Q_WS_QPA //We do this in choose context->setupSharing()
if (d->sharing) // ok, we managed to share
QGLContextGroup::addShare(this, shareContext);
+#endif
return d->valid;
}
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
index 9a51a77..ff73d88 100644
--- a/src/opengl/qgl.h
+++ b/src/opengl/qgl.h
@@ -333,6 +333,7 @@ public:
MemoryManagedBindOption = 0x0010, // internal flag
CanFlipNativePixmapBindOption = 0x0020, // internal flag
+ TemporarilyCachedBindOption = 0x0040, // internal flag
DefaultBindOption = LinearFilteringBindOption
| InvertedYBindOption
diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp
index 674d80d..d6b2d3b 100644
--- a/src/opengl/qgl_egl.cpp
+++ b/src/opengl/qgl_egl.cpp
@@ -39,6 +39,7 @@
**
****************************************************************************/
+#include <QtCore/qdebug.h>
#include <QtOpenGL/qgl.h>
#include <QtOpenGL/qglpixelbuffer.h>
#include "qgl_p.h"
@@ -195,6 +196,7 @@ void QGLContext::makeCurrent()
// PowerVR MBX/SGX chips needs to clear all buffers when starting to render
// a new frame, otherwise there will be a performance penalty to pay for
// each frame.
+ qDebug() << "Found SGX/MBX driver, enabling FullClearOnEveryFrame";
d->workaround_needsFullClearOnEveryFrame = true;
// Older PowerVR SGX drivers (like the one in the N900) have a
@@ -202,10 +204,31 @@ void QGLContext::makeCurrent()
// or GL_ALPHA texture bound to an FBO. The only way to
// identify that driver is to check the EGL version number for it.
const char *egl_version = eglQueryString(d->eglContext->display(), EGL_VERSION);
- if (egl_version && strstr(egl_version, "1.3"))
+
+ if (egl_version && strstr(egl_version, "1.3")) {
+ qDebug() << "Found v1.3 driver, enabling brokenFBOReadBack";
d->workaround_brokenFBOReadBack = true;
- else if (egl_version && strstr(egl_version, "1.4"))
+ } else if (egl_version && strstr(egl_version, "1.4")) {
+ qDebug() << "Found v1.4 driver, enabling brokenTexSubImage";
d->workaround_brokenTexSubImage = true;
+
+ // this is a bit complicated; 1.4 version SGX drivers from
+ // Nokia have fixed the brokenFBOReadBack problem, but
+ // official drivers from TI haven't, meaning that things
+ // like the beagleboard are broken unless we hack around it
+ // - but at the same time, we want to not reduce performance
+ // by not enabling this elsewhere.
+ //
+ // so, let's check for a Nokia-specific addon, and only
+ // enable if it isn't present.
+ // (see MeeGo bug #5616)
+ if (!QEgl::hasExtension("EGL_NOK_image_shared")) {
+ // no Nokia extension, this is probably a standard SGX
+ // driver, so enable the workaround
+ qDebug() << "Found non-Nokia v1.4 driver, enabling brokenFBOReadBack";
+ d->workaround_brokenFBOReadBack = true;
+ }
+ }
}
}
}
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
index 5508598..5a5e5cc 100644
--- a/src/opengl/qgl_p.h
+++ b/src/opengl/qgl_p.h
@@ -416,6 +416,9 @@ public:
uint workaround_brokenTextureFromPixmap : 1;
uint workaround_brokenTextureFromPixmap_init : 1;
+ uint workaround_brokenAlphaTexSubImage : 1;
+ uint workaround_brokenAlphaTexSubImage_init : 1;
+
#ifndef QT_NO_EGL
uint ownsEglContext : 1;
#endif
diff --git a/src/opengl/qgl_qpa.cpp b/src/opengl/qgl_qpa.cpp
index f7991b7..4bac1fb 100644
--- a/src/opengl/qgl_qpa.cpp
+++ b/src/opengl/qgl_qpa.cpp
@@ -324,20 +324,20 @@ void QGLWidget::setMouseTracking(bool enable)
bool QGLWidget::event(QEvent *e)
{
- Q_D(QGLWidget);
return QWidget::event(e);
}
void QGLWidget::resizeEvent(QResizeEvent *e)
{
Q_D(QGLWidget);
+
+ QWidget::resizeEvent(e);
if (!isValid())
return;
makeCurrent();
if (!d->glcx->initialized())
glInit();
resizeGL(width(), height());
- return QWidget::resizeEvent(e);
}
diff --git a/src/opengl/qgl_symbian.cpp b/src/opengl/qgl_symbian.cpp
index a9e2248..7caaabd 100644
--- a/src/opengl/qgl_symbian.cpp
+++ b/src/opengl/qgl_symbian.cpp
@@ -41,9 +41,7 @@
#include "qgl.h"
-#include <coemain.h>
-#include <coecntrl.h>
-#include <w32std.h>
+#include <fbs.h>
#include <private/qt_s60_p.h>
#include <private/qpixmap_s60_p.h>
#include <private/qimagepixmapcleanuphooks_p.h>
@@ -52,6 +50,7 @@
#include <private/qwidget_p.h> // to access QWExtra
#include "qgl_egl_p.h"
#include "qpixmapdata_gl_p.h"
+#include "qgltexturepool_p.h"
#include "qcolormap.h"
#include <QDebug>
@@ -71,6 +70,8 @@ QT_BEGIN_NAMESPACE
#endif
#endif
+extern int qt_gl_pixmap_serial;
+
/*
QGLTemporaryContext implementation
*/
@@ -227,13 +228,20 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) // almost same as
d->eglSurface = QEgl::createSurface(device(), d->eglContext->config());
-#if !defined(QGL_NO_PRESERVED_SWAP)
- eglGetError(); // Clear error state first.
- eglSurfaceAttrib(QEgl::display(), d->eglSurface,
- EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
- if (eglGetError() != EGL_SUCCESS) {
- qWarning("QGLContext: could not enable preserved swap");
- }
+ eglGetError(); // Clear error state first.
+
+#ifdef QGL_NO_PRESERVED_SWAP
+ eglSurfaceAttrib(QEgl::display(), d->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLContext: could not enable destroyed swap behaviour");
+#else
+ eglSurfaceAttrib(QEgl::display(), d->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLContext: could not enable preserved swap behaviour");
#endif
setWindowCreated(true);
@@ -360,117 +368,61 @@ void QGLWidgetPrivate::recreateEglSurface()
eglSurfaceWindowId = currentId;
}
-/*
- * Symbian specific QGLPixmapData functions
- */
-
-static CFbsBitmap* createBlitCopy(CFbsBitmap* bitmap)
+static inline bool knownGoodFormat(QImage::Format format)
{
- CFbsBitmap *copy = q_check_ptr(new CFbsBitmap);
- if (!copy)
- return 0;
-
- if (copy->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) {
- delete copy;
- copy = 0;
-
- return 0;
+ switch (format) {
+ case QImage::Format_RGB16: // EColor64K
+ case QImage::Format_RGB32: // EColor16MU
+ case QImage::Format_ARGB32_Premultiplied: // EColor16MAP
+ return true;
+ default:
+ return false;
}
-
- CFbsBitmapDevice* bitmapDevice = 0;
- CFbsBitGc *bitmapGc = 0;
- QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(copy));
- QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
- bitmapGc->Activate(bitmapDevice);
-
- bitmapGc->BitBlt(TPoint(), bitmap);
-
- delete bitmapGc;
- delete bitmapDevice;
-
- return copy;
}
void QGLPixmapData::fromNativeType(void* pixmap, NativeType type)
{
if (type == QPixmapData::FbsBitmap) {
- CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap);
-
- bool deleteSourceBitmap = false;
-#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE
-
- // Rasterize extended bitmaps
-
- TUid extendedBitmapType = bitmap->ExtendedBitmapType();
- if (extendedBitmapType != KNullUid) {
- bitmap = createBlitCopy(bitmap);
- deleteSourceBitmap = true;
- }
-#endif
-
- if (bitmap->IsCompressedInRAM()) {
- bitmap = createBlitCopy(bitmap);
- deleteSourceBitmap = true;
- }
-
- TDisplayMode displayMode = bitmap->DisplayMode();
- QImage::Format format = qt_TDisplayMode2Format(displayMode);
-
- TSize size = bitmap->SizeInPixels();
- int bytesPerLine = bitmap->ScanLineLength(size.iWidth, displayMode);
-
- bitmap->BeginDataAccess();
- uchar *bytes = (uchar*)bitmap->DataAddress();
- QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format);
- img = img.copy();
- bitmap->EndDataAccess();
-
- if (displayMode == EGray2) {
- //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
- //So invert mono bitmaps so that masks work correctly.
- img.invertPixels();
- } else if (displayMode == EColor16M) {
- img = img.rgbSwapped(); // EColor16M is BGR
+ CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap *>(pixmap);
+ QSize size(bitmap->SizeInPixels().iWidth, bitmap->SizeInPixels().iHeight);
+ if (size.width() == w && size.height() == h)
+ setSerialNumber(++qt_gl_pixmap_serial);
+ resize(size.width(), size.height());
+ m_source = QVolatileImage(bitmap);
+ if (pixelType() == BitmapType) {
+ m_source.ensureFormat(QImage::Format_MonoLSB);
+ } else if (!knownGoodFormat(m_source.format())) {
+ m_source.beginDataAccess();
+ QImage::Format format = idealFormat(m_source.imageRef(), Qt::AutoColor);
+ m_source.endDataAccess(true);
+ m_source.ensureFormat(format);
}
-
- fromImage(img, Qt::AutoColor);
-
- if (deleteSourceBitmap)
- delete bitmap;
+ m_hasAlpha = m_source.hasAlphaChannel();
+ m_hasFillColor = false;
+ m_dirty = true;
+
+ } else if (type == QPixmapData::VolatileImage && pixmap) {
+ // Support QS60Style in more efficient skin graphics retrieval.
+ QVolatileImage *img = static_cast<QVolatileImage *>(pixmap);
+ if (img->width() == w && img->height() == h)
+ setSerialNumber(++qt_gl_pixmap_serial);
+ resize(img->width(), img->height());
+ m_source = *img;
+ m_hasAlpha = m_source.hasAlphaChannel();
+ m_hasFillColor = false;
+ m_dirty = true;
}
}
void* QGLPixmapData::toNativeType(NativeType type)
{
if (type == QPixmapData::FbsBitmap) {
- CFbsBitmap *bitmap = q_check_ptr(new CFbsBitmap);
-
- if (bitmap) {
- QImage image = toImage();
-
- TDisplayMode displayMode(EColor16MU);
- if (image.format()==QImage::Format_ARGB32_Premultiplied)
- displayMode = EColor16MAP;
-
- if (bitmap->Create(TSize(image.width(), image.height()),
- displayMode) == KErrNone) {
- const uchar *sptr = const_cast<const QImage&>(image).bits();
- bitmap->BeginDataAccess();
-
- uchar *dptr = (uchar*)bitmap->DataAddress();
- Mem::Copy(dptr, sptr, image.byteCount());
-
- bitmap->EndDataAccess();
- } else {
- delete bitmap;
- bitmap = 0;
- }
- }
-
- return reinterpret_cast<void*>(bitmap);
+ if (m_source.isNull())
+ m_source = QVolatileImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ return m_source.duplicateNativeImage();
}
+
return 0;
}
QT_END_NAMESPACE
-
diff --git a/src/opengl/qgltexturepool.cpp b/src/opengl/qgltexturepool.cpp
new file mode 100644
index 0000000..a5472ec
--- /dev/null
+++ b/src/opengl/qgltexturepool.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgltexturepool_p.h"
+#include "qpixmapdata_gl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget();
+
+static QGLTexturePool *qt_gl_texture_pool = 0;
+
+class QGLTexturePoolPrivate
+{
+public:
+ QGLTexturePoolPrivate() : lruFirst(0), lruLast(0) {}
+
+ QGLPixmapData *lruFirst;
+ QGLPixmapData *lruLast;
+};
+
+QGLTexturePool::QGLTexturePool()
+ : d_ptr(new QGLTexturePoolPrivate())
+{
+}
+
+QGLTexturePool::~QGLTexturePool()
+{
+}
+
+QGLTexturePool *QGLTexturePool::instance()
+{
+ if (!qt_gl_texture_pool)
+ qt_gl_texture_pool = new QGLTexturePool();
+ return qt_gl_texture_pool;
+}
+
+GLuint QGLTexturePool::createTextureForPixmap(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data)
+{
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(target, texture);
+ do {
+ glTexImage2D(target, level, internalformat, width, height, 0, format, type, 0);
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) {
+ if (data)
+ moveToHeadOfLRU(data);
+ return texture;
+ } else if (error != GL_OUT_OF_MEMORY) {
+ qWarning("QGLTexturePool: cannot create temporary texture because of invalid params");
+ return 0;
+ }
+ } while (reclaimSpace(internalformat, width, height, format, type, data));
+ qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap",
+ width, height);
+ return 0;
+}
+
+bool QGLTexturePool::createPermanentTexture(GLuint texture,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data)
+{
+ glBindTexture(target, texture);
+ do {
+ glTexImage2D(target, level, internalformat, width, height, 0, format, type, data);
+
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) {
+ return true;
+ } else if (error != GL_OUT_OF_MEMORY) {
+ qWarning("QGLTexturePool: cannot create permanent texture because of invalid params");
+ return false;
+ }
+ } while (reclaimSpace(internalformat, width, height, format, type, 0));
+ qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap",
+ width, height);
+ return 0;
+}
+
+void QGLTexturePool::releaseTexture(QGLPixmapData *data, GLuint texture)
+{
+ // Very simple strategy at the moment: just destroy the texture.
+ if (data)
+ removeFromLRU(data);
+
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (shareWidget) {
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &texture);
+ }
+}
+
+void QGLTexturePool::useTexture(QGLPixmapData *data)
+{
+ moveToHeadOfLRU(data);
+}
+
+void QGLTexturePool::detachTexture(QGLPixmapData *data)
+{
+ removeFromLRU(data);
+}
+
+bool QGLTexturePool::reclaimSpace(GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data)
+{
+ Q_UNUSED(internalformat); // For future use in picking the best texture to eject.
+ Q_UNUSED(width);
+ Q_UNUSED(height);
+ Q_UNUSED(format);
+ Q_UNUSED(type);
+
+ bool succeeded = false;
+ bool wasInLRU = false;
+ if (data) {
+ wasInLRU = data->inLRU;
+ moveToHeadOfLRU(data);
+ }
+
+ QGLPixmapData *lrudata = pixmapLRU();
+ if (lrudata && lrudata != data) {
+ lrudata->reclaimTexture();
+ succeeded = true;
+ }
+
+ if (data && !wasInLRU)
+ removeFromLRU(data);
+
+ return succeeded;
+}
+
+void QGLTexturePool::hibernate()
+{
+ Q_D(QGLTexturePool);
+ QGLPixmapData *pd = d->lruLast;
+ while (pd) {
+ QGLPixmapData *prevLRU = pd->prevLRU;
+ pd->inTexturePool = false;
+ pd->inLRU = false;
+ pd->nextLRU = 0;
+ pd->prevLRU = 0;
+ pd->hibernate();
+ pd = prevLRU;
+ }
+ d->lruFirst = 0;
+ d->lruLast = 0;
+}
+
+void QGLTexturePool::moveToHeadOfLRU(QGLPixmapData *data)
+{
+ Q_D(QGLTexturePool);
+ if (data->inLRU) {
+ if (!data->prevLRU)
+ return; // Already at the head of the list.
+ removeFromLRU(data);
+ }
+ data->inLRU = true;
+ data->nextLRU = d->lruFirst;
+ data->prevLRU = 0;
+ if (d->lruFirst)
+ d->lruFirst->prevLRU = data;
+ else
+ d->lruLast = data;
+ d->lruFirst = data;
+}
+
+void QGLTexturePool::removeFromLRU(QGLPixmapData *data)
+{
+ Q_D(QGLTexturePool);
+ if (!data->inLRU)
+ return;
+ if (data->nextLRU)
+ data->nextLRU->prevLRU = data->prevLRU;
+ else
+ d->lruLast = data->prevLRU;
+ if (data->prevLRU)
+ data->prevLRU->nextLRU = data->nextLRU;
+ else
+ d->lruFirst = data->nextLRU;
+ data->inLRU = false;
+}
+
+QGLPixmapData *QGLTexturePool::pixmapLRU()
+{
+ Q_D(QGLTexturePool);
+ return d->lruLast;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgltexturepool_p.h b/src/opengl/qgltexturepool_p.h
new file mode 100644
index 0000000..8b6f726
--- /dev/null
+++ b/src/opengl/qgltexturepool_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenVG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLTEXTUREPOOL_P_H
+#define QGLTEXTUREPOOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgl.h"
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLPixmapData;
+class QGLTexturePoolPrivate;
+
+class QGLTexturePool
+{
+public:
+ QGLTexturePool();
+ virtual ~QGLTexturePool();
+
+ static QGLTexturePool *instance();
+
+ // Create a new texture with the specified parameters and associate
+ // it with "data". The QGLPixmapData will be notified when the
+ // texture needs to be reclaimed by the pool.
+ //
+ // This function will call reclaimSpace() when texture creation fails.
+ GLuint createTextureForPixmap(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data);
+
+ // Create a permanent texture with the specified parameters.
+ // If there is insufficient space for the texture,
+ // then this function will call reclaimSpace() and try again.
+ //
+ // The caller is responsible for calling glDeleteTextures()
+ // when it no longer needs the texture, as the texture is not
+ // recorded in the texture pool.
+ bool createPermanentTexture(GLuint texture,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data);
+
+ // Release a texture that is no longer required.
+ void releaseTexture(QGLPixmapData *data, GLuint texture);
+
+ // Notify the pool that a QGLPixmapData object is using
+ // an texture again. This allows the pool to move the texture
+ // within a least-recently-used list of QGLPixmapData objects.
+ void useTexture(QGLPixmapData *data);
+
+ // Notify the pool that the texture associated with a
+ // QGLPixmapData is being detached from the pool. The caller
+ // will become responsible for calling glDeleteTextures().
+ void detachTexture(QGLPixmapData *data);
+
+ // Reclaim space for an image allocation with the specified parameters.
+ // Returns true if space was reclaimed, or false if there is no
+ // further space that can be reclaimed. The "data" parameter
+ // indicates the pixmap that is trying to obtain space which should
+ // not itself be reclaimed.
+ bool reclaimSpace(GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ QGLPixmapData *data);
+
+ // Hibernate the image pool because the context is about to be
+ // destroyed. All textures left in the pool should be released.
+ void hibernate();
+
+protected:
+ // Helper functions for managing the LRU list of QGLPixmapData objects.
+ void moveToHeadOfLRU(QGLPixmapData *data);
+ void removeFromLRU(QGLPixmapData *data);
+ QGLPixmapData *pixmapLRU();
+
+private:
+ QScopedPointer<QGLTexturePoolPrivate> d_ptr;
+
+ Q_DECLARE_PRIVATE(QGLTexturePool)
+ Q_DISABLE_COPY(QGLTexturePool)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp
index 3574756..0aa3c2e 100644
--- a/src/opengl/qgraphicssystem_gl.cpp
+++ b/src/opengl/qgraphicssystem_gl.cpp
@@ -57,6 +57,10 @@
#include <QtGui/private/qapplication_p.h>
#endif
+#ifdef QGL_USE_TEXTURE_POOL
+#include "private/qgltexturepool_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
extern QGLWidget *qt_gl_getShareWidget();
@@ -100,6 +104,11 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const
return new QGLWindowSurface(widget);
}
-
+#ifdef QGL_USE_TEXTURE_POOL
+void QGLGraphicsSystem::releaseCachedResources()
+{
+ QGLTexturePool::instance()->hibernate();
+}
+#endif
QT_END_NAMESPACE
diff --git a/src/opengl/qgraphicssystem_gl_p.h b/src/opengl/qgraphicssystem_gl_p.h
index 4630da1..5829dcc 100644
--- a/src/opengl/qgraphicssystem_gl_p.h
+++ b/src/opengl/qgraphicssystem_gl_p.h
@@ -66,6 +66,10 @@ public:
QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
QWindowSurface *createWindowSurface(QWidget *widget) const;
+
+#ifdef QGL_USE_TEXTURE_POOL
+ void releaseCachedResources();
+#endif
private:
bool m_useX11GL;
};
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
index a4066fd..41740dd 100644
--- a/src/opengl/qpixmapdata_gl_p.h
+++ b/src/opengl/qpixmapdata_gl_p.h
@@ -59,6 +59,10 @@
#include "private/qpixmapdata_p.h"
#include "private/qglpaintdevice_p.h"
+#ifdef Q_OS_SYMBIAN
+#include "private/qvolatileimage_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
class QPaintEngine;
@@ -66,6 +70,12 @@ class QGLFramebufferObject;
class QGLFramebufferObjectFormat;
class QGLPixmapData;
+#ifdef QGL_USE_TEXTURE_POOL
+void qt_gl_register_pixmap(QGLPixmapData *pd);
+void qt_gl_unregister_pixmap(QGLPixmapData *pd);
+void qt_gl_hibernate_pixmaps();
+#endif
+
class QGLFramebufferObjectPool
{
public:
@@ -129,7 +139,25 @@ public:
GLuint bind(bool copyBack = true) const;
QGLTexture *texture() const;
-#if defined(Q_OS_SYMBIAN)
+#ifdef QGL_USE_TEXTURE_POOL
+ void destroyTexture();
+ // Detach this image from the image pool.
+ void detachTextureFromPool();
+ // Release the GL resources associated with this pixmap and copy
+ // the pixmap's contents out of the GPU back into main memory.
+ // The GL resource will be automatically recreated the next time
+ // ensureCreated() is called. Does nothing if the pixmap cannot be
+ // hibernated for some reason (e.g. texture is shared with another
+ // process via a SgImage).
+ void hibernate();
+ // Called when the QGLTexturePool wants to reclaim this pixmap's
+ // texture objects to reuse storage.
+ void reclaimTexture();
+ void forceToImage();
+#endif
+
+#ifdef Q_OS_SYMBIAN
+ QImage::Format idealFormat(QImage &image, Qt::ImageConversionFlags flags);
void* toNativeType(NativeType type);
void fromNativeType(void* pixmap, NativeType type);
#endif
@@ -161,7 +189,11 @@ private:
mutable QGLFramebufferObject *m_renderFbo;
mutable QPaintEngine *m_engine;
mutable QGLContext *m_ctx;
+#ifdef Q_OS_SYMBIAN
+ mutable QVolatileImage m_source;
+#else
mutable QImage m_source;
+#endif
mutable QGLTexture m_texture;
// the texture is not in sync with the source image
@@ -176,6 +208,23 @@ private:
mutable QGLPixmapGLPaintDevice m_glDevice;
+#ifdef QGL_USE_TEXTURE_POOL
+ QGLPixmapData *nextLRU;
+ QGLPixmapData *prevLRU;
+ mutable bool inLRU;
+ mutable bool failedToAlloc;
+ mutable bool inTexturePool;
+
+ QGLPixmapData *next;
+ QGLPixmapData *prev;
+
+ friend class QGLTexturePool;
+
+ friend void qt_gl_register_pixmap(QGLPixmapData *pd);
+ friend void qt_gl_unregister_pixmap(QGLPixmapData *pd);
+ friend void qt_gl_hibernate_pixmaps();
+#endif
+
friend class QGLPixmapGLPaintDevice;
friend class QMeeGoPixmapData;
friend class QMeeGoLivePixmapData;
diff --git a/src/opengl/qpixmapdata_poolgl.cpp b/src/opengl/qpixmapdata_poolgl.cpp
new file mode 100644
index 0000000..64de29e
--- /dev/null
+++ b/src/opengl/qpixmapdata_poolgl.cpp
@@ -0,0 +1,934 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpixmap.h"
+#include "qglframebufferobject.h"
+
+#include <private/qpaintengine_raster_p.h>
+
+#include "qpixmapdata_gl_p.h"
+
+#include <private/qgl_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+
+#include <private/qpaintengineex_opengl2_p.h>
+
+#include <qdesktopwidget.h>
+#include <qfile.h>
+#include <qimagereader.h>
+#include <qbuffer.h>
+
+#include "qgltexturepool_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget();
+
+/*!
+ \class QGLFramebufferObjectPool
+ \since 4.6
+
+ \brief The QGLFramebufferObject class provides a pool of framebuffer
+ objects for offscreen rendering purposes.
+
+ When requesting an FBO of a given size and format, an FBO of the same
+ format and a size at least as big as the requested size will be returned.
+
+ \internal
+*/
+
+static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
+{
+ return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
+}
+
+extern int qt_next_power_of_two(int v);
+
+static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
+{
+#ifdef QT_OPENGL_ES_2
+ QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
+ if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
+ return rounded;
+#endif
+ return sz;
+}
+
+
+QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize)
+{
+ QGLFramebufferObject *chosen = 0;
+ QGLFramebufferObject *candidate = 0;
+ for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
+ QGLFramebufferObject *fbo = m_fbos.at(i);
+
+ if (strictSize) {
+ if (fbo->size() == requestSize && fbo->format() == requestFormat) {
+ chosen = fbo;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (fbo->format() == requestFormat) {
+ // choose the fbo with a matching format and the closest size
+ if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
+ candidate = fbo;
+ }
+
+ if (candidate) {
+ m_fbos.removeOne(candidate);
+
+ const QSize fboSize = candidate->size();
+ QSize sz = fboSize;
+
+ if (sz.width() < requestSize.width())
+ sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
+ if (sz.height() < requestSize.height())
+ sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
+
+ // wasting too much space?
+ if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4)
+ sz = requestSize;
+
+ if (sz != fboSize) {
+ delete candidate;
+ candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
+ }
+
+ chosen = candidate;
+ }
+ }
+
+ if (!chosen) {
+ if (strictSize)
+ chosen = new QGLFramebufferObject(requestSize, requestFormat);
+ else
+ chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
+ }
+
+ if (!chosen->isValid()) {
+ delete chosen;
+ chosen = 0;
+ }
+
+ return chosen;
+}
+
+void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
+{
+ if (fbo)
+ m_fbos << fbo;
+}
+
+
+QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const
+{
+ return data->paintEngine();
+}
+
+void QGLPixmapGLPaintDevice::beginPaint()
+{
+ if (!data->isValid())
+ return;
+
+ // QGLPaintDevice::beginPaint will store the current binding and replace
+ // it with m_thisFBO:
+ m_thisFBO = data->m_renderFbo->handle();
+ QGLPaintDevice::beginPaint();
+
+ Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2);
+
+ // QPixmap::fill() is deferred until now, where we actually need to do the fill:
+ if (data->needsFill()) {
+ const QColor &c = data->fillColor();
+ float alpha = c.alphaF();
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ else if (!data->isUninitialized()) {
+ // If the pixmap (GL Texture) has valid content (it has been
+ // uploaded from an image or rendered into before), we need to
+ // copy it from the texture to the render FBO.
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+
+#if !defined(QT_OPENGL_ES_2)
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, data->width(), data->height(), 0, -999999, 999999);
+#endif
+
+ glViewport(0, 0, data->width(), data->height());
+
+ // Pass false to bind so it doesn't copy the FBO into the texture!
+ context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false));
+ }
+}
+
+void QGLPixmapGLPaintDevice::endPaint()
+{
+ if (!data->isValid())
+ return;
+
+ data->copyBackFromRenderFbo(false);
+
+ // Base's endPaint will restore the previous FBO binding
+ QGLPaintDevice::endPaint();
+
+ qgl_fbo_pool()->release(data->m_renderFbo);
+ data->m_renderFbo = 0;
+}
+
+QGLContext* QGLPixmapGLPaintDevice::context() const
+{
+ data->ensureCreated();
+ return data->m_ctx;
+}
+
+QSize QGLPixmapGLPaintDevice::size() const
+{
+ return data->size();
+}
+
+bool QGLPixmapGLPaintDevice::alphaRequested() const
+{
+ return data->m_hasAlpha;
+}
+
+void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d)
+{
+ data = d;
+}
+
+int qt_gl_pixmap_serial = 0;
+
+QGLPixmapData::QGLPixmapData(PixelType type)
+ : QPixmapData(type, OpenGLClass)
+ , m_renderFbo(0)
+ , m_engine(0)
+ , m_ctx(0)
+ , m_dirty(false)
+ , m_hasFillColor(false)
+ , m_hasAlpha(false)
+ , inLRU(false)
+ , failedToAlloc(false)
+ , inTexturePool(false)
+{
+ setSerialNumber(++qt_gl_pixmap_serial);
+ m_glDevice.setPixmapData(this);
+
+ qt_gl_register_pixmap(this);
+}
+
+QGLPixmapData::~QGLPixmapData()
+{
+ delete m_engine;
+
+ destroyTexture();
+ qt_gl_unregister_pixmap(this);
+}
+
+void QGLPixmapData::destroyTexture()
+{
+ if (inTexturePool) {
+ QGLTexturePool *pool = QGLTexturePool::instance();
+ if (m_texture.id)
+ pool->releaseTexture(this, m_texture.id);
+ } else {
+ if (m_texture.id) {
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (shareWidget) {
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &m_texture.id);
+ }
+ }
+ }
+ m_texture.id = 0;
+ inTexturePool = false;
+}
+
+QPixmapData *QGLPixmapData::createCompatiblePixmapData() const
+{
+ return new QGLPixmapData(pixelType());
+}
+
+bool QGLPixmapData::isValid() const
+{
+ return w > 0 && h > 0;
+}
+
+bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
+{
+ if (ctx == m_ctx)
+ return true;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
+}
+
+void QGLPixmapData::resize(int width, int height)
+{
+ if (width == w && height == h)
+ return;
+
+ if (width <= 0 || height <= 0) {
+ width = 0;
+ height = 0;
+ }
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+ d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
+
+ destroyTexture();
+
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ setSerialNumber(++qt_gl_pixmap_serial);
+}
+
+void QGLPixmapData::ensureCreated() const
+{
+ if (!m_dirty)
+ return;
+
+ m_dirty = false;
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ m_ctx = ctx;
+
+ const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB;
+#ifdef QT_OPENGL_ES_2
+ const GLenum external_format = internal_format;
+#else
+ const GLenum external_format = qt_gl_preferredTextureFormat();
+#endif
+ const GLenum target = GL_TEXTURE_2D;
+
+ GLenum type = GL_UNSIGNED_BYTE;
+ // Avoid conversion when pixmap is created from CFbsBitmap of EColor64K.
+ if (!m_source.isNull() && m_source.format() == QImage::Format_RGB16)
+ type = GL_UNSIGNED_SHORT_5_6_5;
+
+ m_texture.options &= ~QGLContext::MemoryManagedBindOption;
+
+ if (!m_texture.id) {
+ m_texture.id = QGLTexturePool::instance()->createTextureForPixmap(
+ target,
+ 0, internal_format,
+ w, h,
+ external_format,
+ type,
+ const_cast<QGLPixmapData*>(this));
+ if (!m_texture.id) {
+ failedToAlloc = true;
+ return;
+ }
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ inTexturePool = true;
+ } else if (inTexturePool) {
+ glBindTexture(target, m_texture.id);
+ QGLTexturePool::instance()->useTexture(const_cast<QGLPixmapData*>(this));
+ }
+
+ if (!m_source.isNull() && m_texture.id) {
+ if (external_format == GL_RGB) {
+ m_source.beginDataAccess();
+ QImage tx;
+ if (type == GL_UNSIGNED_BYTE)
+ tx = m_source.imageRef().convertToFormat(QImage::Format_RGB888).mirrored(false, true);
+ else if (type == GL_UNSIGNED_SHORT_5_6_5)
+ tx = m_source.imageRef().mirrored(false, true);
+ m_source.endDataAccess(true);
+
+ glBindTexture(target, m_texture.id);
+ if (!tx.isNull())
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ type, tx.constBits());
+ else
+ qWarning("QGLPixmapData: Failed to create GL_RGB image of size %dx%d", w, h);
+ } else {
+ // do byte swizzling ARGB -> RGBA
+ m_source.beginDataAccess();
+ const QImage tx = ctx->d_func()->convertToGLFormat(m_source.imageRef(), true, external_format);
+ m_source.endDataAccess(true);
+ glBindTexture(target, m_texture.id);
+ if (!tx.isNull())
+ glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
+ type, tx.constBits());
+ else
+ qWarning("QGLPixmapData: Failed to create GL_RGBA image of size %dx%d", w, h);
+ }
+
+ if (useFramebufferObjects())
+ m_source = QVolatileImage();
+ }
+}
+
+
+void QGLPixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags)
+{
+ QImage img = image;
+ createPixmapForImage(img, flags, false);
+}
+
+void QGLPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
+
+ createPixmapForImage(image, flags, true);
+}
+
+bool QGLPixmapData::fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ if (pixelType() == QPixmapData::BitmapType)
+ return QPixmapData::fromFile(filename, format, flags);
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ QByteArray data = file.peek(64);
+ bool alpha;
+ if (m_texture.canBindCompressedTexture
+ (data.constData(), data.size(), format, &alpha)) {
+ resize(0, 0);
+ data = file.readAll();
+ file.close();
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ QSize size = m_texture.bindCompressedTexture
+ (data.constData(), data.size(), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ QImage image = QImageReader(filename, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ bool alpha;
+ const char *buf = reinterpret_cast<const char *>(buffer);
+ if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) {
+ resize(0, 0);
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ QSize size = m_texture.bindCompressedTexture(buf, int(len), format);
+ if (!size.isEmpty()) {
+ w = size.width();
+ h = size.height();
+ is_null = false;
+ d = 32;
+ m_hasAlpha = alpha;
+ m_source = QVolatileImage();
+ m_dirty = isValid();
+ return true;
+ }
+ }
+
+ QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
+ QBuffer b(&a);
+ b.open(QIODevice::ReadOnly);
+ QImage image = QImageReader(&b, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
+ return !isNull();
+}
+
+QImage::Format QGLPixmapData::idealFormat(QImage &image, Qt::ImageConversionFlags flags)
+{
+ QImage::Format format = QImage::Format_RGB32;
+ if (qApp->desktop()->depth() == 16)
+ format = QImage::Format_RGB16;
+
+ if (image.hasAlphaChannel()
+ && ((flags & Qt::NoOpaqueDetection)
+ || const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()))
+ format = QImage::Format_ARGB32_Premultiplied;
+
+ return format;
+}
+
+void QGLPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
+{
+ if (image.size() == QSize(w, h))
+ setSerialNumber(++qt_gl_pixmap_serial);
+
+ resize(image.width(), image.height());
+
+ if (pixelType() == BitmapType) {
+ QImage convertedImage = image.convertToFormat(QImage::Format_MonoLSB);
+ if (image.format() == QImage::Format_MonoLSB)
+ convertedImage.detach();
+
+ m_source = QVolatileImage(convertedImage);
+
+ } else {
+ QImage::Format format = idealFormat(image, flags);
+
+ if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
+ m_source = QVolatileImage(image);
+ } else {
+ QImage convertedImage = image.convertToFormat(format);
+
+ // convertToFormat won't detach the image if format stays the same.
+ if (image.format() == format)
+ convertedImage.detach();
+
+ m_source = QVolatileImage(convertedImage);
+ }
+ }
+
+ m_dirty = true;
+ m_hasFillColor = false;
+
+ m_hasAlpha = m_source.hasAlphaChannel();
+ w = image.width();
+ h = image.height();
+ is_null = (w <= 0 || h <= 0);
+ d = m_source.depth();
+
+ destroyTexture();
+}
+
+bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+ Q_UNUSED(rect);
+ return false;
+}
+
+void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->classId() != QPixmapData::OpenGLClass || !static_cast<const QGLPixmapData *>(data)->useFramebufferObjects()) {
+ QPixmapData::copy(data, rect);
+ return;
+ }
+
+ const QGLPixmapData *other = static_cast<const QGLPixmapData *>(data);
+ if (other->m_renderFbo) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ resize(rect.width(), rect.height());
+ m_hasAlpha = other->m_hasAlpha;
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ if (!other->m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, other->m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+ if (ctx->d_ptr->active_engine && ctx->d_ptr->active_engine->type() == QPaintEngine::OpenGL2)
+ static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine)->invalidateState();
+
+ glBlitFramebufferEXT(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(),
+ 0, 0, w, h,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ QPixmapData::copy(data, rect);
+ }
+}
+
+void QGLPixmapData::fill(const QColor &color)
+{
+ if (!isValid())
+ return;
+
+ bool hasAlpha = color.alpha() != 255;
+ if (hasAlpha && !m_hasAlpha) {
+ if (m_texture.id) {
+ destroyTexture();
+ m_dirty = true;
+ }
+ m_hasAlpha = color.alpha() != 255;
+ }
+
+ if (useFramebufferObjects()) {
+ m_source = QVolatileImage();
+ m_hasFillColor = true;
+ m_fillColor = color;
+ } else {
+ forceToImage();
+
+ if (m_source.depth() == 32) {
+ m_source.fill(PREMUL(color.rgba()));
+
+ } else if (m_source.depth() == 1) {
+ if (color == Qt::color1)
+ m_source.fill(1);
+ else
+ m_source.fill(0);
+ }
+ }
+}
+
+bool QGLPixmapData::hasAlphaChannel() const
+{
+ return m_hasAlpha;
+}
+
+QImage QGLPixmapData::fillImage(const QColor &color) const
+{
+ QImage img;
+ if (pixelType() == BitmapType) {
+ img = QImage(w, h, QImage::Format_MonoLSB);
+
+ img.setColorCount(2);
+ img.setColor(0, QColor(Qt::color0).rgba());
+ img.setColor(1, QColor(Qt::color1).rgba());
+
+ if (color == Qt::color1)
+ img.fill(1);
+ else
+ img.fill(0);
+ } else {
+ img = QImage(w, h,
+ m_hasAlpha
+ ? QImage::Format_ARGB32_Premultiplied
+ : QImage::Format_RGB32);
+ img.fill(PREMUL(color.rgba()));
+ }
+ return img;
+}
+
+extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+
+QImage QGLPixmapData::toImage() const
+{
+ if (!isValid())
+ return QImage();
+
+ if (m_renderFbo) {
+ copyBackFromRenderFbo(true);
+ } else if (!m_source.isNull()) {
+ // QVolatileImage::toImage() will make a copy always so no check
+ // for active painting is needed.
+ QImage img = m_source.toImage();
+ if (img.format() == QImage::Format_MonoLSB) {
+ img.setColorCount(2);
+ img.setColor(0, QColor(Qt::color0).rgba());
+ img.setColor(1, QColor(Qt::color1).rgba());
+ }
+ return img;
+ } else if (m_dirty || m_hasFillColor) {
+ return fillImage(m_fillColor);
+ } else {
+ ensureCreated();
+ }
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glBindTexture(GL_TEXTURE_2D, m_texture.id);
+ return qt_gl_read_texture(QSize(w, h), true, true);
+}
+
+struct TextureBuffer
+{
+ QGLFramebufferObject *fbo;
+ QGL2PaintEngineEx *engine;
+};
+
+Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool)
+QGLFramebufferObjectPool* qgl_fbo_pool()
+{
+ return _qgl_fbo_pool();
+}
+
+void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
+{
+ if (!isValid())
+ return;
+
+ m_hasFillColor = false;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ QGLShareContextScope ctx(share_ctx);
+
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_texture.id, 0);
+
+ const int x0 = 0;
+ const int x1 = w;
+ const int y0 = 0;
+ const int y1 = h;
+
+ if (!m_renderFbo->isBound())
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBlitFramebufferEXT(x0, y0, x1, y1,
+ x0, y0, x1, y1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ if (keepCurrentFboBound) {
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+ } else {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, m_renderFbo->handle());
+ ctx->d_ptr->current_fbo = m_renderFbo->handle();
+ }
+}
+
+bool QGLPixmapData::useFramebufferObjects() const
+{
+#ifdef Q_OS_SYMBIAN
+ // We don't want to use FBOs on Symbian
+ return false;
+#else
+ return QGLFramebufferObject::hasOpenGLFramebufferObjects()
+ && QGLFramebufferObject::hasOpenGLFramebufferBlit()
+ && qt_gl_preferGL2Engine()
+ && (w * h > 32*32); // avoid overhead of FBOs for small pixmaps
+#endif
+}
+
+QPaintEngine* QGLPixmapData::paintEngine() const
+{
+ if (!isValid())
+ return 0;
+
+ if (m_renderFbo)
+ return m_engine;
+
+ if (useFramebufferObjects()) {
+ extern QGLWidget* qt_gl_share_widget();
+
+ if (!QGLContext::currentContext())
+ qt_gl_share_widget()->makeCurrent();
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(4);
+ format.setInternalTextureFormat(GLenum(m_hasAlpha ? GL_RGBA : GL_RGB));
+
+ m_renderFbo = qgl_fbo_pool()->acquire(size(), format);
+
+ if (m_renderFbo) {
+ if (!m_engine)
+ m_engine = new QGL2PaintEngineEx;
+ return m_engine;
+ }
+
+ qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
+ }
+
+ // If the application wants to paint into the QPixmap, we first
+ // force it to QImage format and then paint into that.
+ // This is simpler than juggling multiple GL contexts.
+ const_cast<QGLPixmapData *>(this)->forceToImage();
+
+ if (m_hasFillColor) {
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ m_hasFillColor = false;
+ }
+ return m_source.paintEngine();
+}
+
+extern QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format);
+
+// If copyBack is true, bind will copy the contents of the render
+// FBO to the texture (which is not bound to the texture, as it's
+// a multisample FBO).
+GLuint QGLPixmapData::bind(bool copyBack) const
+{
+ if (m_renderFbo && copyBack) {
+ copyBackFromRenderFbo(true);
+ } else {
+ ensureCreated();
+ }
+
+ GLuint id = m_texture.id;
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ if (m_hasFillColor) {
+ if (!useFramebufferObjects()) {
+ m_source = QVolatileImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ m_source.fill(PREMUL(m_fillColor.rgba()));
+ }
+
+ m_hasFillColor = false;
+
+ GLenum format = qt_gl_preferredTextureFormat();
+ QImage tx(w, h, QImage::Format_ARGB32_Premultiplied);
+ tx.fill(qt_gl_convertToGLFormat(m_fillColor.rgba(), format));
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.constBits());
+ }
+
+ return id;
+}
+
+QGLTexture* QGLPixmapData::texture() const
+{
+ return &m_texture;
+}
+
+void QGLPixmapData::detachTextureFromPool()
+{
+ if (inTexturePool) {
+ QGLTexturePool::instance()->detachTexture(this);
+ inTexturePool = false;
+ }
+}
+
+void QGLPixmapData::hibernate()
+{
+ // If the texture was imported (e.g, from an SgImage under Symbian),
+ // then we cannot copy it back to main memory for storage.
+ if (m_texture.id && m_source.isNull())
+ return;
+
+ forceToImage();
+ destroyTexture();
+}
+
+void QGLPixmapData::reclaimTexture()
+{
+ if (!inTexturePool)
+ return;
+ forceToImage();
+ destroyTexture();
+}
+
+Q_GUI_EXPORT int qt_defaultDpiX();
+Q_GUI_EXPORT int qt_defaultDpiY();
+
+int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ if (w == 0)
+ return 0;
+
+ switch (metric) {
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 0;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM:
+ return qRound(w * 25.4 / qt_defaultDpiX());
+ case QPaintDevice::PdmHeightMM:
+ return qRound(h * 25.4 / qt_defaultDpiY());
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ default:
+ qWarning("QGLPixmapData::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+// Force the pixmap data to be backed by some valid data.
+void QGLPixmapData::forceToImage()
+{
+ if (!isValid())
+ return;
+
+ if (m_source.isNull()) {
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (pixelType() == BitmapType)
+ format = QImage::Format_MonoLSB;
+ m_source = QVolatileImage(w, h, format);
+ }
+
+ m_dirty = true;
+}
+
+QGLPaintDevice *QGLPixmapData::glDevice() const
+{
+ return &m_glDevice;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp
index 21b2f09..eae1c9b 100644
--- a/src/opengl/qwindowsurface_gl.cpp
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -181,17 +181,15 @@ QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
//
// QGLWindowSurface
//
-
#ifndef Q_WS_QPA
class QGLGlobalShareWidget
{
public:
- QGLGlobalShareWidget() : widget(0), initializing(false) {}
+ QGLGlobalShareWidget() : firstPixmap(0), widgetRefCount(0), widget(0), initializing(false) {}
QGLWidget *shareWidget() {
if (!initializing && !widget && !cleanedUp) {
initializing = true;
-
widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer));
widget->resize(1, 1);
@@ -227,6 +225,9 @@ public:
static bool cleanedUp;
+ QGLPixmapData *firstPixmap;
+ int widgetRefCount;
+
private:
QGLWidget *widget;
bool initializing;
@@ -274,6 +275,43 @@ const QGLContext *qt_gl_share_context()
#endif
}
+#ifdef QGL_USE_TEXTURE_POOL
+void qt_gl_register_pixmap(QGLPixmapData *pd)
+{
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+ pd->next = shared->firstPixmap;
+ pd->prev = 0;
+ if (shared->firstPixmap)
+ shared->firstPixmap->prev = pd;
+ shared->firstPixmap = pd;
+}
+
+void qt_gl_unregister_pixmap(QGLPixmapData *pd)
+{
+ if (pd->next)
+ pd->next->prev = pd->prev;
+ if (pd->prev) {
+ pd->prev->next = pd->next;
+ } else {
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+ if (shared)
+ shared->firstPixmap = pd->next;
+ }
+}
+
+void qt_gl_hibernate_pixmaps()
+{
+ QGLGlobalShareWidget *shared = _qt_gl_share_widget();
+
+ // Scan all QGLPixmapData objects in the system and hibernate them.
+ QGLPixmapData *pd = shared->firstPixmap;
+ while (pd != 0) {
+ pd->hibernate();
+ pd = pd->next;
+ }
+}
+#endif
+
struct QGLWindowSurfacePrivate
{
QGLFramebufferObject *fbo;
@@ -299,6 +337,8 @@ struct QGLWindowSurfacePrivate
QList<QImage> buffers;
QGLWindowSurfaceGLPaintDevice glDevice;
QGLWindowSurface* q_ptr;
+
+ bool partialUpdateSupport;
};
QGLFormat QGLWindowSurface::surfaceFormat;
@@ -351,6 +391,7 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window)
d_ptr->q_ptr = this;
d_ptr->geometry_updated = false;
d_ptr->did_paint = false;
+ d_ptr->partialUpdateSupport = false;
}
QGLWindowSurface::~QGLWindowSurface()
@@ -366,6 +407,27 @@ QGLWindowSurface::~QGLWindowSurface()
delete d_ptr->pb;
delete d_ptr->fbo;
delete d_ptr;
+
+ if (QGLGlobalShareWidget::cleanedUp)
+ return;
+
+ --(_qt_gl_share_widget()->widgetRefCount);
+
+#ifdef QGL_USE_TEXTURE_POOL
+ if (_qt_gl_share_widget()->widgetRefCount <= 0) {
+ // All of the widget window surfaces have been destroyed
+ // but we still have GL pixmaps active. Ask them to hibernate
+ // to free up GPU resources until a widget is shown again.
+ // This may eventually cause the EGLContext to be destroyed
+ // because nothing in the system needs a context, which will
+ // free up even more GPU resources.
+ qt_gl_hibernate_pixmaps();
+
+ // Destroy the context if necessary.
+ if (!qt_gl_share_widget()->context()->isSharing())
+ qt_destroy_gl_share_widget();
+ }
+#endif
}
void QGLWindowSurface::deleted(QObject *object)
@@ -415,6 +477,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
ctx->create(qt_gl_share_context());
+ if (widget != qt_gl_share_widget())
+ ++(_qt_gl_share_widget()->widgetRefCount);
+
#ifndef QT_NO_EGL
static bool checkedForNOKSwapRegion = false;
static bool haveNOKSwapRegion = false;
@@ -426,14 +491,22 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
if (haveNOKSwapRegion)
qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
}
+ bool swapBehaviourPreserved = (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED)
+ || (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT);
+ if (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
+ EGLint swapBehavior;
+ if (eglQuerySurface(ctx->d_func()->eglContext->display(), ctx->d_func()->eglSurface
+ , EGL_SWAP_BEHAVIOR, &swapBehavior)) {
+ swapBehaviourPreserved = (swapBehavior == EGL_BUFFER_PRESERVED);
+ }
+ }
- if (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED &&
- ! haveNOKSwapRegion)
- setPartialUpdateSupport(false); // Force full-screen updates
+ if (!swapBehaviourPreserved && !haveNOKSwapRegion)
+ d_ptr->partialUpdateSupport = false; // Force full-screen updates
else
- setPartialUpdateSupport(true);
+ d_ptr->partialUpdateSupport = true;
#else
- setPartialUpdateSupport(false);
+ d_ptr->partialUpdateSupport = false;
#endif
widgetPrivate->extraData()->glContext = ctx;
@@ -444,7 +517,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
voidPtrPtr = &widgetPrivate->extraData()->glContext;
d_ptr->contexts << ctxPtrPtr;
+#ifndef Q_OS_SYMBIAN
qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
+#endif
}
QGLContext *QGLWindowSurface::context() const
@@ -474,6 +549,7 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize,
void QGLWindowSurface::beginPaint(const QRegion &)
{
d_ptr->did_paint = true;
+ updateGeometry();
if (!context())
return;
@@ -845,14 +921,22 @@ void QGLWindowSurface::updateGeometry() {
ctx->d_func()->eglSurface = QEgl::createSurface(ctx->device(),
ctx->d_func()->eglContext->config());
-#if !defined(QGL_NO_PRESERVED_SWAP)
- eglGetError(); // Clear error state first.
- eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurface,
- EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
- if (eglGetError() != EGL_SUCCESS) {
- qWarning("QGLWindowSurface: could not restore preserved swap behaviour");
+ eglGetError(); // Clear error state.
+ if (hasPartialUpdateSupport()) {
+ eglSurfaceAttrib(ctx->d_func()->eglContext->display(),
+ ctx->d_func()->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLWindowSurface: could not enable preserved swap behaviour");
+ } else {
+ eglSurfaceAttrib(ctx->d_func()->eglContext->display(),
+ ctx->d_func()->eglSurface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
+
+ if (eglGetError() != EGL_SUCCESS)
+ qWarning("QGLWindowSurface: could not enable destroyed swap behaviour");
}
-#endif
}
#endif
@@ -943,8 +1027,9 @@ void QGLWindowSurface::updateGeometry() {
if (d_ptr->destructive_swap_buffers)
initializeOffscreenTexture(surfSize);
#endif
-
- qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
+#ifndef Q_OS_SYMBIAN
+ qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;
+#endif
d_ptr->ctx = ctx;
d_ptr->ctx->d_ptr->internal_context = true;
}
@@ -1081,6 +1166,11 @@ QImage *QGLWindowSurface::buffer(const QWidget *widget)
return &d_ptr->buffers.last();
}
+bool QGLWindowSurface::hasPartialUpdateSupport() const
+{
+ return d_ptr->partialUpdateSupport;
+}
+
QT_END_NAMESPACE
diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h
index 67f9f41..e1689bf 100644
--- a/src/opengl/qwindowsurface_gl_p.h
+++ b/src/opengl/qwindowsurface_gl_p.h
@@ -108,6 +108,8 @@ public:
QImage *buffer(const QWidget *widget);
+ bool hasPartialUpdateSupport() const;
+
QGLContext *context() const;
static QGLFormat surfaceFormat;