diff options
-rw-r--r-- | src/opengl/qpixmapdata_gl.cpp | 134 | ||||
-rw-r--r-- | src/opengl/qpixmapdata_gl_p.h | 13 |
2 files changed, 106 insertions, 41 deletions
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index f0c7e20..f6a9bf4 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -55,6 +55,85 @@ QT_BEGIN_NAMESPACE extern QGLWidget* qt_gl_share_widget(); +/*! + \class QGLFramebufferObjectPool + \since 4.6 + + \brief The QGLFramebufferObject class provides a pool of framebuffer + objects for offscreen rendering purposes. + + When requesting an FBO of a given size and format, an FBO of the same + format and a size at least as big as the requested size will be returned. + + \internal +*/ + +static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo) +{ + return qAbs(size.width() * size.height() - fbo->width() * fbo->height()); +} + +QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat) +{ + QGLFramebufferObject *chosen = 0; + QGLFramebufferObject *candidate = 0; + for (int i = 0; !chosen && i < m_fbos.size(); ++i) { + QGLFramebufferObject *fbo = m_fbos.at(i); + + QGLFramebufferObjectFormat format = fbo->format(); + if (format.samples() == requestFormat.samples() + && format.attachment() == requestFormat.attachment() + && format.textureTarget() == requestFormat.textureTarget() + && format.internalFormat() == requestFormat.internalFormat()) + { + // choose the fbo with a matching format and the closest size + if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo)) + candidate = fbo; + } + + if (candidate) { + m_fbos.removeOne(candidate); + + const QSize fboSize = candidate->size(); + QSize sz = fboSize; + + if (sz.width() < requestSize.width()) + sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5))); + if (sz.height() < requestSize.height()) + sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5))); + + // wasting too much space? + if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 2.5) + sz = requestSize; + + if (sz != fboSize) { + delete candidate; + qDebug() << "Resizing fbo in pool:" << sz; + candidate = new QGLFramebufferObject(sz, requestFormat); + } + + chosen = candidate; + } + } + + if (!chosen) { + qDebug() << "Creating new fbo in pool:" << requestSize; + chosen = new QGLFramebufferObject(requestSize, requestFormat); + } + + if (!chosen->isValid()) { + delete chosen; + chosen = 0; + } + + return chosen; +} + +void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo) +{ + m_fbos << fbo; +} + class QGLShareContextScope { public: @@ -330,8 +409,11 @@ struct TextureBuffer QGL2PaintEngineEx *engine; }; -static QVector<TextureBuffer> textureBufferStack; -static int currentTextureBuffer = 0; +Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool) +QGLFramebufferObjectPool* qgl_fbo_pool() +{ + return _qgl_fbo_pool(); +} void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const { @@ -378,7 +460,8 @@ void QGLPixmapData::swapBuffers() copyBackFromRenderFbo(false); m_renderFbo->release(); - --currentTextureBuffer; + qgl_fbo_pool()->release(m_renderFbo); + delete m_engine; m_renderFbo = 0; m_engine = 0; @@ -396,19 +479,6 @@ void QGLPixmapData::doneCurrent() m_renderFbo->release(); } -static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0) -{ - TextureBuffer buffer; - QGLFramebufferObjectFormat fmt; - fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil); - fmt.setSamples(4); - - buffer.fbo = new QGLFramebufferObject(size, fmt); - buffer.engine = engine ? engine : new QGL2PaintEngineEx; - - return buffer; -} - bool QGLPixmapData::useFramebufferObjects() { return QGLFramebufferObject::hasOpenGLFramebufferObjects() @@ -431,33 +501,15 @@ QPaintEngine* QGLPixmapData::paintEngine() const qt_gl_share_widget()->makeCurrent(); QGLShareContextScope ctx(qt_gl_share_widget()->context()); - if (textureBufferStack.size() <= currentTextureBuffer) { - textureBufferStack << createTextureBuffer(size()); - } else { - QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size(); - if (sz.width() < w || sz.height() < h) { - if (sz.width() < w) - sz.setWidth(qMax(w, qRound(sz.width() * 1.5))); - if (sz.height() < h) - sz.setHeight(qMax(h, qRound(sz.height() * 1.5))); - - // wasting too much space? - if (sz.width() * sz.height() > w * h * 2.5) - sz = QSize(w, h); - - delete textureBufferStack.at(currentTextureBuffer).fbo; - textureBufferStack[currentTextureBuffer] = - createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine); - qDebug() << "Creating new pixmap texture buffer:" << sz; - } - } - - if (textureBufferStack.at(currentTextureBuffer).fbo->isValid()) { - m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo; - m_engine = textureBufferStack.at(currentTextureBuffer).engine; + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setSamples(4); + format.setInternalFormat(m_hasAlpha ? GL_RGBA : GL_RGB); - ++currentTextureBuffer; + m_renderFbo = qgl_fbo_pool()->acquire(size(), format); + if (m_renderFbo) { + m_engine = new QGL2PaintEngineEx; return m_engine; } diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index a6aa22d..aaeb8ac 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -61,6 +61,19 @@ QT_BEGIN_NAMESPACE class QPaintEngine; class QGLFramebufferObject; +class QGLFramebufferObjectFormat; + +class QGLFramebufferObjectPool +{ +public: + QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format); + void release(QGLFramebufferObject *fbo); + +private: + QList<QGLFramebufferObject *> m_fbos; +}; + +QGLFramebufferObjectPool* qgl_fbo_pool(); class QGLPixmapData : public QPixmapData { |