From 9d81ddd3da51166a9fc92073cf7ba508cdd22dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Fri, 22 Oct 2010 12:17:56 +0200 Subject: Added support for blitting to native child widgets in GL window surface. Support blitting by copying from the top-level window's back buffer to a temporary texture and then blitting from the texture to the native child widget. Performance suffers, but it's better than failing. Reviewed-by: Gunnar Sletta --- src/opengl/qgl.cpp | 2 +- src/opengl/qgl_egl.cpp | 4 +- src/opengl/qgl_p.h | 2 +- src/opengl/qwindowsurface_gl.cpp | 139 +++++++++++++++++++++++---------------- src/opengl/qwindowsurface_gl_p.h | 1 + 5 files changed, 88 insertions(+), 60 deletions(-) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 62eff6e..dbd295f 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2104,7 +2104,7 @@ void QGLContextPrivate::syncGlState() #undef ctx #ifdef QT_NO_EGL -void QGLContextPrivate::swapRegion(const QRegion *) +void QGLContextPrivate::swapRegion(const QRegion &) { Q_Q(QGLContext); q->swapBuffers(); diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp index c79c4cd..27f7ad9 100644 --- a/src/opengl/qgl_egl.cpp +++ b/src/opengl/qgl_egl.cpp @@ -276,12 +276,12 @@ EGLSurface QGLContextPrivate::eglSurfaceForDevice() const return eglSurface; } -void QGLContextPrivate::swapRegion(const QRegion *region) +void QGLContextPrivate::swapRegion(const QRegion ®ion) { if (!valid || !eglContext) return; - eglContext->swapBuffersRegion2NOK(eglSurfaceForDevice(), region); + eglContext->swapBuffersRegion2NOK(eglSurfaceForDevice(), ®ion); } void QGLWidget::setMouseTracking(bool enable) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 6c494ee..f86c77f 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -343,7 +343,7 @@ public: void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true); void syncGlState(); // Makes sure the GL context's state is what we think it is - void swapRegion(const QRegion *region); + void swapRegion(const QRegion ®ion); #if defined(Q_WS_WIN) void updateFormatVersion(); diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index 8157b2a..9f3dcdf 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -275,6 +275,8 @@ struct QGLWindowSurfacePrivate QRegion paintedRegion; QSize size; + QSize textureSize; + QList buffers; QGLWindowSurfaceGLPaintDevice glDevice; QGLWindowSurface* q_ptr; @@ -316,6 +318,7 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window) d_ptr->pb = 0; d_ptr->fbo = 0; d_ptr->ctx = 0; + d_ptr->tex_id = 0; #if defined (QT_OPENGL_ES_2) d_ptr->tried_fbo = true; d_ptr->tried_pb = true; @@ -433,7 +436,7 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, void QGLWindowSurface::beginPaint(const QRegion &) { - if (! context()) + if (!context()) return; int clearFlags = 0; @@ -457,13 +460,42 @@ void QGLWindowSurface::endPaint(const QRegion &rgn) d_ptr->buffers.clear(); } -void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset) +static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect) { - if (context() && widget != window()) { - qWarning("No native child widget support in GL window surface without FBOs or pixel buffers"); - return; - } + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + glViewport(0, 0, viewport.width(), viewport.height()); + + QGLShaderProgram *blitProgram = + QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram(); + blitProgram->bind(); + blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/); + + // The shader manager's blit program does not multiply the + // vertices by the pmv matrix, so we need to do the effect + // of the orthographic projection here ourselves. + QRectF r; + qreal w = viewport.width(); + qreal h = viewport.height(); + r.setLeft((targetRect.left() / w) * 2.0f - 1.0f); + if (targetRect.right() == (viewport.width() - 1)) + r.setRight(1.0f); + else + r.setRight((targetRect.right() / w) * 2.0f - 1.0f); + r.setBottom((targetRect.top() / h) * 2.0f - 1.0f); + if (targetRect.bottom() == (viewport.height() - 1)) + r.setTop(1.0f); + else + r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f); + + drawTexture(r, texture, texSize, sourceRect); +} + +void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset) +{ //### Find out why d_ptr->geometry_updated isn't always false. // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either // d_ptr->fbo or d_ptr->pb is allocated and has the correct size. @@ -534,12 +566,29 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & } } #endif - if (hasPartialUpdateSupport() && - d_ptr->paintedRegion.boundingRect().width() * d_ptr->paintedRegion.boundingRect().height() < - geometry().width() * geometry().height() * 0.2) { - context()->d_func()->swapRegion(&d_ptr->paintedRegion); - } else - context()->swapBuffers(); + bool doingPartialUpdate = hasPartialUpdateSupport() && br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2; + QGLContext *ctx = reinterpret_cast(parent->d_func()->extraData()->glContext); + if (widget != window()) { + if (initializeOffscreenTexture(window()->size())) + qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss"; + glBindTexture(target, d_ptr->tex_id); + + const uint bottom = window()->height() - (br.y() + br.height()); + glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); + + glBindTexture(target, 0); + + ctx->makeCurrent(); + if (doingPartialUpdate) + blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br); + else + blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset)); + } + + if (doingPartialUpdate) + ctx->d_func()->swapRegion(br); + else + ctx->swapBuffers(); d_ptr->paintedRegion = QRegion(); } else { @@ -665,38 +714,10 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & else if (d_ptr->fbo) { Q_UNUSED(target); - GLuint texture = d_ptr->fbo->texture(); - - glDisable(GL_DEPTH_TEST); - if (d_ptr->fbo->isBound()) d_ptr->fbo->release(); - glViewport(0, 0, size.width(), size.height()); - - QGLShaderProgram *blitProgram = - QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram(); - blitProgram->bind(); - blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/); - - // The shader manager's blit program does not multiply the - // vertices by the pmv matrix, so we need to do the effect - // of the orthographic projection here ourselves. - QRectF r; - qreal w = size.width() ? size.width() : 1.0f; - qreal h = size.height() ? size.height() : 1.0f; - r.setLeft((rect.left() / w) * 2.0f - 1.0f); - if (rect.right() == (size.width() - 1)) - r.setRight(1.0f); - else - r.setRight((rect.right() / w) * 2.0f - 1.0f); - r.setBottom((rect.top() / h) * 2.0f - 1.0f); - if (rect.bottom() == (size.height() - 1)) - r.setTop(1.0f); - else - r.setTop((rect.bottom() / w) * 2.0f - 1.0f); - - drawTexture(r, texture, window()->size(), br); + blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br); } #endif @@ -719,7 +740,6 @@ void QGLWindowSurface::updateGeometry() { return; d_ptr->geometry_updated = false; - QRect rect = geometry(); hijackWindow(window()); QGLContext *ctx = reinterpret_cast(window()->d_func()->extraData()->glContext); @@ -740,11 +760,8 @@ void QGLWindowSurface::updateGeometry() { if (d_ptr->ctx) { #ifndef QT_OPENGL_ES_2 - if (d_ptr->destructive_swap_buffers) { - glBindTexture(target, d_ptr->tex_id); - glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glBindTexture(target, 0); - } + if (d_ptr->destructive_swap_buffers) + initializeOffscreenTexture(rect.size()); #endif return; } @@ -824,15 +841,8 @@ void QGLWindowSurface::updateGeometry() { ctx->makeCurrent(); #ifndef QT_OPENGL_ES_2 - if (d_ptr->destructive_swap_buffers) { - glGenTextures(1, &d_ptr->tex_id); - glBindTexture(target, d_ptr->tex_id); - glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindTexture(target, 0); - } + if (d_ptr->destructive_swap_buffers) + initializeOffscreenTexture(rect.size()); #endif qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;; @@ -840,6 +850,23 @@ void QGLWindowSurface::updateGeometry() { d_ptr->ctx->d_ptr->internal_context = true; } +bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size) +{ + if (size == d_ptr->textureSize) + return false; + + glGenTextures(1, &d_ptr->tex_id); + glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + d_ptr->textureSize = size; + return true; +} + bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy) { // this code randomly fails currently for unknown reasons diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h index ffc2e86..6906f35 100644 --- a/src/opengl/qwindowsurface_gl_p.h +++ b/src/opengl/qwindowsurface_gl_p.h @@ -107,6 +107,7 @@ private slots: private: void hijackWindow(QWidget *widget); + bool initializeOffscreenTexture(const QSize &size); QGLWindowSurfacePrivate *d_ptr; }; -- cgit v0.12