diff options
Diffstat (limited to 'src/opengl/qgl.cpp')
-rw-r--r-- | src/opengl/qgl.cpp | 1483 |
1 files changed, 1119 insertions, 364 deletions
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 18c70f3..3f96d1c 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -59,23 +59,37 @@ # include <private/qt_mac_p.h> #endif +#include <qdatetime.h> + #include <stdlib.h> // malloc #include "qpixmap.h" #include "qimage.h" #include "qgl_p.h" -#if defined(QT_OPENGL_ES_2) +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) #include "gl2paintengineex/qpaintengineex_opengl2_p.h" -#else +#endif + +#ifndef QT_OPENGL_ES_2 #include <private/qpaintengine_opengl_p.h> #endif +#ifdef Q_WS_QWS +#include <private/qglpaintdevice_qws_p.h> +#include <private/qglwindowsurface_qws_p.h> +#endif + +#include <qglpixelbuffer.h> +#include <qglframebufferobject.h> + #include <private/qimage_p.h> #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 "qcache.h" #include "qfile.h" #include "qlibrary.h" @@ -91,7 +105,11 @@ QT_BEGIN_NAMESPACE QGLExtensionFuncs QGLContextPrivate::qt_extensionFuncs; #endif -QThreadStorage<QGLThreadContext *> qgl_context_storage; +struct QGLThreadContext { + QGLContext *context; +}; + +static QThreadStorage<QGLThreadContext *> qgl_context_storage; Q_GLOBAL_STATIC(QGLFormat, qgl_default_format) @@ -128,13 +146,56 @@ QGLSignalProxy *QGLSignalProxy::instance() return theSignalProxy(); } + +class QGLEngineSelector +{ +public: + QGLEngineSelector() : engineType(QPaintEngine::MaxUser) { } + + void setPreferredPaintEngine(QPaintEngine::Type type) { + if (type == QPaintEngine::OpenGL || type == QPaintEngine::OpenGL2) + engineType = type; + } + + QPaintEngine::Type preferredPaintEngine() { + if (engineType == QPaintEngine::MaxUser) { + // No user-set engine - use the defaults +#if defined(QT_OPENGL_ES_2) + engineType = QPaintEngine::OpenGL2; +#else + // We can't do this in the constructor for this object because it + // needs to be called *before* the QApplication constructor + if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) + && qgetenv("QT_GL_USE_OPENGL1ENGINE").isEmpty()) + engineType = QPaintEngine::OpenGL2; + else + engineType = QPaintEngine::OpenGL; +#endif + } + return engineType; + } + +private: + QPaintEngine::Type engineType; +}; + +Q_GLOBAL_STATIC(QGLEngineSelector, qgl_engine_selector) + + +bool qt_gl_preferGL2Engine() +{ + return qgl_engine_selector()->preferredPaintEngine() == QPaintEngine::OpenGL2; +} + + /*! \namespace QGL + \inmodule QtOpenGL \brief The QGL namespace specifies miscellaneous identifiers used in the Qt OpenGL module. - \ingroup multimedia + \ingroup painting-3D */ /*! @@ -167,6 +228,32 @@ QGLSignalProxy *QGLSignalProxy::instance() \sa {Sample Buffers Example} */ +/*! + \fn void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType) + + \since 4.6 + + Sets the preferred OpenGL paint engine that is used to draw onto + QGLWidget, QGLPixelBuffer and QGLFramebufferObject targets with QPainter + in Qt. + + The \a engineType parameter specifies which of the GL engines to + use. Only \c QPaintEngine::OpenGL and \c QPaintEngine::OpenGL2 are + valid parameters to this function. All other values are ignored. + + By default, the \c QPaintEngine::OpenGL2 engine is used if GL/GLES + version 2.0 is available, otherwise \c QPaintEngine::OpenGL is + used. + + \warning This function must be called before the QApplication + constructor is called. +*/ +void QGL::setPreferredPaintEngine(QPaintEngine::Type engineType) +{ + qgl_engine_selector()->setPreferredPaintEngine(engineType); +} + + /***************************************************************************** QGLFormat implementation *****************************************************************************/ @@ -177,7 +264,7 @@ QGLSignalProxy *QGLSignalProxy::instance() \brief The QGLFormat class specifies the display format of an OpenGL rendering context. - \ingroup multimedia + \ingroup painting-3D A display format has several characteristics: \list @@ -190,13 +277,14 @@ QGLSignalProxy *QGLSignalProxy::instance() \i \link setStereo() Stereo buffers.\endlink \i \link setDirectRendering() Direct rendering.\endlink \i \link setOverlay() Presence of an overlay.\endlink - \i \link setPlane() The plane of an overlay format.\endlink + \i \link setPlane() Plane of an overlay.\endlink \i \link setSampleBuffers() Multisample buffers.\endlink \endlist - You can also specify preferred bit depths for the depth buffer, - alpha buffer, accumulation buffer and the stencil buffer with the - functions: setDepthBufferSize(), setAlphaBufferSize(), + You can also specify preferred bit depths for the color buffer, + depth buffer, alpha buffer, accumulation buffer and the stencil + buffer with the functions: setRedBufferSize(), setGreenBufferSize(), + setBlueBufferSize(), setDepthBufferSize(), setAlphaBufferSize(), setAccumBufferSize() and setStencilBufferSize(). Note that even if you specify that you prefer a 32 bit depth @@ -237,6 +325,8 @@ QGLSignalProxy *QGLSignalProxy::instance() \sa QGLContext, QGLWidget */ +#ifndef QT_OPENGL_ES + static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]) { #define M(row,col) m[col*4+row] @@ -279,20 +369,23 @@ static inline GLint qgluProject(GLdouble objx, GLdouble objy, GLdouble objz, return GL_TRUE; } +#endif // !QT_OPENGL_ES + /*! - Constructs a QGLFormat object with the factory default settings: + Constructs a QGLFormat object with the following default settings: \list \i \link setDoubleBuffer() Double buffer:\endlink Enabled. \i \link setDepth() Depth buffer:\endlink Enabled. \i \link setRgba() RGBA:\endlink Enabled (i.e., color index disabled). \i \link setAlpha() Alpha channel:\endlink Disabled. \i \link setAccum() Accumulator buffer:\endlink Disabled. - \i \link setStencil() Stencil buffer:\endlink Disabled. + \i \link setStencil() Stencil buffer:\endlink Enabled. \i \link setStereo() Stereo:\endlink Disabled. \i \link setDirectRendering() Direct rendering:\endlink Enabled. \i \link setOverlay() Overlay:\endlink Disabled. \i \link setPlane() Plane:\endlink 0 (i.e., normal plane). - \i \link setSampleBuffers() Multisample buffers:\endlink Disabled. + \i \link setSampleBuffers() Multisample buffers:\endlink Enabled on + OpenGL/ES 2.0, disabled on other platforms. \endlist */ @@ -303,26 +396,26 @@ QGLFormat::QGLFormat() /*! - Creates a QGLFormat object that is a copy of the current \link - defaultFormat() application default format\endlink. + Creates a QGLFormat object that is a copy of the current + defaultFormat(). - If \a options is not 0, this copy is modified by these format - options. The \a options parameter should be \c FormatOption values - OR'ed together. + If \a options is not 0, the default format is modified by the + specified format options. The \a options parameter should be + QGL::FormatOption values OR'ed together. This constructor makes it easy to specify a certain desired format in classes derived from QGLWidget, for example: \snippet doc/src/snippets/code/src_opengl_qgl.cpp 3 - Note that there are \c FormatOption values to turn format settings - both on and off, e.g. \c DepthBuffer and \c NoDepthBuffer, - \c DirectRendering and \c IndirectRendering, etc. + Note that there are QGL::FormatOption values to turn format settings + both on and off, e.g. QGL::DepthBuffer and QGL::NoDepthBuffer, + QGL::DirectRendering and QGL::IndirectRendering, etc. The \a plane parameter defaults to 0 and is the plane which this format should be associated with. Not all OpenGL implementations supports overlay/underlay rendering planes. - \sa defaultFormat(), setOption() + \sa defaultFormat(), setOption(), setPlane() */ QGLFormat::QGLFormat(QGL::FormatOptions options, int plane) @@ -336,13 +429,26 @@ QGLFormat::QGLFormat(QGL::FormatOptions options, int plane) } /*! + \internal +*/ +void QGLFormat::detach() +{ + if (d->ref != 1) { + QGLFormatPrivate *newd = new QGLFormatPrivate(d); + if (!d->ref.deref()) + delete d; + d = newd; + } +} + +/*! Constructs a copy of \a other. */ QGLFormat::QGLFormat(const QGLFormat &other) { - d = new QGLFormatPrivate; - *d = *other.d; + d = other.d; + d->ref.ref(); } /*! @@ -351,7 +457,12 @@ QGLFormat::QGLFormat(const QGLFormat &other) QGLFormat &QGLFormat::operator=(const QGLFormat &other) { - *d = *other.d; + if (d != other.d) { + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + } return *this; } @@ -360,7 +471,8 @@ QGLFormat &QGLFormat::operator=(const QGLFormat &other) */ QGLFormat::~QGLFormat() { - delete d; + if (!d->ref.deref()) + delete d; } /*! @@ -514,7 +626,7 @@ void QGLFormat::setAccum(bool enable) \fn bool QGLFormat::stencil() const Returns true if the stencil buffer is enabled; otherwise returns - false. The stencil buffer is disabled by default. + false. The stencil buffer is enabled by default. \sa setStencil(), setStencilBufferSize() */ @@ -523,7 +635,7 @@ void QGLFormat::setAccum(bool enable) If \a enable is true enables the stencil buffer; otherwise disables the stencil buffer. - The stencil buffer is disabled by default. + The stencil buffer is enabled by default. The stencil buffer masks certain parts of the drawing area so that masked parts are not drawn on. @@ -636,6 +748,7 @@ int QGLFormat::samples() const */ void QGLFormat::setSamples(int numSamples) { + detach(); if (numSamples < 0) { qWarning("QGLFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples); return; @@ -663,6 +776,7 @@ void QGLFormat::setSamples(int numSamples) */ void QGLFormat::setSwapInterval(int interval) { + detach(); d->swapInterval = interval; } @@ -708,7 +822,7 @@ void QGLFormat::setOverlay(bool enable) is 0, which means the normal plane. The default for overlay formats is 1, which is the first overlay plane. - \sa setPlane() + \sa setPlane(), defaultOverlayFormat() */ int QGLFormat::plane() const { @@ -730,6 +844,7 @@ int QGLFormat::plane() const */ void QGLFormat::setPlane(int plane) { + detach(); d->pln = plane; } @@ -741,6 +856,7 @@ void QGLFormat::setPlane(int plane) void QGLFormat::setOption(QGL::FormatOptions opt) { + detach(); if (opt & 0xffff) d->opts |= opt; else @@ -770,6 +886,7 @@ bool QGLFormat::testOption(QGL::FormatOptions opt) const */ void QGLFormat::setDepthBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size); return; @@ -796,6 +913,7 @@ int QGLFormat::depthBufferSize() const */ void QGLFormat::setRedBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setRedBufferSize: Cannot set negative red buffer size %d", size); return; @@ -824,6 +942,7 @@ int QGLFormat::redBufferSize() const */ void QGLFormat::setGreenBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size); return; @@ -852,6 +971,7 @@ int QGLFormat::greenBufferSize() const */ void QGLFormat::setBlueBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size); return; @@ -879,6 +999,7 @@ int QGLFormat::blueBufferSize() const */ void QGLFormat::setAlphaBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size); return; @@ -905,6 +1026,7 @@ int QGLFormat::alphaBufferSize() const */ void QGLFormat::setAccumBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size); return; @@ -929,6 +1051,7 @@ int QGLFormat::accumBufferSize() const */ void QGLFormat::setStencilBufferSize(int size) { + detach(); if (size < 0) { qWarning("QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size); return; @@ -1124,11 +1247,11 @@ QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags() if (cachedDefault) { return defaultVersionFlags; } else { - cachedDefault = true; if (!hasOpenGL()) return defaultVersionFlags; dummy = new QGLWidget; dummy->makeCurrent(); // glGetString() needs a current context + cachedDefault = true; } } @@ -1148,8 +1271,8 @@ QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags() /*! - Returns the default QGLFormat for the application. All QGLWidgets - that are created use this format unless another format is + Returns the default QGLFormat for the application. All QGLWidget + objects that are created use this format unless another format is specified, e.g. when they are constructed. If no special default format has been set using @@ -1182,7 +1305,7 @@ void QGLFormat::setDefaultFormat(const QGLFormat &f) /*! Returns the default QGLFormat for overlay contexts. - The factory default overlay format is: + The default overlay format is: \list \i \link setDoubleBuffer() Double buffer:\endlink Disabled. \i \link setDepth() Depth buffer:\endlink Disabled. @@ -1193,6 +1316,7 @@ void QGLFormat::setDefaultFormat(const QGLFormat &f) \i \link setStereo() Stereo:\endlink Disabled. \i \link setDirectRendering() Direct rendering:\endlink Enabled. \i \link setOverlay() Overlay:\endlink Disabled. + \i \link setSampleBuffers() Multisample buffers:\endlink Disabled. \i \link setPlane() Plane:\endlink 1 (i.e., first overlay plane). \endlist @@ -1235,21 +1359,30 @@ void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f) /*! - Returns true if all the options of the two QGLFormats are equal; - otherwise returns false. + Returns true if all the options of the two QGLFormat objects + \a a and \a b are equal; otherwise returns false. + + \relates QGLFormat */ bool operator==(const QGLFormat& a, const QGLFormat& b) { return (int) a.d->opts == (int) b.d->opts && a.d->pln == b.d->pln && a.d->alphaSize == b.d->alphaSize && a.d->accumSize == b.d->accumSize && a.d->stencilSize == b.d->stencilSize - && a.d->depthSize == b.d->depthSize; + && a.d->depthSize == b.d->depthSize + && a.d->redSize == b.d->redSize + && a.d->greenSize == b.d->greenSize + && a.d->blueSize == b.d->blueSize + && a.d->numSamples == b.d->numSamples + && a.d->swapInterval == b.d->swapInterval; } /*! - Returns false if all the options of the two QGLFormats are equal; - otherwise returns true. + Returns false if all the options of the two QGLFormat objects + \a a and \a b are equal; otherwise returns true. + + \relates QGLFormat */ bool operator!=(const QGLFormat& a, const QGLFormat& b) @@ -1260,6 +1393,45 @@ bool operator!=(const QGLFormat& a, const QGLFormat& b) /***************************************************************************** QGLContext implementation *****************************************************************************/ + +QGLContextGroup::~QGLContextGroup() +{ + // Clear any remaining QGLSharedResourceGuard objects on the group. + QGLSharedResourceGuard *guard = m_guards; + while (guard != 0) { + guard->m_group = 0; + guard->m_id = 0; + guard = guard->m_next; + } +} + +void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard) +{ + if (m_guards) + m_guards->m_prev = guard; + guard->m_next = m_guards; + guard->m_prev = 0; + m_guards = guard; +} + +void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard) +{ + if (guard->m_next) + guard->m_next->m_prev = guard->m_prev; + if (guard->m_prev) + guard->m_prev->m_next = guard->m_next; + else + m_guards = guard->m_next; +} + +QGLContextPrivate::~QGLContextPrivate() +{ + if (!group->m_refs.deref()) { + Q_ASSERT(group->context() == q_ptr); + delete group; + } +} + void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) { Q_Q(QGLContext); @@ -1288,15 +1460,17 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) #endif #if defined(QT_OPENGL_ES) eglContext = 0; + eglSurface = EGL_NO_SURFACE; #endif - pbo = 0; + fbo = 0; crWin = false; initDone = false; sharing = false; - clear_on_painter_begin = true; max_texture_size = -1; version_flags_cached = false; version_flags = QGLFormat::OpenGL_Version_None; + current_fbo = 0; + active_engine = 0; } QGLContext* QGLContext::currentCtx = 0; @@ -1307,12 +1481,8 @@ QGLContext* QGLContext::currentCtx = 0; QGLFramebufferObject::toImage() */ -QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +static void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha) { - QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); - int w = size.width(); - int h = size.height(); - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { // OpenGL gives RGBA; Qt wants ARGB uint *p = (uint*)img.bits(); @@ -1343,7 +1513,30 @@ QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include } } - return img.mirrored(); + img = img.mirrored(); +} + +QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +{ + QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); + int w = size.width(); + int h = size.height(); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + convertFromGLImage(img, w, h, alpha_format, include_alpha); + return img; +} + +QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha) +{ + QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); + int w = size.width(); + int h = size.height(); +#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + //### glGetTexImage not in GL ES 2.0, need to do something else here! + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); +#endif + convertFromGLImage(img, w, h, alpha_format, include_alpha); + return img; } // returns the highest number closest to v, which is a power of 2 @@ -1360,39 +1553,120 @@ 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; + + QImagePixmapCleanupHooks::instance()->addPixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->addImageHook(imageCleanupHook); +} + +QGLTextureCache::~QGLTextureCache() +{ + qt_gl_texture_cache = 0; + + QImagePixmapCleanupHooks::instance()->removePixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->removeImageHook(imageCleanupHook); +} + +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->options |= QGLContext::MemoryManagedBindOption; // 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::imageCleanupHook(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->options & QGLContext::MemoryManagedBindOption) + instance()->remove(cacheKey); +} + + +void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap) +{ + // ### remove when the GL texture cache becomes thread-safe + if (qApp->thread() == QThread::currentThread()) { + const qint64 cacheKey = pixmap->cacheKey(); + QGLTexture *texture = instance()->getTexture(cacheKey); + if (texture && texture->options & QGLContext::MemoryManagedBindOption) + instance()->remove(cacheKey); + } +#if defined(Q_WS_X11) + QPixmapData *pd = pixmap->data_ptr().data(); + if (pd->classId() == QPixmapData::X11Class) { + Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken + QGLContextPrivate::destroyGlSurfaceForPixmap(pd); + } +#endif +} + +void QGLTextureCache::deleteIfEmpty() +{ + if (instance()->size() == 0) + delete instance(); +} + // DDS format structure struct DDSFormat { quint32 dwSize; @@ -1429,7 +1703,7 @@ struct DDSFormat { #define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 #endif -Q_GLOBAL_STATIC(QGLShareRegister, _qgl_share_reg); +Q_GLOBAL_STATIC(QGLShareRegister, _qgl_share_reg) Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() { return _qgl_share_reg(); @@ -1439,7 +1713,7 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() \class QGLContext \brief The QGLContext class encapsulates an OpenGL rendering context. - \ingroup multimedia + \ingroup painting-3D An OpenGL rendering context is a complete set of OpenGL state variables. The rendering context's \l {QGL::FormatOption} {format} @@ -1464,6 +1738,45 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() Please note that QGLContext is not thread safe. */ +/*! + \enum QGLContext::BindOption + A set of options to decide how to bind a texture using bindTexture(). + + \value NoBindOption Don't do anything, pass the texture straight + thru. + + \value InvertedYBindOption Specifies that the texture should be flipped + over the X axis so that the texture coordinate 0,0 corresponds to + the top left corner. Inverting the texture implies a deep copy + prior to upload. + + \value MipmapBindOption Specifies that bindTexture() should try + to generate mipmaps. If the GL implementation supports the \c + GL_SGIS_generate_mipmap extension, mipmaps will be automatically + generated for the texture. Mipmap generation is only supported for + the \c GL_TEXTURE_2D target. + + \value PremultipliedAlphaBindOption Specifies that the image should be + uploaded with premultiplied alpha and does a conversion accordingly. + + \value LinearFilteringBindOption Specifies that the texture filtering + should be set to GL_LINEAR. Default is GL_NEAREST. If mipmap is + also enabled, filtering will be set to GL_LINEAR_MIPMAP_LINEAR. + + \value DefaultBindOption In Qt 4.5 and earlier, bindTexture() + would mirror the image and automatically generate mipmaps. This + option helps preserve this default behavior. + + \omitvalue CanFlipNativePixmapBindOption Used by x11 from pixmap to choose + wether or not it can bind the pixmap upside down or not. + + \omitvalue MemoryManagedBindOption Used by paint engines to + indicate that the pixmap should be memory managed along side with + the pixmap/image that it stems from, e.g. installing destruction + hooks in them. + + \omitvalue InternalBindOption +*/ /*! \obsolete @@ -1484,8 +1797,8 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() */ QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device) + : d_ptr(new QGLContextPrivate(this)) { - d_ptr = new QGLContextPrivate(this); Q_D(QGLContext); d->init(device, format); } @@ -1507,8 +1820,8 @@ QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device) \sa format(), isValid() */ QGLContext::QGLContext(const QGLFormat &format) + : d_ptr(new QGLContextPrivate(this)) { - d_ptr = new QGLContextPrivate(this); Q_D(QGLContext); d->init(0, format); } @@ -1519,37 +1832,18 @@ QGLContext::QGLContext(const QGLFormat &format) 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 + + d_ptr->group->cleanupResources(this); QGLSignalProxy::instance()->emitAboutToDestroyContext(this); reset(); - delete d; } void QGLContextPrivate::cleanup() { - Q_Q(QGLContext); - if (pbo) { - QGLContext *ctx = q; - glDeleteBuffersARB(1, &pbo); - pbo = 0; - } } typedef QHash<QString, GLuint> QGLDDSCache; @@ -1666,76 +1960,108 @@ 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) { - Q_ASSERT(dst.size() == img.size()); Q_ASSERT(dst.depth() == 32); Q_ASSERT(img.depth() == 32); - const int width = img.width(); - const int height = img.height(); - const uint *p = (const uint*) img.scanLine(img.height() - 1); - uint *q = (uint*) dst.scanLine(0); - - if (texture_format == GL_BGRA) { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - // mirror + swizzle - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 24) & 0xff000000) - | ((*p >> 24) & 0x000000ff) - | ((*p << 8) & 0x00ff0000) - | ((*p >> 8) & 0x0000ff00); - p++; - q++; + if (dst.size() != img.size()) { + int target_width = dst.width(); + int target_height = dst.height(); + qreal sx = target_width / qreal(img.width()); + qreal sy = target_height / qreal(img.height()); + + quint32 *dest = (quint32 *) dst.scanLine(0); // NB! avoid detach here + uchar *srcPixels = (uchar *) img.scanLine(img.height() - 1); + int sbpl = img.bytesPerLine(); + int dbpl = dst.bytesPerLine(); + + int ix = int(0x00010000 / sx); + int iy = int(0x00010000 / sy); + + quint32 basex = int(0.5 * ix); + quint32 srcy = int(0.5 * iy); + + // scale, swizzle and mirror in one loop + while (target_height--) { + const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl); + int srcx = basex; + for (int x=0; x<target_width; ++x) { + uint src_pixel = src[srcx >> 16]; + if (texture_format == GL_BGRA) { + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + dest[x] = ((src_pixel << 24) & 0xff000000) + | ((src_pixel >> 24) & 0x000000ff) + | ((src_pixel << 8) & 0x00ff0000) + | ((src_pixel >> 8) & 0x0000ff00); + } else { + dest[x] = src_pixel; + } + } else { // GL_RGBA + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + dest[x] = (src_pixel << 8) | ((src_pixel >> 24) & 0xff); + } else { + dest[x] = ((src_pixel << 16) & 0xff0000) + | ((src_pixel >> 16) & 0xff) + | (src_pixel & 0xff00ff00); + } } - p -= 2 * width; - } - } else { - const uint bytesPerLine = img.bytesPerLine(); - for (int i=0; i < height; ++i) { - memcpy(q, p, bytesPerLine); - q += width; - p -= width; + srcx += ix; } + dest = (quint32 *)(((uchar *) dest) + dbpl); + srcy += iy; } } else { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = (*p << 8) | ((*p >> 24) & 0xFF); - p++; - q++; + const int width = img.width(); + const int height = img.height(); + const uint *p = (const uint*) img.scanLine(img.height() - 1); + uint *q = (uint*) dst.scanLine(0); + + if (texture_format == GL_BGRA) { + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // mirror + swizzle + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 24) & 0xff000000) + | ((*p >> 24) & 0x000000ff) + | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00); + p++; + q++; + } + p -= 2 * width; + } + } else { + const uint bytesPerLine = img.bytesPerLine(); + for (int i=0; i < height; ++i) { + memcpy(q, p, bytesPerLine); + q += width; + p -= width; } - p -= 2 * width; } } else { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); - p++; - q++; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = (*p << 8) | ((*p >> 24) & 0xff); + p++; + q++; + } + p -= 2 * width; + } + } else { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + q++; + } + p -= 2 * width; } - p -= 2 * width; } } } @@ -1753,161 +2079,280 @@ QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_prem return result; } -GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, - const qint64 key, bool clean) +/*! \internal */ +QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, + QGLContext::BindOptions options) { - Q_Q(QGLContext); + const qint64 key = image.cacheKey(); + QGLTexture *texture = textureCacheLookup(key, target); + if (texture) { + glBindTexture(target, texture->id); + return texture; + } - QGLContext *ctx = q; + if (!texture) + texture = bindTexture(image, target, format, key, options); + // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null + Q_ASSERT(texture); - bool use_pbo = false; - if (QGLExtensions::glExtensions & QGLExtensions::PixelBufferObject) { + if (texture->id > 0) + const_cast<QImage &>(image).data_ptr()->is_cached = true; - use_pbo = qt_resolve_buffer_extensions(ctx); - if (use_pbo && pbo == 0) - glGenBuffersARB(1, &pbo); - } + return texture; +} - // 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; - } +// #define QGL_BIND_TEXTURE_DEBUG + +QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint internalFormat, + const qint64 key, QGLContext::BindOptions options) +{ + Q_Q(QGLContext); + +#ifdef QGL_BIND_TEXTURE_DEBUG + printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x\n", + image.width(), image.height(), internalFormat, int(options)); + QTime time; + time.start(); +#endif // Scale the pixmap if needed. GL textures needs to have the - // dimensions 2^n+2(border) x 2^m+2(border). + // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL + // 2.0 or use the GL_TEXTURE_RECTANGLE texture target int tx_w = qt_next_power_of_two(image.width()); int tx_h = qt_next_power_of_two(image.height()); - // Note: the clean param is only true when a texture is bound - // from the QOpenGLPaintEngine - in that case we have to force - // a premultiplied texture format QImage img = image; if (( !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0) ) && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height()))) { - img = image.scaled(tx_w, tx_h); + img = img.scaled(tx_w, tx_h); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed()); + +#endif } + GLuint filtering = options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST; + GLuint tx_id; glGenTextures(1, &tx_id); glBindTexture(target, tx_id); - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, filtering); + +#if defined(QT_OPENGL_ES_2) + bool genMipmap = false; +#endif if (glFormat.directRendering() && QGLExtensions::glExtensions & QGLExtensions::GenerateMipmap - && target == GL_TEXTURE_2D && !clean) + && target == GL_TEXTURE_2D + && options & QGLContext::MipmapBindOption) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - generating mipmaps (%d ms)\n", time.elapsed()); +#endif +#if !defined(QT_OPENGL_ES_2) glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); #ifndef QT_OPENGL_ES glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); #else glTexParameterf(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); #endif - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +#else + glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); + genMipmap = true; +#endif + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, options & QGLContext::LinearFilteringBindOption + ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST); + } else { + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, filtering); + } - // Mipmap generation causes huge slowdown with PBO's for some reason - use_pbo = false; + QImage::Format target_format = img.format(); + bool premul = options & QGLContext::PremultipliedAlphaBindOption; + GLenum externalFormat; + GLuint pixel_type; + if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) { + externalFormat = GL_BGRA; + pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; } else { - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + externalFormat = GL_RGBA; + pixel_type = GL_UNSIGNED_BYTE; } - uchar *ptr = 0; - if (use_pbo) { - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, img.width() * img.height() * 4, 0, GL_STREAM_DRAW_ARB); - ptr = reinterpret_cast<uchar *>(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB)); + switch (target_format) { + case QImage::Format_ARGB32: + if (premul) { + img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed()); +#endif + } + break; + case QImage::Format_ARGB32_Premultiplied: + if (!premul) { + img = img.convertToFormat(target_format = QImage::Format_ARGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed()); +#endif + } + break; + case QImage::Format_RGB16: + pixel_type = GL_UNSIGNED_SHORT_5_6_5; + externalFormat = GL_RGB; + internalFormat = GL_RGB; + break; + case QImage::Format_RGB32: + break; + default: + if (img.hasAlphaChannel()) { + img = img.convertToFormat(premul + ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_ARGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed()); +#endif + } else { + img = img.convertToFormat(QImage::Format_RGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting to 32-bit (%d ms)\n", time.elapsed()); +#endif + } } - if (ptr) { - QImage::Format target_format = img.format(); - if (clean || img.format() != QImage::Format_ARGB32) - target_format = QImage::Format_ARGB32_Premultiplied; + if (options & QGLContext::InvertedYBindOption) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - flipping bits over y (%d ms)\n", time.elapsed()); +#endif + int ipl = img.bytesPerLine() / 4; + int h = img.height(); + for (int y=0; y<h/2; ++y) { + int *a = (int *) img.scanLine(y); + int *b = (int *) img.scanLine(h - y - 1); + for (int x=0; x<ipl; ++x) + qSwap(a[x], b[x]); + } + } - QImage buffer(ptr, img.width(), img.height(), target_format); - convertToGLFormatHelper(buffer, img.convertToFormat(target_format), texture_format); - glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); - glTexImage2D(target, 0, format, img.width(), img.height(), 0, texture_format, GL_UNSIGNED_BYTE, 0); - } else { - QImage tx = convertToGLFormat(img, clean, texture_format); - glTexImage2D(target, 0, format, tx.width(), tx.height(), 0, texture_format, - GL_UNSIGNED_BYTE, tx.bits()); + if (externalFormat == GL_RGBA) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - doing byte swapping (%d ms)\n", time.elapsed()); +#endif + // The only case where we end up with a depth different from + // 32 in the switch above is for the RGB16 case, where we set + // the format to GL_RGB + Q_ASSERT(img.depth() == 32); + const int width = img.width(); + const int height = img.height(); + + if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV + || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) { + for (int i=0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x=0; x<width; ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } + } else { + for (int i=0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x=0; x<width; ++x) + p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff); + } + } } +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n", + img.format(), externalFormat, internalFormat, pixel_type); +#endif + + const QImage &constRef = img; // to avoid detach in bits()... + glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat, + pixel_type, constRef.bits()); +#if defined(QT_OPENGL_ES_2) + if (genMipmap) + glGenerateMipmap(target); +#endif +#ifndef QT_NO_DEBUG + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + qWarning(" - texture upload failed, error code 0x%x\n", error); + } +#endif + +#ifdef QGL_BIND_TEXTURE_DEBUG + static int totalUploadTime = 0; + totalUploadTime += time.elapsed(); + printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime); +#endif - if (use_pbo) - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); // 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, options); + 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 || QGLContext::areSharing(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, QGLContext::BindOptions options) { -#if !defined(QT_OPENGL_ES_2) - if (target == qt_gl_preferredTextureTarget() && pixmap.pixmapData()->classId() == QPixmapData::OpenGLClass) { - const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pixmap.pixmapData()); - - if (data->isValidContext(QGLContext::currentContext())) - return data->bind(); + Q_Q(QGLContext); + QPixmapData *pd = pixmap.pixmapData(); +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) { + const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd); + + if (data->isValidContext(q)) { + data->bind(); + return data->texture(); + } } +#else + Q_UNUSED(pd); + Q_UNUSED(q); #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) { + texture = bindTextureFromNativePixmap(pd, key, options); + if (texture) { + texture->options |= QGLContext::MemoryManagedBindOption; + texture->boundPixmap = pd; + boundPixmaps.insert(pd, 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, options); + // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null + Q_ASSERT(texture); + + if (texture->id > 0) + const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = true; + + return texture; } /*! \internal */ @@ -1946,6 +2391,22 @@ int QGLContextPrivate::maxTextureSize() } /*! + Generates and binds a 2D GL texture to the current context, based + on \a image. The generated texture id is returned and can be used in + later \c glBindTexture() calls. + + \overload +*/ +GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) +{ + Q_D(QGLContext); + QGLTexture *texture = d->bindTexture(image, target, format, false, DefaultBindOption); + return texture->id; +} + +/*! + \since 4.6 + Generates and binds a 2D GL texture to the current context, based on \a image. The generated texture id is returned and can be used in later \c glBindTexture() calls. @@ -1954,12 +2415,10 @@ int QGLContextPrivate::maxTextureSize() target is \c GL_TEXTURE_2D. The \a format parameter sets the internal format for the - texture. The default format is \c GL_RGBA8. + texture. The default format is \c GL_RGBA. - If the GL implementation supports the \c GL_SGIS_generate_mipmap - extension, mipmaps will be automatically generated for the - texture. Mipmap generation is only supported for the \c - GL_TEXTURE_2D target. + The binding \a options are a set of options used to decide how to + bind the texture to the context. The texture that is generated is cached, so multiple calls to bindTexture() with the same QImage will return the same texture @@ -1970,10 +2429,11 @@ int QGLContextPrivate::maxTextureSize() \sa deleteTexture() */ -GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) +GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options) { Q_D(QGLContext); - return d->bindTexture(image, target, format, false); + QGLTexture *texture = d->bindTexture(image, target, format, false, options); + return texture->id; } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -1981,7 +2441,17 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format) { Q_D(QGLContext); - return d->bindTexture(image, GLenum(target), GLint(format), false); + QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, DefaultBindOption); + return texture->id; +} + +/*! \internal */ +GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format, + BindOptions options) +{ + Q_D(QGLContext); + QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, options); + return texture->id; } #endif @@ -1992,7 +2462,22 @@ 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, DefaultBindOption); + return texture->id; +} + +/*! + \overload + \since 4.6 + + Generates and binds a 2D GL texture to the current context, based + on \a pixmap. +*/ +GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, BindOptions options) +{ + Q_D(QGLContext); + QGLTexture *texture = d->bindTexture(pixmap, target, format, options); + return texture->id; } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -2000,7 +2485,16 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format) { Q_D(QGLContext); - return d->bindTexture(pixmap, GLenum(target), GLint(format), false); + QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), DefaultBindOption); + return texture->id; +} +/*! \internal */ +GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format, + BindOptions options) +{ + Q_D(QGLContext); + QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), options); + return texture->id; } #endif @@ -2013,17 +2507,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 @@ -2046,12 +2531,36 @@ void QGLContext::deleteTexture(QMacCompatGLuint id) } #endif -// qpaintengine_opengl.cpp +void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) +{ + qreal left = r.left(); + qreal right = r.right(); + qreal top = r.top(); + qreal bottom = r.bottom(); + + array[0] = f2vt(left); + array[1] = f2vt(top); + array[2] = f2vt(right); + array[3] = f2vt(top); + array[4] = f2vt(right); + array[5] = f2vt(bottom); + array[6] = f2vt(left); + array[7] = f2vt(bottom); +} + +void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array) +{ + array[0] = f2vt(x1); + array[1] = f2vt(y1); + array[2] = f2vt(x2); + array[3] = f2vt(y1); + array[4] = f2vt(x2); + array[5] = f2vt(y2); + array[6] = f2vt(x1); + array[7] = f2vt(y2); +} + #if !defined(QT_OPENGL_ES_2) -extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); -#else -void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) {}; -#endif static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget) { @@ -2081,7 +2590,6 @@ static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint tex q_vertexType vertexArray[4*2]; qt_add_rect_to_array(target, vertexArray); -#if !defined(QT_OPENGL_ES_2) glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray); glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray); @@ -2091,9 +2599,10 @@ static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint tex glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); -#endif } +#endif // !QT_OPENGL_ES_2 + /*! \since 4.4 @@ -2101,10 +2610,11 @@ static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint tex \a target, in OpenGL model space. The \a textureTarget should be a 2D texture target. - Equivalent to the corresponding QGLContext::drawTexture(). + \note This function is not supported under OpenGL/ES 2.0. */ void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) { +#ifndef QT_OPENGL_ES_2 #ifdef QT_OPENGL_ES if (textureTarget != GL_TEXTURE_2D) { qWarning("QGLContext::drawTexture(): texture target must be GL_TEXTURE_2D on OpenGL ES"); @@ -2128,6 +2638,12 @@ void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum text glDisable(textureTarget); glBindTexture(textureTarget, oldTexture); #endif +#else + Q_UNUSED(target); + Q_UNUSED(textureId); + Q_UNUSED(textureTarget); + qWarning("drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES/2.0"); +#endif } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -2144,7 +2660,7 @@ void QGLContext::drawTexture(const QRectF &target, QMacCompatGLuint textureId, Q Draws the given texture at the given \a point in OpenGL model space. The \a textureTarget should be a 2D texture target. - Equivalent to the corresponding QGLContext::drawTexture(). + \note This function is not supported under OpenGL/ES. */ void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) { @@ -2195,9 +2711,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); } /*! @@ -2207,7 +2721,7 @@ void QGLContext::setTextureCacheLimit(int size) */ int QGLContext::textureCacheLimit() { - return qt_tex_cache_limit; + return QGLTextureCache::instance()->maxCost(); } @@ -2288,6 +2802,20 @@ void QGLContext::setDevice(QPaintDevice *pDev) */ /*! + Returns true if \a context1 and \a context2 are sharing their + GL resources such as textures, shader programs, etc; + otherwise returns false. + + \since 4.6 +*/ +bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2) +{ + if (!context1 || !context2) + return false; + return context1->d_ptr->group == context2->d_ptr->group; +} + +/*! \fn bool QGLContext::deviceIsPixmap() const Returns true if the paint device of this context is a pixmap; @@ -2485,11 +3013,28 @@ void QGLContext::setInitialized(bool on) const QGLContext* QGLContext::currentContext() { - if (qgl_context_storage.hasLocalData()) - return qgl_context_storage.localData()->context; + QGLThreadContext *threadContext = qgl_context_storage.localData(); + if (threadContext) + return threadContext->context; return 0; } +void QGLContextPrivate::setCurrentContext(QGLContext *context) +{ + QGLThreadContext *threadContext = qgl_context_storage.localData(); + if (!threadContext) { + if (!QThread::currentThread()) { + // We don't have a current QThread, so just set the static. + QGLContext::currentCtx = context; + return; + } + threadContext = new QGLThreadContext; + qgl_context_storage.setLocalData(threadContext); + } + threadContext->context = context; + QGLContext::currentCtx = context; // XXX: backwards-compat, not thread-safe +} + /*! \fn bool QGLContext::chooseContext(const QGLContext* shareContext = 0) @@ -2505,7 +3050,7 @@ const QGLContext* QGLContext::currentContext() */ /*! \fn int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc) - + \bold{Win32 only:} This virtual function chooses a pixel format that matches the OpenGL \link setFormat() format\endlink. Reimplement this function in a subclass if you need a custom @@ -2519,7 +3064,7 @@ const QGLContext* QGLContext::currentContext() */ /*! \fn void *QGLContext::chooseVisual() - + \bold{X11 only:} This virtual function tries to find a visual that matches the format, reducing the demands if the original request cannot be met. @@ -2608,8 +3153,8 @@ const QGLContext* QGLContext::currentContext() \class QGLWidget \brief The QGLWidget class is a widget for rendering OpenGL graphics. - \ingroup multimedia - \mainclass + \ingroup painting-3D + QGLWidget provides functionality for displaying OpenGL graphics integrated into a Qt application. It is very simple to use. You @@ -2650,7 +3195,7 @@ const QGLContext* QGLContext::currentContext() QGLFormat format\endlink and you can also create widgets with customized rendering \link QGLContext contexts\endlink. - You can also share OpenGL display lists between QGLWidgets (see + You can also share OpenGL display lists between QGLWidget objects (see the documentation of the QGLWidget constructors for details). Note that under Windows, the QGLContext belonging to a QGLWidget @@ -2932,7 +3477,7 @@ bool QGLWidget::isValid() const Returns true if this widget's GL context is shared with another GL context, otherwise false is returned. Context sharing might not be - possible if the QGLWidgets use different formats. + possible if the widgets use different formats. \sa format() */ @@ -3030,7 +3575,7 @@ void QGLWidget::swapBuffers() resizeGL() or paintGL(). This method will try to keep display list and texture object sharing - in effect with other QGLWidgets, but changing the format might make + in effect with other QGLWidget objects, but changing the format might make sharing impossible. Use isSharing() to see if sharing is still in effect. @@ -3238,21 +3783,29 @@ bool QGLWidget::event(QEvent *e) glFinish(); doneCurrent(); } else if (e->type() == QEvent::ParentChange) { - if (d->glcx->d_func()->screen != d->xinfo.screen()) { + // if we've reparented a window that has the current context + // bound, we need to rebind that context to the new window id + if (d->glcx == QGLContext::currentContext()) + makeCurrent(); + + if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) { setContext(new QGLContext(d->glcx->requestedFormat(), this)); // ### recreating the overlay isn't supported atm } + } + #if defined(QT_OPENGL_ES) - // The window may have been re-created during re-parent - if so, the EGL + if ((e->type() == QEvent::ParentChange) || (e->type() == QEvent::WindowStateChange)) { + // The window may have been re-created during re-parent or state change - if so, the EGL // surface will need to be re-created. d->recreateEglSurface(false); -#endif } +#endif #elif defined(Q_WS_WIN) if (e->type() == QEvent::ParentChange) { QGLContext *newContext = new QGLContext(d->glcx->requestedFormat(), this); - qgl_share_reg()->replaceShare(d->glcx, newContext); - setContext(newContext); + setContext(newContext, d->glcx); + // the overlay needs to be recreated as well delete d->olcx; if (isValid() && context()->format().hasOverlay()) { @@ -3331,7 +3884,7 @@ void QGLWidget::paintEvent(QPaintEvent *) /*! Renders the current scene on a pixmap and returns the pixmap. - You can use this method on both visible and invisible QGLWidgets. + You can use this method on both visible and invisible QGLWidget objects. This method will create a pixmap and a temporary QGLContext to render on the pixmap. It will then call initializeGL(), @@ -3517,6 +4070,8 @@ void QGLWidget::glDraw() Calls glColor4 (in RGBA mode) or glIndex (in color-index mode) with the color \a c. Applies to this widgets GL context. + \note This function is not supported on OpenGL/ES 2.0 systems. + \sa qglClearColor(), QGLContext::currentContext(), QColor */ @@ -3524,7 +4079,7 @@ void QGLWidget::qglColor(const QColor& c) const { #if !defined(QT_OPENGL_ES_2) #ifdef QT_OPENGL_ES - glColor4f(c.red()/255.0, c.green()/255.0, c.blue()/255.0, c.alpha()/255.0); + glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); #else Q_D(const QGLWidget); const QGLContext *ctx = QGLContext::currentContext(); @@ -3540,6 +4095,8 @@ void QGLWidget::qglColor(const QColor& c) const glIndexi(ctx->colorIndex(c)); } #endif //QT_OPENGL_ES +#else + Q_UNUSED(c); #endif //QT_OPENGL_ES_2 } @@ -3554,15 +4111,13 @@ void QGLWidget::qglColor(const QColor& c) const void QGLWidget::qglClearColor(const QColor& c) const { #ifdef QT_OPENGL_ES - glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0, - (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0); + glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); #else Q_D(const QGLWidget); const QGLContext *ctx = QGLContext::currentContext(); if (ctx) { if (ctx->format().rgba()) - glClearColor((GLfloat)c.red() / 255.0, (GLfloat)c.green() / 255.0, - (GLfloat)c.blue() / 255.0, (GLfloat) c.alpha() / 255.0); + glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); else if (!d->cmap.isEmpty()) { // QGLColormap in use? int i = d->cmap.find(c.rgb()); if (i < 0) @@ -3633,10 +4188,10 @@ QImage QGLWidget::convertToGLFormat(const QImage& img) colormaps installed. Asking for the colormap of a child widget will return the colormap for the child's top-level widget. - If no colormap has been set for this widget, the QColormap + If no colormap has been set for this widget, the QGLColormap returned will be empty. - \sa setColormap() + \sa setColormap(), QGLColormap::isEmpty() */ /*! @@ -3656,9 +4211,12 @@ QImage QGLWidget::convertToGLFormat(const QImage& img) the characters in the given \a font. \a listBase indicates the base value used when generating the display lists for the font. The default value is 2000. + + \note This function is not supported on OpenGL/ES systems. */ int QGLWidget::fontDisplayListBase(const QFont & font, int listBase) { +#ifndef QT_OPENGL_ES Q_D(QGLWidget); int base; @@ -3676,9 +4234,7 @@ int QGLWidget::fontDisplayListBase(const QFont & font, int listBase) QString color_key; if (font.styleStrategy() != QFont::NoAntialias) { GLfloat color[4]; -#ifndef QT_OPENGL_ES glGetFloatv(GL_CURRENT_COLOR, color); -#endif color_key.sprintf("%f_%f_%f",color[0], color[1], color[2]); } QString key = font.key() + color_key + QString::number((int) regenerate); @@ -3701,15 +4257,19 @@ int QGLWidget::fontDisplayListBase(const QFont & font, int listBase) base = maxBase; } return base; +#else // QT_OPENGL_ES + Q_UNUSED(font); + Q_UNUSED(listBase); + return 0; +#endif } +#ifndef QT_OPENGL_ES + static void qt_save_gl_state() { -#ifndef QT_OPENGL_ES glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); glPushAttrib(GL_ALL_ATTRIB_BITS); -#endif -#if !defined(QT_OPENGL_ES_2) glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); @@ -3724,32 +4284,25 @@ static void qt_save_gl_state() glDisable(GL_STENCIL_TEST); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -#endif // !defined(QT_OPENGL_ES_2) } static void qt_restore_gl_state() { -#if !defined(QT_OPENGL_ES_2) glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); -#endif // !defined(QT_OPENGL_ES_2) -#ifndef QT_OPENGL_ES glPopAttrib(); glPopClientAttrib(); -#endif } static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str, const QFont &font) { GLfloat color[4]; -#ifndef QT_OPENGL_ES glGetFloatv(GL_CURRENT_COLOR, &color[0]); -#endif QColor col; col.setRgbF(color[0], color[1], color[2],color[3]); @@ -3764,6 +4317,8 @@ static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str, p->setFont(old_font); } +#endif // !QT_OPENGL_ES + /*! Renders the string \a str into the GL context of this widget. @@ -3778,27 +4333,29 @@ static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str, future version of Qt. \note This function clears the stencil buffer. + + \note This function is not supported on OpenGL/ES systems. + \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead. */ void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, int) { +#ifndef QT_OPENGL_ES Q_D(QGLWidget); if (str.isEmpty() || !isValid()) return; GLint view[4]; -#ifndef QT_OPENGL_ES bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST); if (!use_scissor_testing) glGetIntegerv(GL_VIEWPORT, &view[0]); -#else - bool use_scissor_testing = false; -#endif int width = d->glcx->device()->width(); int height = d->glcx->device()->height(); bool auto_swap = autoBufferSwap(); QPaintEngine *engine = paintEngine(); + if (engine->type() == QPaintEngine::OpenGL2) + static_cast<QGL2PaintEngineEx *>(engine)->setRenderTextActive(true); QPainter *p; bool reuse_painter = false; if (engine->isActive()) { @@ -3806,24 +4363,23 @@ void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, p = engine->painter(); qt_save_gl_state(); -#if !defined(QT_OPENGL_ES_2) glDisable(GL_DEPTH_TEST); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); -#ifndef QT_OPENGL_ES glOrtho(0, width, height, 0, 0, 1); -#else - glOrthof(0, width, height, 0, 0, 1); -#endif glMatrixMode(GL_MODELVIEW); glLoadIdentity(); -#endif // !defined(QT_OPENGL_ES_2) } else { setAutoBufferSwap(false); // disable glClear() as a result of QPainter::begin() - d->glcx->d_func()->clear_on_painter_begin = false; + d->disable_clear_on_painter_begin = true; + if (engine->type() == QPaintEngine::OpenGL2) { + qt_save_gl_state(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } p = new QPainter(this); } @@ -3846,8 +4402,19 @@ void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, p->end(); delete p; setAutoBufferSwap(auto_swap); - d->glcx->d_func()->clear_on_painter_begin = true; + d->disable_clear_on_painter_begin = false; + if (engine->type() == QPaintEngine::OpenGL2) + qt_restore_gl_state(); } + if (engine->type() == QPaintEngine::OpenGL2) + static_cast<QGL2PaintEngineEx *>(engine)->setRenderTextActive(false); +#else // QT_OPENGL_ES + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(str); + Q_UNUSED(font); + qWarning("QGLWidget::renderText is not supported under OpenGL/ES"); +#endif } /*! \overload @@ -3856,9 +4423,13 @@ void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font, relative to the currently set projection and model matrices. This can be useful if you want to annotate models with text labels and have the labels move with the model as it is rotated etc. + + \note This function is not supported on OpenGL/ES systems. + \l{Overpainting Example}{Overpaint} with QPainter::drawText() instead. */ void QGLWidget::renderText(double x, double y, double z, const QString &str, const QFont &font, int) { +#ifndef QT_OPENGL_ES Q_D(QGLWidget); if (str.isEmpty() || !isValid()) return; @@ -3869,26 +4440,21 @@ void QGLWidget::renderText(double x, double y, double z, const QString &str, con int height = d->glcx->device()->height(); GLdouble model[4][4], proj[4][4]; GLint view[4]; -#ifndef QT_OPENGL_ES glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]); glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]); glGetIntegerv(GL_VIEWPORT, &view[0]); -#endif GLdouble win_x = 0, win_y = 0, win_z = 0; qgluProject(x, y, z, &model[0][0], &proj[0][0], &view[0], &win_x, &win_y, &win_z); win_y = height - win_y; // y is inverted QPaintEngine *engine = paintEngine(); + if (engine->type() == QPaintEngine::OpenGL2) + static_cast<QGL2PaintEngineEx *>(engine)->setRenderTextActive(true); QPainter *p; bool reuse_painter = false; -#ifndef QT_OPENGL_ES bool use_depth_testing = glIsEnabled(GL_DEPTH_TEST); bool use_scissor_testing = glIsEnabled(GL_SCISSOR_TEST); -#else - bool use_depth_testing = false; - bool use_scissor_testing = false; -#endif if (engine->isActive()) { reuse_painter = true; @@ -3897,7 +4463,9 @@ void QGLWidget::renderText(double x, double y, double z, const QString &str, con } else { setAutoBufferSwap(false); // disable glClear() as a result of QPainter::begin() - d->glcx->d_func()->clear_on_painter_begin = false; + d->disable_clear_on_painter_begin = true; + if (engine->type() == QPaintEngine::OpenGL2) + qt_save_gl_state(); p = new QPainter(this); } @@ -3908,27 +4476,17 @@ void QGLWidget::renderText(double x, double y, double z, const QString &str, con } else if (use_scissor_testing) { glEnable(GL_SCISSOR_TEST); } -#if !defined(QT_OPENGL_ES_2) glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 0, width, height); -#ifndef QT_OPENGL_ES glOrtho(0, width, height, 0, 0, 1); -#else - glOrthof(0, width, height, 0, 0, 1); -#endif glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glAlphaFunc(GL_GREATER, 0.0); glEnable(GL_ALPHA_TEST); if (use_depth_testing) glEnable(GL_DEPTH_TEST); -#ifndef QT_OPENGL_ES glTranslated(0, 0, -win_z); -#else - glTranslatef(0, 0, -win_z); -#endif -#endif // !defined(QT_OPENGL_ES_2) qt_gl_draw_text(p, qRound(win_x), qRound(win_y), str, font); if (reuse_painter) { @@ -3936,9 +4494,21 @@ void QGLWidget::renderText(double x, double y, double z, const QString &str, con } else { p->end(); delete p; + if (engine->type() == QPaintEngine::OpenGL2) + qt_restore_gl_state(); setAutoBufferSwap(auto_swap); - d->glcx->d_func()->clear_on_painter_begin = true; + d->disable_clear_on_painter_begin = false; } + if (engine->type() == QPaintEngine::OpenGL2) + static_cast<QGL2PaintEngineEx *>(engine)->setRenderTextActive(false); +#else // QT_OPENGL_ES + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); + Q_UNUSED(str); + Q_UNUSED(font); + qWarning("QGLWidget::renderText is not supported under OpenGL/ES"); +#endif } QGLFormat QGLWidget::format() const @@ -3980,15 +4550,36 @@ bool QGLWidget::autoBufferSwap() const GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format) { Q_D(QGLWidget); - return d->glcx->bindTexture(image, target, format); + return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption); } +/*! + \overload + \since 4.6 + + The binding \a options are a set of options used to decide how to + bind the texture to the context. + */ +GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options) +{ + Q_D(QGLWidget); + return d->glcx->bindTexture(image, target, format, options); +} + + #ifdef Q_MAC_COMPAT_GL_FUNCTIONS /*! \internal */ GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format) { Q_D(QGLWidget); - return d->glcx->bindTexture(image, GLenum(target), GLint(format)); + return d->glcx->bindTexture(image, GLenum(target), GLint(format), QGLContext::DefaultBindOption); +} + +GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format, + QGLContext::BindOptions options) +{ + Q_D(QGLWidget); + return d->glcx->bindTexture(image, GLenum(target), GLint(format), options); } #endif @@ -4001,7 +4592,24 @@ GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMac GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { Q_D(QGLWidget); - return d->glcx->bindTexture(pixmap, target, format); + return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption); +} + +/*! + \overload + \since 4.6 + + Generates and binds a 2D GL texture to the current context, based + on \a pixmap. The generated texture id is returned and can be used in + + The binding \a options are a set of options used to decide how to + bind the texture to the context. + */ +GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, + QGLContext::BindOptions options) +{ + Q_D(QGLWidget); + return d->glcx->bindTexture(pixmap, target, format, options); } #ifdef Q_MAC_COMPAT_GL_FUNCTIONS @@ -4009,7 +4617,14 @@ GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format) { Q_D(QGLWidget); - return d->glcx->bindTexture(pixmap, target, format); + return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption); +} + +GLuint QGLWidget::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format, + QGLContext::BindOptions options) +{ + Q_D(QGLWidget); + return d->glcx->bindTexture(pixmap, target, format, options); } #endif @@ -4094,14 +4709,16 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM } #endif -#if defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_engine) -#else +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine) +#endif + +#ifndef QT_OPENGL_ES_2 Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine) #endif #ifdef Q_WS_QWS -Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine() +Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine() { #if !defined(QT_OPENGL_ES_2) return qt_gl_engine(); @@ -4119,7 +4736,16 @@ Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine() */ QPaintEngine *QGLWidget::paintEngine() const { +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) return qt_gl_engine(); +#elif defined(QT_OPENGL_ES_2) + return qt_gl_2_engine(); +#else + if (qt_gl_preferGL2Engine()) + return qt_gl_2_engine(); + else + return qt_gl_engine(); +#endif } #ifdef QT3_SUPPORT @@ -4208,6 +4834,24 @@ void QGLExtensions::init_extensions() glExtensions |= FramebufferObject; glExtensions |= GenerateMipmap; #endif +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) + if (extensions.contains(QLatin1String("OES_framebuffer_object"))) + glExtensions |= FramebufferObject; +#endif +#if defined(QT_OPENGL_ES) + if (extensions.contains(QLatin1String("OES_packed_depth_stencil"))) + glExtensions |= PackedDepthStencil; +#endif + if (extensions.contains(QLatin1String("ARB_framebuffer_object"))) { + // ARB_framebuffer_object also includes EXT_framebuffer_blit. + glExtensions |= FramebufferObject; + glExtensions |= FramebufferBlit; + } + 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) { @@ -4222,6 +4866,8 @@ void QGLWidgetPrivate::initContext(QGLContext *context, const QGLWidget* shareWi { Q_Q(QGLWidget); + glDevice.setWidget(q); + QGLExtensions::init(); glcx = 0; autoSwap = true; @@ -4237,7 +4883,7 @@ void QGLWidgetPrivate::initContext(QGLContext *context, const QGLWidget* shareWi } #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) -Q_GLOBAL_STATIC(QString, qt_gl_lib_name); +Q_GLOBAL_STATIC(QString, qt_gl_lib_name) Q_OPENGL_EXPORT void qt_set_gl_library_name(const QString& name) { @@ -4248,7 +4894,7 @@ Q_OPENGL_EXPORT const QString qt_gl_library_name() { if (qt_gl_lib_name()->isNull()) { #if defined(Q_WS_X11) || defined(Q_WS_QWS) - return QString(QLatin1String("GL")); + return QLatin1String("GL"); #else // Q_WS_MAC return QLatin1String("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"); #endif @@ -4257,4 +4903,113 @@ Q_OPENGL_EXPORT const QString qt_gl_library_name() } #endif +void QGLShareRegister::addShare(const QGLContext *context, const QGLContext *share) { + Q_ASSERT(context && share); + if (context->d_ptr->group == share->d_ptr->group) + return; + + // Make sure 'context' is not already shared with another group of contexts. + Q_ASSERT(context->d_ptr->group->m_refs == 1); + + // Free 'context' group resources and make it use the same resources as 'share'. + QGLContextGroup *group = share->d_ptr->group; + delete context->d_ptr->group; + context->d_ptr->group = group; + group->m_refs.ref(); + + // Maintain a list of all the contexts in each group of sharing contexts. + // The list is empty if the "share" context wasn't sharing already. + if (group->m_shares.isEmpty()) + group->m_shares.append(share); + group->m_shares.append(context); +} + +QList<const QGLContext *> QGLShareRegister::shares(const QGLContext *context) { + return context->d_ptr->group->m_shares; +} + +void QGLShareRegister::removeShare(const QGLContext *context) { + // Remove the context from the group. + QGLContextGroup *group = context->d_ptr->group; + if (group->m_shares.isEmpty()) + return; + group->m_shares.removeAll(context); + + // Update context group representative. + Q_ASSERT(group->m_shares.size() != 0); + if (group->m_context == context) + group->m_context = group->m_shares[0]; + + // If there is only one context left, then make the list empty. + if (group->m_shares.size() == 1) + group->m_shares.clear(); +} + +QGLContextResource::QGLContextResource(FreeFunc f) + : free(f), active(0) +{ +} + +QGLContextResource::~QGLContextResource() +{ +#ifndef QT_NO_DEBUG + if (active != 0) { + qWarning("QtOpenGL: Resources are still available at program shutdown.\n" + " This is possibly caused by a leaked QGLWidget, \n" + " QGLFramebufferObject or QGLPixelBuffer."); + } +#endif +} + +void QGLContextResource::insert(const QGLContext *key, void *value) +{ + QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + Q_ASSERT(!group->m_resources.contains(this)); + group->m_resources.insert(this, value); + active.ref(); +} + +void *QGLContextResource::value(const QGLContext *key) +{ + QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + return group->m_resources.value(this, 0); +} + +void QGLContextResource::cleanup(const QGLContext *ctx, void *value) +{ + QGLShareContextScope scope(ctx); + free(value); + active.deref(); +} + +void QGLContextGroup::cleanupResources(const QGLContext *ctx) +{ + // If there are still shares, then no cleanup to be done yet. + if (m_shares.size() > 1) + return; + + // Iterate over all resources and free each in turn. + QHash<QGLContextResource *, void *>::ConstIterator it; + for (it = m_resources.begin(); it != m_resources.end(); ++it) + it.key()->cleanup(ctx, it.value()); +} + +QGLSharedResourceGuard::~QGLSharedResourceGuard() +{ + if (m_group) + m_group->removeGuard(this); +} + +void QGLSharedResourceGuard::setContext(const QGLContext *context) +{ + if (m_group) + m_group->removeGuard(this); + if (context) { + m_group = QGLContextPrivate::contextGroup(context); + m_group->addGuard(this); + } else { + m_group = 0; + } +} + QT_END_NAMESPACE |