summaryrefslogtreecommitdiffstats
path: root/src/opengl/qpixmapdata_gl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/qpixmapdata_gl.cpp')
-rw-r--r--src/opengl/qpixmapdata_gl.cpp139
1 files changed, 96 insertions, 43 deletions
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index 8de1aae..a441a23 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:
@@ -112,6 +191,8 @@ QGLPixmapData::~QGLPixmapData()
if (!shareWidget)
return;
+ delete m_engine;
+
if (m_texture.id) {
QGLShareContextScope ctx(shareWidget->context());
glDeleteTextures(1, &m_texture.id);
@@ -332,8 +413,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
{
@@ -380,10 +464,9 @@ void QGLPixmapData::swapBuffers()
copyBackFromRenderFbo(false);
m_renderFbo->release();
- --currentTextureBuffer;
+ qgl_fbo_pool()->release(m_renderFbo);
m_renderFbo = 0;
- m_engine = 0;
}
void QGLPixmapData::makeCurrent()
@@ -398,19 +481,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()
@@ -423,7 +493,7 @@ QPaintEngine* QGLPixmapData::paintEngine() const
if (!isValid())
return 0;
- if (m_engine)
+ if (m_renderFbo)
return m_engine;
if (useFramebufferObjects()) {
@@ -433,33 +503,16 @@ 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) {
+ if (!m_engine)
+ m_engine = new QGL2PaintEngineEx;
return m_engine;
}