summaryrefslogtreecommitdiffstats
path: root/src/opengl/qpixmapdata_gl.cpp
diff options
context:
space:
mode:
authorSamuel Rødal <sroedal@trolltech.com>2009-04-16 08:55:12 (GMT)
committerSamuel Rødal <sroedal@trolltech.com>2009-04-16 15:28:26 (GMT)
commita241ebac49f01ba0e26a177f1aadbd18c7e9cae7 (patch)
treec7e577ecf60c990a1a5a1902ba0637bd3187a4d6 /src/opengl/qpixmapdata_gl.cpp
parent1e1371e19ae62a5bf57dcad8d53ac70dcd2ad0cb (diff)
downloadQt-a241ebac49f01ba0e26a177f1aadbd18c7e9cae7.zip
Qt-a241ebac49f01ba0e26a177f1aadbd18c7e9cae7.tar.gz
Qt-a241ebac49f01ba0e26a177f1aadbd18c7e9cae7.tar.bz2
Use FBOs as pixmap backend in GL graphics system.
We now use FBOs to implement render-to-pixmap for the GL pixmap backend. A multisample FBO is used for rendering, and is then blitted onto a non-multisample FBO dynamically bound to the relevant texture. Reviewed-by: Tom
Diffstat (limited to 'src/opengl/qpixmapdata_gl.cpp')
-rw-r--r--src/opengl/qpixmapdata_gl.cpp236
1 files changed, 168 insertions, 68 deletions
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index ec71fa6..0656880 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qpixmap.h"
+#include "qglframebufferobject.h"
#include <private/qpaintengine_raster_p.h>
@@ -48,6 +49,12 @@
#include <private/qgl_p.h>
#include <private/qdrawhelper_p.h>
+#if 1 || defined(QT_OPENGL_ES_2)
+#include <private/qpaintengineex_opengl2_p.h>
+#else
+#include <private/qpaintengine_opengl_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
extern QGLWidget* qt_gl_share_widget();
@@ -89,48 +96,16 @@ private:
QGLContext *m_ctx;
};
-void qt_gl_convertFromGLImage(QImage *img)
-{
- const int w = img->width();
- const int h = img->height();
-
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
- uint *p = (uint*)img->bits();
- uint *end = p + w*h;
-
- while (p < end) {
- uint a = *p << 24;
- *p = (*p >> 8) | a;
- p++;
- }
-
- *img = img->mirrored();
- } else {
- // mirror image
- uint *data = (uint *)img->bits();
-
- const int mid = h/2;
-
- for (int y = 0; y < mid; ++y) {
- uint *p = data + y * w;
- uint *end = p + w;
- uint *q = data + (h - y - 1) * w;
-
- while (p < end)
- qSwap(*p++, *q++);
- }
- }
-}
-
-
static int qt_gl_pixmap_serial = 0;
QGLPixmapData::QGLPixmapData(PixelType type)
: QPixmapData(type, OpenGLClass)
, m_width(0)
, m_height(0)
+ , m_renderFbo(0)
+ , m_textureId(0)
+ , m_engine(0)
, m_ctx(0)
- , m_texture(0)
, m_dirty(false)
{
setSerialNumber(++qt_gl_pixmap_serial);
@@ -138,10 +113,11 @@ QGLPixmapData::QGLPixmapData(PixelType type)
QGLPixmapData::~QGLPixmapData()
{
- if (m_texture && qt_gl_share_widget()) {
- QGLShareContextScope ctx(qt_gl_share_widget()->context());
- glDeleteTextures(1, &m_texture);
- }
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (!shareWidget)
+ return;
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &m_textureId);
}
bool QGLPixmapData::isValid() const
@@ -166,6 +142,12 @@ void QGLPixmapData::resize(int width, int height)
m_width = width;
m_height = height;
+ if (m_textureId) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glDeleteTextures(1, &m_textureId);
+ m_textureId = 0;
+ }
+
m_source = QImage();
m_dirty = isValid();
setSerialNumber(++qt_gl_pixmap_serial);
@@ -184,24 +166,30 @@ void QGLPixmapData::ensureCreated() const
const GLenum format = qt_gl_preferredTextureFormat();
const GLenum target = qt_gl_preferredTextureTarget();
- if (!m_texture)
- glGenTextures(1, &m_texture);
-
- glBindTexture(target, m_texture);
+ if (!m_textureId) {
+ glGenTextures(1, &m_textureId);
+ glBindTexture(target, m_textureId);
+ glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
- if (m_source.isNull()) {
- glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format, GL_UNSIGNED_BYTE, 0);
- } else {
+ if (!m_source.isNull()) {
const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format);
- glBindTexture(target, m_texture);
- glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format,
- GL_UNSIGNED_BYTE, tx.bits());
+ glBindTexture(target, m_textureId);
+ glTexSubImage2D(target, 0, 0, 0, m_width, m_height, format,
+ GL_UNSIGNED_BYTE, tx.bits());
- m_source = QImage();
+ if (useFramebufferObjects())
+ m_source = QImage();
}
}
+QGLFramebufferObject *QGLPixmapData::fbo() const
+{
+ return m_renderFbo;
+}
+
void QGLPixmapData::fromImage(const QImage &image,
Qt::ImageConversionFlags)
{
@@ -220,6 +208,17 @@ bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
return false;
}
+void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->classId() != QPixmapData::OpenGLClass) {
+ QPixmapData::copy(data, rect);
+ return;
+ }
+
+ // can be optimized to do a framebuffer blit or similar ...
+ QPixmapData::copy(data, rect);
+}
+
void QGLPixmapData::fill(const QColor &color)
{
if (!isValid())
@@ -246,7 +245,9 @@ QImage QGLPixmapData::toImage() const
if (!isValid())
return QImage();
- if (!m_source.isNull())
+ if (m_renderFbo)
+ return m_renderFbo->toImage().copy(0, m_renderFbo->height() - m_height, m_width, m_height);
+ else if (!m_source.isNull())
return m_source;
else if (m_dirty)
return QImage(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
@@ -254,21 +255,84 @@ QImage QGLPixmapData::toImage() const
ensureCreated();
QGLShareContextScope ctx(qt_gl_share_widget()->context());
- QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+ extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+ glBindTexture(qt_gl_preferredTextureTarget(), m_textureId);
+ return qt_gl_read_texture(QSize(m_width, m_height), true, true);
+}
- GLenum format = qt_gl_preferredTextureFormat();
- GLenum target = qt_gl_preferredTextureTarget();
+struct TextureBuffer
+{
+ QGLFramebufferObject *fbo;
+ QGL2PaintEngineEx *engine;
+};
- glBindTexture(target, m_texture);
-#ifndef QT_OPENGL_ES
- glGetTexImage(target, 0, format, GL_UNSIGNED_BYTE, img.bits());
-#else
- // XXX - cannot download textures this way on OpenGL/ES.
-#endif
+static QVector<TextureBuffer> textureBufferStack;
+static int currentTextureBuffer = 0;
- qt_gl_convertFromGLImage(&img);
+void QGLPixmapData::beginPaint()
+{
+ if (!isValid())
+ return;
- return img;
+ m_renderFbo->bind();
+}
+
+void QGLPixmapData::endPaint()
+{
+ if (!isValid())
+ return;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ QGLShareContextScope ctx(share_ctx);
+
+ ensureCreated();
+
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffersEXT(1, &ctx->d_ptr->fbo);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ qt_gl_preferredTextureTarget(), m_textureId, 0);
+
+ const int x0 = 0;
+ const int x1 = m_width;
+ const int y0 = 0;
+ const int y1 = m_height;
+
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBlitFramebufferEXT(x0, y0, x1, y1,
+ x0, y0, x1, y1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
+
+ m_renderFbo->release();
+
+ --currentTextureBuffer;
+
+ m_renderFbo = 0;
+ m_engine = 0;
+}
+
+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()
+ && QGLFramebufferObject::hasOpenGLFramebufferBlit();
}
QPaintEngine* QGLPixmapData::paintEngine() const
@@ -276,23 +340,59 @@ QPaintEngine* QGLPixmapData::paintEngine() const
if (!isValid())
return 0;
- m_source = toImage();
- m_dirty = true;
+ if (m_engine)
+ return m_engine;
+ else if (!useFramebufferObjects()) {
+ m_dirty = true;
+
+ if (m_source.size() != size())
+ m_source = QImage(size(), QImage::Format_ARGB32_Premultiplied);
+ return m_source.paintEngine();
+ }
- return m_source.paintEngine();
+ extern QGLWidget* qt_gl_share_widget();
+
+ if (!QGLContext::currentContext())
+ 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() < m_width || sz.height() < m_height) {
+ if (sz.width() < m_width)
+ sz.setWidth(qMax(m_width, qRound(sz.width() * 1.5)));
+ if (sz.height() < m_height)
+ sz.setHeight(qMax(m_height, qRound(sz.height() * 1.5)));
+ delete textureBufferStack.at(currentTextureBuffer).fbo;
+ textureBufferStack[currentTextureBuffer] =
+ createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine);
+ qDebug() << "Creating new pixmap texture buffer:" << sz;
+ }
+ }
+
+ m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo;
+ m_engine = textureBufferStack.at(currentTextureBuffer).engine;
+
+ ++currentTextureBuffer;
+
+ return m_engine;
}
GLuint QGLPixmapData::bind() const
{
ensureCreated();
- glBindTexture(qt_gl_preferredTextureTarget(), m_texture);
- return m_texture;
+
+ GLuint id = m_textureId;
+ glBindTexture(qt_gl_preferredTextureTarget(), id);
+ return id;
}
GLuint QGLPixmapData::textureId() const
{
ensureCreated();
- return m_texture;
+ return m_textureId;
}
extern int qt_defaultDpiX();