diff options
-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; |