diff options
author | Tom Cooksey <thomas.cooksey@nokia.com> | 2009-07-21 14:15:57 (GMT) |
---|---|---|
committer | Tom Cooksey <thomas.cooksey@nokia.com> | 2009-07-22 14:49:24 (GMT) |
commit | 3597a5b82610752cf95372a5a5f7b33afc8d30b9 (patch) | |
tree | 4746be0f1b90894e72ab1e630268c5a048d0410c /src | |
parent | e2aa651aba89bcc409dd090466b04036277a9a7c (diff) | |
download | Qt-3597a5b82610752cf95372a5a5f7b33afc8d30b9.zip Qt-3597a5b82610752cf95372a5a5f7b33afc8d30b9.tar.gz Qt-3597a5b82610752cf95372a5a5f7b33afc8d30b9.tar.bz2 |
Use texture_from_pixmap on X11 & avoid copies
The patch tries to use texture_from_pixmap extentions on glX to properly
bind an X Pixmap to a texture in QGLContextPrivate::bindTexture(QPixmap,).
Because GL & X have different coordinate systems, the pixmap will be
inverted about the y-axis. The extension does however allow a
GLX_Y_INVERTED_EXT attribute to be set which will bind the pixmap the
correct way up. If the underlying driver doesn't support this,
texture_from_pixmap can't be used for QGLContext::bindTexture, because
that function expects the resulting texture to be the right way up.
However, it can still be used internally by the paint engine for
drawPixmap operations. For these cases, if the pixmap is inverted, the
paint engine can simply invert the texture coords to compensate. This is
why this patch also moves QGLTexture into qgl_p.h.
QGLContextPrivate::bindTexture(QPixmap,) now returns a QGLTexture which
the paint engine can inspect to see if it needs to invert the texture
coords.
Finally, it seems on some (probably all) drivers, deleting an X pixmap
which has been bound to a texture before calling glFinish/swapBuffers
renders garbage. Presumably this is because X deletes the pixmap behind
the driver's back before it's had a chance to use it. To fix this, we
reference all QPixmaps which have been bound to stop them being deleted
and only deref them after we swap the buffer, when they can be safely
deleted.
Reviewed-By: Kim
Diffstat (limited to 'src')
-rw-r--r-- | src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 22 | ||||
-rw-r--r-- | src/opengl/qgl.cpp | 296 | ||||
-rw-r--r-- | src/opengl/qgl_p.h | 80 | ||||
-rw-r--r-- | src/opengl/qgl_x11.cpp | 133 | ||||
-rw-r--r-- | src/opengl/qgl_x11egl.cpp | 11 | ||||
-rw-r--r-- | src/opengl/qglpixmapfilter.cpp | 2 | ||||
-rw-r--r-- | src/opengl/qpixmapdata_gl.cpp | 46 | ||||
-rw-r--r-- | src/opengl/qpixmapdata_gl_p.h | 6 |
8 files changed, 422 insertions, 174 deletions
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 7e8a281..167eb64 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -74,6 +74,7 @@ #include <private/qpainter_p.h> #include <private/qfontengine_p.h> #include <private/qtextureglyphcache_p.h> +#include <private/qpixmapdata_gl_p.h> #include "qglgradientcache_p.h" #include "qglengineshadermanager_p.h" @@ -1054,14 +1055,18 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c QGLContext *ctx = d->ctx; glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - GLuint id = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true); + QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true, true); + + GLfloat top = texture->yInverted ? (pixmap.height() - src.top()) : src.top(); + GLfloat bottom = texture->yInverted ? (pixmap.height() - src.bottom()) : src.bottom(); + QGLRect srcRect(src.left(), top, src.right(), bottom); bool isBitmap = pixmap.isQBitmap(); bool isOpaque = !isBitmap && !pixmap.hasAlphaChannel(); d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, - state()->renderHints & QPainter::SmoothPixmapTransform, id); - d->drawTexture(dest, src, pixmap.size(), isOpaque, isBitmap); + state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); + d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); } void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, @@ -1073,7 +1078,8 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QGLContext *ctx = d->ctx; glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - GLuint id = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); + QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); + GLuint id = texture->id; d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, state()->renderHints & QPainter::SmoothPixmapTransform, id); @@ -1287,6 +1293,14 @@ bool QGL2PaintEngineEx::end() glUseProgram(0); d->transferMode(BrushDrawingMode); d->drawable.swapBuffers(); +#if defined(Q_WS_X11) + // On some (probably all) drivers, deleting an X pixmap which has been bound to a texture + // before calling glFinish/swapBuffers renders garbage. Presumably this is because X deletes + // the pixmap behind the driver's back before it's had a chance to use it. To fix this, we + // reference all QPixmaps which have been bound to stop them being deleted and only deref + // them here, after swapBuffers, where they can be safely deleted. + ctx->d_func()->boundPixmaps.clear(); +#endif d->drawable.doneCurrent(); d->ctx->d_ptr->active_engine = 0; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index f51b271..392e750 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -87,7 +87,6 @@ #include <private/qglpixelbuffer_p.h> #include <private/qwindowsurface_gl_p.h> #include "qcolormap.h" -#include "qcache.h" #include "qfile.h" #include "qlibrary.h" @@ -1395,39 +1394,99 @@ int qt_next_power_of_two(int v) return v; } -class QGLTexture { -public: - QGLTexture(const QGLContext *ctx, GLuint tx_id, GLenum tx_target, bool _clean = false) - : context(ctx), id(tx_id), target(tx_target), clean(_clean) {} - ~QGLTexture() { - if (clean) { - QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext()); - QGLContext *ctx = const_cast<QGLContext *>(context); - bool switch_context = current && current != ctx && !qgl_share_reg()->checkSharing(current, ctx); - if (switch_context) - ctx->makeCurrent(); - glDeleteTextures(1, &id); - if (switch_context) - current->makeCurrent(); - } - } - - const QGLContext *context; - GLuint id; - GLenum target; - bool clean; -}; - -typedef QCache<qint64, QGLTexture> QGLTextureCache; -static int qt_tex_cache_limit = 64*1024; // cache ~64 MB worth of textures - this is not accurate though -static QGLTextureCache *qt_tex_cache = 0; - typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); typedef void (*_qt_image_cleanup_hook_64)(qint64); extern Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64; extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64; +static QGLTextureCache *qt_gl_texture_cache = 0; + +QGLTextureCache::QGLTextureCache() + : m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though +{ + Q_ASSERT(qt_gl_texture_cache == 0); + qt_gl_texture_cache = this; + qt_pixmap_cleanup_hook_64 = cleanupHook; + qt_image_cleanup_hook_64 = cleanupHook; +} + +QGLTextureCache::~QGLTextureCache() +{ + qt_gl_texture_cache = 0; + qt_pixmap_cleanup_hook_64 = 0; + qt_image_cleanup_hook_64 = 0; +} + +void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost) +{ + if (m_cache.totalCost() + cost > m_cache.maxCost()) { + // the cache is full - make an attempt to remove something + const QList<qint64> keys = m_cache.keys(); + int i = 0; + while (i < m_cache.count() + && (m_cache.totalCost() + cost > m_cache.maxCost())) { + QGLTexture *tex = m_cache.object(keys.at(i)); + if (tex->context == ctx) + m_cache.remove(keys.at(i)); + ++i; + } + } + m_cache.insert(key, texture, cost); +} + +bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId) +{ + QList<qint64> keys = m_cache.keys(); + for (int i = 0; i < keys.size(); ++i) { + QGLTexture *tex = m_cache.object(keys.at(i)); + if (tex->id == textureId && tex->context == ctx) { + tex->clean = true; // forces a glDeleteTextures() call + m_cache.remove(keys.at(i)); + return true; + } + } + return false; +} + +void QGLTextureCache::removeContextTextures(QGLContext* ctx) +{ + QList<qint64> keys = m_cache.keys(); + for (int i = 0; i < keys.size(); ++i) { + const qint64 &key = keys.at(i); + if (m_cache.object(key)->context == ctx) + m_cache.remove(key); + } +} + +QGLTextureCache* QGLTextureCache::instance() +{ + if (!qt_gl_texture_cache) + qt_gl_texture_cache = new QGLTextureCache; + + return qt_gl_texture_cache; +} + +/* + a hook that removes textures from the cache when a pixmap/image + is deref'ed +*/ +void QGLTextureCache::cleanupHook(qint64 cacheKey) +{ + // ### remove when the GL texture cache becomes thread-safe + if (qApp->thread() != QThread::currentThread()) + return; + QGLTexture *texture = instance()->getTexture(cacheKey); + if (texture && texture->clean) + instance()->remove(cacheKey); +} + +void QGLTextureCache::deleteIfEmpty() +{ + if (instance()->size() == 0) + delete instance(); +} + // DDS format structure struct DDSFormat { quint32 dwSize; @@ -1556,21 +1615,8 @@ QGLContext::~QGLContext() { Q_D(QGLContext); // remove any textures cached in this context - if (qt_tex_cache) { - QList<qint64> keys = qt_tex_cache->keys(); - for (int i = 0; i < keys.size(); ++i) { - const qint64 &key = keys.at(i); - if (qt_tex_cache->object(key)->context == this) - qt_tex_cache->remove(key); - } - // ### thread safety - if (qt_tex_cache->size() == 0) { - qt_pixmap_cleanup_hook_64 = 0; - qt_image_cleanup_hook_64 = 0; - delete qt_tex_cache; - qt_tex_cache = 0; - } - } + QGLTextureCache::instance()->removeContextTextures(this); + QGLTextureCache::deleteIfEmpty(); // ### thread safety QGLSignalProxy::instance()->emitAboutToDestroyContext(this); reset(); @@ -1701,21 +1747,6 @@ GLuint QGLContext::bindTexture(const QString &fileName) return tx_id; } -/* - a hook that removes textures from the cache when a pixmap/image - is deref'ed -*/ -static void qt_gl_clean_cache(qint64 cacheKey) -{ - // ### remove when the GL texture cache becomes thread-safe - if (qApp->thread() != QThread::currentThread()) - return; - if (qt_tex_cache) { - QGLTexture *texture = qt_tex_cache->object(cacheKey); - if (texture && texture->clean) - qt_tex_cache->remove(cacheKey); - } -} static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format) { @@ -1835,7 +1866,7 @@ QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_prem return result; } -GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, +QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key, bool clean) { Q_Q(QGLContext); @@ -1853,11 +1884,6 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint // the GL_BGRA format is only present in GL version >= 1.2 GLenum texture_format = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) ? GL_BGRA : GL_RGBA; - if (!qt_tex_cache) { - qt_tex_cache = new QGLTextureCache(qt_tex_cache_limit); - qt_pixmap_cleanup_hook_64 = qt_gl_clean_cache; - qt_image_cleanup_hook_64 = qt_gl_clean_cache; - } // Scale the pixmap if needed. GL textures needs to have the // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL @@ -1930,53 +1956,26 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint // this assumes the size of a texture is always smaller than the max cache size int cost = img.width()*img.height()*4/1024; - if (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost()) { - // the cache is full - make an attempt to remove something - const QList<qint64> keys = qt_tex_cache->keys(); - int i = 0; - while (i < qt_tex_cache->count() - && (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost())) { - QGLTexture *tex = qt_tex_cache->object(keys.at(i)); - if (tex->context == q) - qt_tex_cache->remove(keys.at(i)); - ++i; - } - } - qt_tex_cache->insert(key, new QGLTexture(q, tx_id, target, clean), cost); - return tx_id; + QGLTexture *texture = new QGLTexture(q, tx_id, target, clean, false); + QGLTextureCache::instance()->insert(q, key, texture, cost); + return texture; } -bool QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target, GLuint *id) +QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target) { Q_Q(QGLContext); - if (qt_tex_cache) { - QGLTexture *texture = qt_tex_cache->object(key); - if (texture && texture->target == target - && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context))) - { - *id = texture->id; - return true; - } + QGLTexture *texture = QGLTextureCache::instance()->getTexture(key); + if (texture && texture->target == target + && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context))) + { + return texture; } - return false; + return 0; } -/*! \internal */ -GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, bool clean) -{ - const qint64 key = image.cacheKey(); - GLuint id; - if (textureCacheLookup(key, target, &id)) { - glBindTexture(target, id); - return id; - } - GLuint cached = bindTexture(image, target, format, key, clean); - const_cast<QImage &>(image).data_ptr()->is_cached = (cached > 0); - return cached; -} /*! \internal */ -GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean) +QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean, bool canInvert) { Q_Q(QGLContext); QPixmapData *pd = pixmap.pixmapData(); @@ -1984,20 +1983,45 @@ GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLin if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) { const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd); - if (data->isValidContext(q)) - return data->bind(); + if (data->isValidContext(q)) { + data->bind(); + return data->texture(); + } } #endif const qint64 key = pixmap.cacheKey(); - GLuint id; - if (textureCacheLookup(key, target, &id)) { - glBindTexture(target, id); - return id; + QGLTexture *texture = textureCacheLookup(key, target); + if (texture) { + glBindTexture(target, texture->id); + return texture; + } + +#if defined(Q_WS_X11) + // Try to use texture_from_pixmap + if (pd->classId() == QPixmapData::X11Class) { + QPixmap *thatPixmap = const_cast<QPixmap*>(&pixmap); + texture = bindTextureFromNativePixmap(thatPixmap, key, canInvert); + if (texture) { + texture->clean = clean; + boundPixmaps.insert(thatPixmap->data_ptr(), QPixmap(pixmap)); + } } - GLuint cached = bindTexture(pixmap.toImage(), target, format, key, clean); - const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = (cached > 0); - return cached; +#endif + + if (!texture) + texture = bindTexture(pixmap.toImage(), target, format, key, clean); + + // We should never return 0 as callers shouldn't need to null-check + static QGLTexture invalidTexture; + if (!texture) + texture = &invalidTexture; + + if (texture->id > 0) + const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = true; + + Q_ASSERT(texture); + return texture; } /*! \internal */ @@ -2063,7 +2087,8 @@ int QGLContextPrivate::maxTextureSize() GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) { Q_D(QGLContext); - return d->bindTexture(image, target, format, false); + QGLTexture *texture = d->bindTexture(image, target, format, false); + return texture->id; } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -2082,7 +2107,8 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { Q_D(QGLContext); - return d->bindTexture(pixmap, target, format, false); + QGLTexture *texture = d->bindTexture(pixmap, target, format, false, false); + return texture->id; } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -2103,17 +2129,8 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q */ void QGLContext::deleteTexture(GLuint id) { - if (qt_tex_cache) { - QList<qint64> keys = qt_tex_cache->keys(); - for (int i = 0; i < keys.size(); ++i) { - QGLTexture *tex = qt_tex_cache->object(keys.at(i)); - if (tex->id == id && tex->context == this) { - tex->clean = true; // forces a glDeleteTextures() call - qt_tex_cache->remove(keys.at(i)); - return; - } - } - } + if (QGLTextureCache::instance()->remove(this, id)) + return; // check the DDS cache if the texture wasn't found in the pixmap/image // cache @@ -2307,9 +2324,7 @@ void QGLContext::drawTexture(const QPointF &point, QMacCompatGLuint textureId, Q */ void QGLContext::setTextureCacheLimit(int size) { - qt_tex_cache_limit = size; - if (qt_tex_cache) - qt_tex_cache->setMaxCost(qt_tex_cache_limit); + QGLTextureCache::instance()->setMaxCost(size); } /*! @@ -2319,7 +2334,7 @@ void QGLContext::setTextureCacheLimit(int size) */ int QGLContext::textureCacheLimit() { - return qt_tex_cache_limit; + return QGLTextureCache::instance()->maxCost(); } @@ -4339,6 +4354,9 @@ void QGLExtensions::init_extensions() if (extensions.contains(QLatin1String("EXT_framebuffer_blit"))) glExtensions |= FramebufferBlit; + if (extensions.contains(QLatin1String("GL_ARB_texture_non_power_of_two"))) + glExtensions |= NPOTTextures; + QGLContext cx(QGLFormat::defaultFormat()); if (glExtensions & TextureCompression) { qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB")); @@ -4546,32 +4564,34 @@ QGLFormat QGLDrawable::format() const GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format) { + QGLTexture *texture; if (widget) - return widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); + texture = widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); else if (buffer) - return buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true); + texture = buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true); else if (fbo && QGLContext::currentContext()) - return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true); + texture = const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true); #if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) else if (wsurf) - return wsurf->context()->d_func()->bindTexture(image, target, format, true); + texture = wsurf->context()->d_func()->bindTexture(image, target, format, true); #endif - return 0; + return texture->id; } GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { + QGLTexture *texture; if (widget) - return widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true); + texture = widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true, true); else if (buffer) - return buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true); + texture = buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true, true); else if (fbo && QGLContext::currentContext()) - return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true); + texture = const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true, true); #if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) else if (wsurf) - return wsurf->context()->d_func()->bindTexture(pixmap, target, format, true); + texture = wsurf->context()->d_func()->bindTexture(pixmap, target, format, true, true); #endif - return 0; + return texture->id; } QColor QGLDrawable::backgroundColor() const diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index fda0257..01385f0 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -60,10 +60,7 @@ #include "QtCore/qthreadstorage.h" #include "QtCore/qhash.h" #include "private/qwidget_p.h" - -#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) -#include "private/qpixmapdata_gl_p.h" -#endif +#include "qcache.h" #ifndef QT_OPENGL_ES_1_CL #define q_vertexType float @@ -203,17 +200,18 @@ struct QGLContextGroupResources QAtomicInt refs; }; +class QGLTexture; + class QGLContextPrivate { Q_DECLARE_PUBLIC(QGLContext) public: explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {groupResources = new QGLContextGroupResources;} ~QGLContextPrivate() {if (!groupResources->refs.deref()) delete groupResources;} - GLuint bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key, + QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key, bool clean = false); - GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean); - GLuint bindTexture(const QImage &image, GLenum target, GLint format, bool clean); - bool textureCacheLookup(const qint64 key, GLenum target, GLuint *id); + QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean, bool canInvert = false); + QGLTexture *textureCacheLookup(const qint64 key, GLenum target); void init(QPaintDevice *dev, const QGLFormat &format); QImage convertToGLFormat(const QImage &image, bool force_premul, GLenum texture_format); int maxTextureSize(); @@ -241,6 +239,8 @@ public: void* pbuf; quint32 gpm; int screen; + QHash<QPixmapData*, QPixmap> boundPixmaps; + QGLTexture *bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool internal); #endif #if defined(Q_WS_MAC) bool update; @@ -300,6 +300,7 @@ class QGLPixelBuffer; class QGLFramebufferObject; class QWSGLWindowSurface; class QGLWindowSurface; +class QGLPixmapData; class QGLDrawable { public: QGLDrawable() : widget(0), buffer(0), fbo(0) @@ -360,7 +361,8 @@ public: PackedDepthStencil = 0x00000200, NVFloatBuffer = 0x00000400, PixelBufferObject = 0x00000800, - FramebufferBlit = 0x00001000 + FramebufferBlit = 0x00001000, + NPOTTextures = 0x00002000 }; Q_DECLARE_FLAGS(Extensions, Extension) @@ -397,6 +399,66 @@ private: extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg(); +class QGLTexture { +public: + QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D, + bool _clean = true, bool _yInverted = false) + : context(ctx), id(tx_id), target(tx_target), clean(_clean), yInverted(_yInverted) +#if defined(Q_WS_X11) + , boundPixmap(0) +#endif + {} + + ~QGLTexture() { + if (clean) { + QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext()); + QGLContext *ctx = const_cast<QGLContext *>(context); + bool switch_context = current && current != ctx && !qgl_share_reg()->checkSharing(current, ctx); + if (switch_context) + ctx->makeCurrent(); +#if defined(Q_WS_X11) + deleteBoundPixmap(); +#endif + glDeleteTextures(1, &id); + if (switch_context) + current->makeCurrent(); + } + } + + QGLContext *context; + GLuint id; + GLenum target; + bool clean; + bool yInverted; // NOTE: Y-Inverted textures are for internal use only! +#if defined(Q_WS_X11) + Qt::HANDLE boundPixmap; + void deleteBoundPixmap(); // in qgl_x11.cpp/qgl_x11egl.cpp +#endif +}; + +class QGLTextureCache { +public: + QGLTextureCache(); + ~QGLTextureCache(); + + void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost); + void remove(quint64 key) { m_cache.remove(key); } + bool remove(QGLContext *ctx, GLuint textureId); + void removeContextTextures(QGLContext *ctx); + int size() { return m_cache.size(); } + void setMaxCost(int newMax) { m_cache.setMaxCost(newMax); } + int maxCost() {return m_cache.maxCost(); } + QGLTexture* getTexture(quint64 key) { return m_cache.object(key); } + + static QGLTextureCache *instance(); + static void deleteIfEmpty(); + static void cleanupHook(qint64 cacheKey); + +private: + QCache<qint64, QGLTexture> m_cache; +}; + + #ifdef Q_WS_QWS extern QPaintEngine* qt_qgl_paint_engine(); diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp index 631625b..0399b48 100644 --- a/src/opengl/qgl_x11.cpp +++ b/src/opengl/qgl_x11.cpp @@ -52,6 +52,7 @@ #include "qdebug.h" #include <private/qfontengine_ft_p.h> #include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> #ifdef Q_OS_HPUX // for GLXPBuffer #include <private/qglpixelbuffer_p.h> @@ -1516,4 +1517,136 @@ void QGLExtensions::init() } } + +typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*); +typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int); +static qt_glXBindTexImageEXT glXBindTexImageEXT = 0; +static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0; +static bool qt_resolved_texture_from_pixmap = false; + +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool canInvert) +{ + Q_Q(QGLContext); + + if (pm->data_ptr()->classId() != QPixmapData::X11Class) + return 0; + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pm->data_ptr()); + const QX11Info *x11Info = qt_x11Info(pm); + + + // Check to see if we have NPOT texture support + // TODO: Use GLX_TEXTURE_RECTANGLE_EXT texture target on systems without npot textures + if ( !(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures) && + !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)) + return 0; + + if (!qt_resolved_texture_from_pixmap) { + qt_resolved_texture_from_pixmap = true; + + QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)); + if (glxExt.contains(QLatin1String("GLX_EXT_texture_from_pixmap"))) { +#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) + void *handle = dlopen(NULL, RTLD_LAZY); + if (handle) { + glXBindTexImageEXT = (qt_glXBindTexImageEXT) dlsym(handle, "glXBindTexImageEXT"); + glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) dlsym(handle, "glXReleaseTexImageEXT"); + dlclose(handle); + } + if (!glXBindTexImageEXT) +#endif + { + extern const QString qt_gl_library_name(); + QLibrary lib(qt_gl_library_name()); + glXBindTexImageEXT = (qt_glXBindTexImageEXT) lib.resolve("glXBindTexImageEXT"); + glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) lib.resolve("glXReleaseTexImageEXT"); + } + } + } + + if (!glXBindTexImageEXT) + return 0; + +#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) + return 0; +#else + GLXFBConfig *configList = 0; + GLXFBConfig glxPixmapConfig; + int configCount = 0; + bool hasAlpha = pixmapData->hasAlphaChannel(); + + int configAttribs[] = { + hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can: + GLX_Y_INVERTED_EXT, canInvert ? GLX_DONT_CARE : False, + XNone +// GLX_BIND_TO_MIPMAP_TEXTURE_EXT, False, +// GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_1D_BIT_EXT or GLX_TEXTURE_2D_BIT_EXT or GLX_TEXTURE_RECTANGLE_BIT_EXT + }; + configList = glXChooseFBConfig(x11Info->display(), x11Info->screen(), configAttribs, &configCount); + if (!configList) + return 0; + glxPixmapConfig = configList[0]; + XFree(configList); + + GLXPixmap glxPixmap; + int pixmapAttribs[] = { + GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_MIPMAP_TEXTURE_EXT, False, + XNone +// GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT or GLX_TEXTURE_FORMAT_RGB_EXT or GLX_TEXTURE_FORMAT_NONE_EXT, +// GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT or GLX_TEXTURE_RECTANGLE_EXT, +// GLX_MIPMAP_TEXTURE_EXT, True or False, + }; + + // Wrap the X Pixmap into a GLXPixmap: + glxPixmap = glXCreatePixmap(x11Info->display(), glxPixmapConfig, pixmapData->handle(), pixmapAttribs); + + if (!glxPixmap) + return 0; + + int yInverted; + glXGetFBConfigAttrib(x11Info->display(), glxPixmapConfig, GLX_Y_INVERTED_EXT, &yInverted); + + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + glXBindTexImageEXT(x11Info->display(), glxPixmap, GLX_FRONT_LEFT_EXT, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + + QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, canInvert, yInverted); + texture->boundPixmap = glxPixmap; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + return texture; +#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) +} + +void QGLTexture::deleteBoundPixmap() +{ + if (boundPixmap) { + // Although glXReleaseTexImage is a glX call, it must be called while there + // is a current context - the context the pixmap was bound to a texture in. + // Otherwise the relese doesn't do anything and you get BadDrawable errors + // when you come to delete the context. + + QGLContext *oldContext = const_cast<QGLContext*>(QGLContext::currentContext()); + if (oldContext != context) + context->makeCurrent(); + glXReleaseTexImageEXT(QX11Info::display(), boundPixmap, GLX_FRONT_LEFT_EXT); + if (oldContext && oldContext != context) + oldContext->makeCurrent(); + + glXDestroyPixmap(QX11Info::display(), boundPixmap); + } + + boundPixmap = 0; +} + + QT_END_NAMESPACE diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 9db3a30..11131ea 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -469,4 +469,15 @@ void QGLWidgetPrivate::recreateEglSurface(bool force) } } +GLuint QGLContextPrivate::bindTextureFromNativePixmap(const QPixmap& pm, const qint64 key, bool canInvert) +{ + // TODO + return 0; +} + +void QGLTexture::deleteBoundPixmap() +{ + //TODO +} + QT_END_NAMESPACE diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 7514743..5a06763 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const { - const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true); + const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true, false); } void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index fe3bb0c..e3ee2b2 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -97,7 +97,6 @@ static int qt_gl_pixmap_serial = 0; QGLPixmapData::QGLPixmapData(PixelType type) : QPixmapData(type, OpenGLClass) , m_renderFbo(0) - , m_textureId(0) , m_engine(0) , m_ctx(0) , m_dirty(false) @@ -113,9 +112,9 @@ QGLPixmapData::~QGLPixmapData() if (!shareWidget) return; - if (m_textureId) { + if (m_texture.id) { QGLShareContextScope ctx(shareWidget->context()); - glDeleteTextures(1, &m_textureId); + glDeleteTextures(1, &m_texture.id); } } @@ -148,10 +147,10 @@ void QGLPixmapData::resize(int width, int height) is_null = (w <= 0 || h <= 0); d = pixelType() == QPixmapData::PixmapType ? 32 : 1; - if (m_textureId) { + if (m_texture.id) { QGLShareContextScope ctx(qt_gl_share_widget()->context()); - glDeleteTextures(1, &m_textureId); - m_textureId = 0; + glDeleteTextures(1, &m_texture.id); + m_texture.id = 0; } m_source = QImage(); @@ -172,9 +171,9 @@ void QGLPixmapData::ensureCreated() const const GLenum format = qt_gl_preferredTextureFormat(); const GLenum target = GL_TEXTURE_2D; - if (!m_textureId) { - glGenTextures(1, &m_textureId); - glBindTexture(target, m_textureId); + if (!m_texture.id) { + glGenTextures(1, &m_texture.id); + glBindTexture(target, m_texture.id); GLenum format = m_hasAlpha ? GL_RGBA : GL_RGB; glTexImage2D(target, 0, format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); @@ -185,13 +184,15 @@ void QGLPixmapData::ensureCreated() const if (!m_source.isNull()) { const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format); - glBindTexture(target, m_textureId); + glBindTexture(target, m_texture.id); glTexSubImage2D(target, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.bits()); if (useFramebufferObjects()) m_source = QImage(); } + + m_texture.clean = false; } QGLFramebufferObject *QGLPixmapData::fbo() const @@ -223,10 +224,10 @@ void QGLPixmapData::fromImage(const QImage &image, is_null = (w <= 0 || h <= 0); d = pixelType() == QPixmapData::PixmapType ? 32 : 1; - if (m_textureId) { + if (m_texture.id) { QGLShareContextScope ctx(qt_gl_share_widget()->context()); - glDeleteTextures(1, &m_textureId); - m_textureId = 0; + glDeleteTextures(1, &m_texture.id); + m_texture.id = 0; } } @@ -256,9 +257,9 @@ void QGLPixmapData::fill(const QColor &color) bool hasAlpha = color.alpha() != 255; if (hasAlpha && !m_hasAlpha) { - if (m_textureId) { - glDeleteTextures(1, &m_textureId); - m_textureId = 0; + if (m_texture.id) { + glDeleteTextures(1, &m_texture.id); + m_texture.id = 0; m_dirty = true; } m_hasAlpha = color.alpha() != 255; @@ -321,7 +322,7 @@ QImage QGLPixmapData::toImage() const } QGLShareContextScope ctx(qt_gl_share_widget()->context()); - glBindTexture(GL_TEXTURE_2D, m_textureId); + glBindTexture(GL_TEXTURE_2D, m_texture.id); return qt_gl_read_texture(QSize(w, h), true, true); } @@ -351,7 +352,7 @@ void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->fbo); glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, m_textureId, 0); + GL_TEXTURE_2D, m_texture.id, 0); const int x0 = 0; const int x1 = w; @@ -489,7 +490,7 @@ GLuint QGLPixmapData::bind(bool copyBack) const ensureCreated(); } - GLuint id = m_textureId; + GLuint id = m_texture.id; glBindTexture(GL_TEXTURE_2D, id); return id; } @@ -497,7 +498,12 @@ GLuint QGLPixmapData::bind(bool copyBack) const GLuint QGLPixmapData::textureId() const { ensureCreated(); - return m_textureId; + return m_texture.id; +} + +QGLTexture* QGLPixmapData::texture() const +{ + return &m_texture; } extern int qt_defaultDpiX(); diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index a6aa22d..671f9a7 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -53,6 +53,7 @@ // We mean it. // +#include "qgl_p.h" #include "qgl.h" #include "private/qpixmapdata_p.h" @@ -80,10 +81,11 @@ public: void fill(const QColor &color); bool hasAlphaChannel() const; QImage toImage() const; - QPaintEngine* paintEngine() const; + QPaintEngine *paintEngine() const; GLuint bind(bool copyBack = true) const; GLuint textureId() const; + QGLTexture *texture() const; bool isValidContext(const QGLContext *ctx) const; @@ -116,10 +118,10 @@ private: QImage fillImage(const QColor &color) const; mutable QGLFramebufferObject *m_renderFbo; - mutable GLuint m_textureId; mutable QPaintEngine *m_engine; mutable QGLContext *m_ctx; mutable QImage m_source; + mutable QGLTexture m_texture; // the texture is not in sync with the source image mutable bool m_dirty; |