summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp32
-rw-r--r--src/opengl/opengl.pro9
-rw-r--r--src/opengl/qgl.cpp21
-rw-r--r--src/opengl/qgl.h1
-rw-r--r--src/opengl/qgl_egl.cpp27
-rw-r--r--src/opengl/qgl_symbian.cpp132
-rw-r--r--src/opengl/qglpaintdevice.cpp2
-rw-r--r--src/opengl/qgltexturepool.cpp244
-rw-r--r--src/opengl/qgltexturepool_p.h147
-rw-r--r--src/opengl/qgraphicssystem_gl.cpp23
-rw-r--r--src/opengl/qgraphicssystem_gl_p.h4
-rw-r--r--src/opengl/qpixmapdata_gl.cpp159
-rw-r--r--src/opengl/qpixmapdata_gl_p.h68
-rw-r--r--src/opengl/qpixmapdata_poolgl.cpp936
-rw-r--r--src/opengl/qwindowsurface_gl.cpp141
15 files changed, 1841 insertions, 105 deletions
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index cda31e5..5c8d2b6 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
@@ -1335,11 +1331,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();
@@ -1351,6 +1350,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,
@@ -1375,12 +1380,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/opengl.pro b/src/opengl/opengl.pro
index 0e82467..6d79584 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -148,11 +148,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
symbian:TARGET.UID3 = 0x2002131A
}
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 062218c..b755153 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -69,6 +69,7 @@
#if !defined(QT_OPENGL_ES_1)
#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
+#include <private/qwindowsurface_gl_p.h>
#endif
#ifndef QT_OPENGL_ES_2
@@ -86,13 +87,16 @@
#include <private/qpixmapdata_p.h>
#include <private/qpixmapdata_gl_p.h>
#include <private/qglpixelbuffer_p.h>
-#include <private/qwindowsurface_gl_p.h>
#include <private/qimagepixmapcleanuphooks_p.h>
#include "qcolormap.h"
#include "qfile.h"
#include "qlibrary.h"
#include <qmutex.h>
+#ifdef QGL_USE_TEXTURE_POOL
+#include <private/qgltexturepool_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
@@ -2022,6 +2026,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
*/
@@ -2529,8 +2537,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);
@@ -2569,7 +2587,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)
{
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
index 9443c74..7d72c8a 100644
--- a/src/opengl/qgl.h
+++ b/src/opengl/qgl.h
@@ -329,6 +329,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_symbian.cpp b/src/opengl/qgl_symbian.cpp
index 82b66f5..1b41db4 100644
--- a/src/opengl/qgl_symbian.cpp
+++ b/src/opengl/qgl_symbian.cpp
@@ -41,15 +41,17 @@
#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>
#include <private/qgl_p.h>
#include <private/qpaintengine_opengl_p.h>
#include <private/qwidget_p.h> // to access QWExtra
+#include <private/qnativeimagehandleprovider_p.h>
#include "qgl_egl_p.h"
+#include "qpixmapdata_gl_p.h"
+#include "qgltexturepool_p.h"
#include "qcolormap.h"
#include <QDebug>
@@ -69,6 +71,8 @@ QT_BEGIN_NAMESPACE
#endif
#endif
+extern int qt_gl_pixmap_serial;
+
/*
QGLTemporaryContext implementation
*/
@@ -225,13 +229,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);
@@ -358,5 +369,104 @@ void QGLWidgetPrivate::recreateEglSurface()
eglSurfaceWindowId = currentId;
}
-QT_END_NAMESPACE
+static inline bool knownGoodFormat(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_RGB16: // EColor64K
+ case QImage::Format_RGB32: // EColor16MU
+ case QImage::Format_ARGB32_Premultiplied: // EColor16MAP
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QGLPixmapData::fromNativeType(void* pixmap, NativeType type)
+{
+ if (type == QPixmapData::FbsBitmap) {
+ 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);
+ }
+ 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;
+ } else if (type == QPixmapData::NativeImageHandleProvider && pixmap) {
+ destroyTexture();
+ nativeImageHandleProvider = static_cast<QNativeImageHandleProvider *>(pixmap);
+ // Cannot defer the retrieval, we need at least the size right away.
+ createFromNativeImageHandleProvider();
+ }
+}
+void* QGLPixmapData::toNativeType(NativeType type)
+{
+ if (type == QPixmapData::FbsBitmap) {
+ if (m_source.isNull())
+ m_source = QVolatileImage(w, h, QImage::Format_ARGB32_Premultiplied);
+ return m_source.duplicateNativeImage();
+ }
+
+ return 0;
+}
+
+bool QGLPixmapData::initFromNativeImageHandle(void *handle, const QString &type)
+{
+ if (type == QLatin1String("RSgImage")) {
+ fromNativeType(handle, QPixmapData::SgImage);
+ return true;
+ } else if (type == QLatin1String("CFbsBitmap")) {
+ fromNativeType(handle, QPixmapData::FbsBitmap);
+ return true;
+ }
+ return false;
+}
+
+void QGLPixmapData::createFromNativeImageHandleProvider()
+{
+ void *handle = 0;
+ QString type;
+ nativeImageHandleProvider->get(&handle, &type);
+ if (handle) {
+ if (initFromNativeImageHandle(handle, type)) {
+ nativeImageHandle = handle;
+ nativeImageType = type;
+ } else {
+ qWarning("QGLPixmapData: Unknown native image type '%s'", qPrintable(type));
+ }
+ } else {
+ qWarning("QGLPixmapData: Native handle is null");
+ }
+}
+
+void QGLPixmapData::releaseNativeImageHandle()
+{
+ if (nativeImageHandleProvider && nativeImageHandle) {
+ nativeImageHandleProvider->release(nativeImageHandle, nativeImageType);
+ nativeImageHandle = 0;
+ nativeImageType = QString();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp
index ae61ca1..0e879df 100644
--- a/src/opengl/qglpaintdevice.cpp
+++ b/src/opengl/qglpaintdevice.cpp
@@ -43,13 +43,13 @@
#include <private/qgl_p.h>
#include <private/qglpixelbuffer_p.h>
#include <private/qglframebufferobject_p.h>
-#include <private/qwindowsurface_gl_p.h>
#ifdef Q_WS_X11
#include <private/qpixmapdata_x11gl_p.h>
#endif
#if !defined(QT_OPENGL_ES_1)
#include <private/qpixmapdata_gl_p.h>
+#include <private/qwindowsurface_gl_p.h>
#endif
QT_BEGIN_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 79911fb..0aa3c2e 100644
--- a/src/opengl/qgraphicssystem_gl.cpp
+++ b/src/opengl/qgraphicssystem_gl.cpp
@@ -53,6 +53,14 @@
#include "private/qwindowsurface_x11gl_p.h"
#endif
+#if defined(Q_OS_SYMBIAN)
+#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();
@@ -86,8 +94,21 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const
}
#endif
+#if defined(Q_OS_SYMBIAN)
+ if (!QApplicationPrivate::instance()->useTranslucentEGLSurfaces) {
+ QWidgetPrivate *d = qt_widget_private(widget);
+ if (!d->isOpaque && widget->testAttribute(Qt::WA_TranslucentBackground))
+ return d->createDefaultWindowSurface_sys();
+ }
+#endif
+
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.cpp b/src/opengl/qpixmapdata_gl.cpp
index 43e80c1..70427d5 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -55,10 +55,11 @@
#include <qdesktopwidget.h>
#include <qfile.h>
#include <qimagereader.h>
+#include <qbuffer.h>
QT_BEGIN_NAMESPACE
-extern QGLWidget* qt_gl_share_widget();
+Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget();
/*!
\class QGLFramebufferObjectPool
@@ -369,40 +370,18 @@ void QGLPixmapData::ensureCreated() const
void QGLPixmapData::fromImage(const QImage &image,
Qt::ImageConversionFlags flags)
{
- if (image.size() == QSize(w, h))
- setSerialNumber(++qt_gl_pixmap_serial);
- resize(image.width(), image.height());
-
- if (pixelType() == BitmapType) {
- m_source = image.convertToFormat(QImage::Format_MonoLSB);
-
- } else {
- 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;;
-
- m_source = image.convertToFormat(format);
- }
-
- m_dirty = true;
- m_hasFillColor = false;
+ QImage img = image;
+ createPixmapForImage(img, flags, false);
+}
- m_hasAlpha = m_source.hasAlphaChannel();
- w = image.width();
- h = image.height();
- is_null = (w <= 0 || h <= 0);
- d = m_source.depth();
+void QGLPixmapData::fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags)
+{
+ QImage image = imageReader->read();
+ if (image.isNull())
+ return;
- if (m_texture.id) {
- QGLShareContextScope ctx(qt_gl_share_widget()->context());
- glDeleteTextures(1, &m_texture.id);
- m_texture.id = 0;
- }
+ createPixmapForImage(image, flags, true);
}
bool QGLPixmapData::fromFile(const QString &filename, const char *format,
@@ -411,31 +390,37 @@ bool QGLPixmapData::fromFile(const QString &filename, const char *format,
if (pixelType() == QPixmapData::BitmapType)
return QPixmapData::fromFile(filename, format, flags);
QFile file(filename);
- if (!file.open(QIODevice::ReadOnly))
- return false;
- 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 = QImage();
- m_dirty = isValid();
- return true;
+ 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 = QImage();
+ m_dirty = isValid();
+ return true;
+ }
+ return false;
}
- return false;
}
- fromImage(QImageReader(&file, format).read(), flags);
+
+ QImage image = QImageReader(filename, format).read();
+ if (image.isNull())
+ return false;
+
+ createPixmapForImage(image, flags, true);
+
return !isNull();
}
@@ -459,7 +444,67 @@ bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
return true;
}
}
- return QPixmapData::fromData(buffer, len, format, flags);
+
+ 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();
+}
+
+/*!
+ out-of-place conversion (inPlace == false) will always detach()
+ */
+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) {
+ m_source = image.convertToFormat(QImage::Format_MonoLSB);
+
+ } else {
+ 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;;
+
+ if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
+ m_source = image;
+ } else {
+ m_source = image.convertToFormat(format);
+
+ // convertToFormat won't detach the image if format stays the same.
+ if (image.format() == format)
+ m_source.detach();
+ }
+ }
+
+ 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();
+
+ if (m_texture.id) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
+ }
}
bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
index 5545d3c..8855c20 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,16 @@ 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
+
+#ifdef Q_OS_SYMBIAN
+class QNativeImageHandleProvider;
+#endif
+
class QGLFramebufferObjectPool
{
public:
@@ -107,6 +121,8 @@ public:
// Re-implemented from QPixmapData:
void resize(int width, int height);
void fromImage(const QImage &image, Qt::ImageConversionFlags flags);
+ void fromImageReader(QImageReader *imageReader,
+ Qt::ImageConversionFlags flags);
bool fromFile(const QString &filename, const char *format,
Qt::ImageConversionFlags flags);
bool fromData(const uchar *buffer, uint len, const char *format,
@@ -127,6 +143,32 @@ public:
GLuint bind(bool copyBack = true) const;
QGLTexture *texture() const;
+#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);
+ bool initFromNativeImageHandle(void *handle, const QString &type);
+ void createFromNativeImageHandleProvider();
+ void releaseNativeImageHandle();
+#endif
+
private:
bool isValid() const;
@@ -149,10 +191,19 @@ private:
QImage fillImage(const QColor &color) const;
+ void createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace);
+
mutable QGLFramebufferObject *m_renderFbo;
mutable QPaintEngine *m_engine;
mutable QGLContext *m_ctx;
+#ifdef Q_OS_SYMBIAN
+ mutable QVolatileImage m_source;
+ mutable QNativeImageHandleProvider *nativeImageHandleProvider;
+ void *nativeImageHandle;
+ QString nativeImageType;
+#else
mutable QImage m_source;
+#endif
mutable QGLTexture m_texture;
// the texture is not in sync with the source image
@@ -167,6 +218,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..95152dd
--- /dev/null
+++ b/src/opengl/qpixmapdata_poolgl.cpp
@@ -0,0 +1,936 @@
+/****************************************************************************
+**
+** 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/qnativeimagehandleprovider_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();
+
+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)
+ , nativeImageHandleProvider(0)
+ , nativeImageHandle(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;
+
+ releaseNativeImageHandle();
+}
+
+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;
+
+ if (nativeImageHandleProvider && !nativeImageHandle)
+ const_cast<QGLPixmapData *>(this)->createFromNativeImageHandleProvider();
+
+ 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 image was imported (e.g, from an SgImage under Symbian), then
+ // skip the hibernation, there is no sense in copying it back to main
+ // memory because the data is most likely shared between several processes.
+ bool skipHibernate = (m_texture.id && m_source.isNull());
+#if defined(Q_OS_SYMBIAN)
+ // However we have to proceed normally if the image was retrieved via
+ // a handle provider.
+ skipHibernate &= !nativeImageHandleProvider;
+#endif
+ if (skipHibernate)
+ 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 aad12d7..b056caa 100644
--- a/src/opengl/qwindowsurface_gl.cpp
+++ b/src/opengl/qwindowsurface_gl.cpp
@@ -181,16 +181,14 @@ QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
//
// QGLWindowSurface
//
-
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);
@@ -226,6 +224,9 @@ public:
static bool cleanedUp;
+ QGLPixmapData *firstPixmap;
+ int widgetRefCount;
+
private:
QGLWidget *widget;
bool initializing;
@@ -256,6 +257,43 @@ void qt_destroy_gl_share_widget()
_qt_gl_share_widget()->destroy();
}
+#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;
@@ -333,6 +371,10 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window)
d_ptr->q_ptr = this;
d_ptr->geometry_updated = false;
d_ptr->did_paint = false;
+
+#ifdef QGL_NO_PRESERVED_SWAP
+ setPartialUpdateSupport(false);
+#endif
}
QGLWindowSurface::~QGLWindowSurface()
@@ -347,6 +389,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)
@@ -394,6 +457,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
ctx->create(qt_gl_share_widget()->context());
+ if (widget != qt_gl_share_widget())
+ ++(_qt_gl_share_widget()->widgetRefCount);
+
#ifndef QT_NO_EGL
static bool checkedForNOKSwapRegion = false;
static bool haveNOKSwapRegion = false;
@@ -406,8 +472,16 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
}
- if (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED &&
- ! haveNOKSwapRegion)
+ bool swapBehaviourPreserved = ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR);
+ 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 (!swapBehaviourPreserved && !haveNOKSwapRegion)
setPartialUpdateSupport(false); // Force full-screen updates
else
setPartialUpdateSupport(true);
@@ -421,7 +495,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget)
voidPtr = &widgetPrivate->extraData()->glContext;
d_ptr->contexts << ctxPtr;
+#ifndef Q_OS_SYMBIAN
qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
+#endif
}
QGLContext *QGLWindowSurface::context() const
@@ -450,6 +526,8 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize,
void QGLWindowSurface::beginPaint(const QRegion &)
{
+ updateGeometry();
+
if (!context())
return;
@@ -628,7 +706,6 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &
} else {
glFlush();
}
-
return;
}
@@ -776,32 +853,53 @@ void QGLWindowSurface::updateGeometry() {
return;
d_ptr->geometry_updated = false;
- QRect rect = geometry();
- hijackWindow(window());
- QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
+ bool hijack(true);
+ QWidgetPrivate *wd = window()->d_func();
+ if (wd->extraData() && wd->extraData()->glContext) {
+#ifdef Q_OS_SYMBIAN // Symbian needs to recreate the context when native window size changes
+ if (d_ptr->size != geometry().size()) {
+ if (window() != qt_gl_share_widget())
+ --(_qt_gl_share_widget()->widgetRefCount);
+
+ delete wd->extraData()->glContext;
+ wd->extraData()->glContext = 0;
+ d_ptr->ctx = 0;
+ }
+ else
+#endif
+ {
+ hijack = false; // we already have gl context for widget
+ }
+ }
+
+ if (hijack)
+ hijackWindow(window());
+
+ QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
#ifdef Q_WS_MAC
ctx->updatePaintDevice();
#endif
- const GLenum target = GL_TEXTURE_2D;
+ QSize surfSize = geometry().size();
- if (rect.width() <= 0 || rect.height() <= 0)
+ if (surfSize.width() <= 0 || surfSize.height() <= 0)
return;
- if (d_ptr->size == rect.size())
+ if (d_ptr->size == surfSize)
return;
- d_ptr->size = rect.size();
+ d_ptr->size = surfSize;
if (d_ptr->ctx) {
#ifndef QT_OPENGL_ES_2
if (d_ptr->destructive_swap_buffers)
- initializeOffscreenTexture(rect.size());
+ initializeOffscreenTexture(surfSize);
#endif
return;
}
+ const GLenum target = GL_TEXTURE_2D;
if (d_ptr->destructive_swap_buffers
&& (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
&& (d_ptr->fbo || !d_ptr->tried_fbo)
@@ -820,10 +918,10 @@ void QGLWindowSurface::updateGeometry() {
if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
format.setSamples(8);
- d_ptr->fbo = new QGLFramebufferObject(rect.size(), format);
+ d_ptr->fbo = new QGLFramebufferObject(surfSize, format);
if (d_ptr->fbo->isValid()) {
- qDebug() << "Created Window Surface FBO" << rect.size()
+ qDebug() << "Created Window Surface FBO" << surfSize
<< "with samples" << d_ptr->fbo->format().samples();
return;
} else {
@@ -844,7 +942,7 @@ void QGLWindowSurface::updateGeometry() {
delete d_ptr->pb;
- d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
+ d_ptr->pb = new QGLPixelBuffer(surfSize.width(), surfSize.height(),
QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
qt_gl_share_widget());
@@ -854,7 +952,7 @@ void QGLWindowSurface::updateGeometry() {
glGenTextures(1, &d_ptr->pb_tex_id);
glBindTexture(target, d_ptr->pb_tex_id);
- glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(target, 0, GL_RGBA, surfSize.width(), surfSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -878,10 +976,11 @@ void QGLWindowSurface::updateGeometry() {
#ifndef QT_OPENGL_ES_2
if (d_ptr->destructive_swap_buffers)
- initializeOffscreenTexture(rect.size());
+ initializeOffscreenTexture(surfSize);
+#endif
+#ifndef Q_OS_SYMBIAN
+ qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;
#endif
-
- qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
d_ptr->ctx = ctx;
d_ptr->ctx->d_ptr->internal_context = true;
}