1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtGui/QApplication>
43 #include <QtGui/QColormap>
44 #include <QtGui/QDesktopWidget>
45 #include <QtGui/QPaintDevice>
46 #include <QtGui/QWidget>
48 #include <qglframebufferobject.h>
49 #include <qglpixelbuffer.h>
50 #include <qcolormap.h>
51 #include <qdesktopwidget.h>
52 #include <private/qwidget_p.h>
56 #include <private/qt_x11_p.h>
57 #include <qx11info_x11.h>
65 #include <private/qglextensions_p.h>
66 #include <private/qwindowsurface_gl_p.h>
68 #include <private/qgl_p.h>
70 #include <private/qglpixelbuffer_p.h>
71 #include <private/qgraphicssystem_gl_p.h>
73 #include <private/qpaintengineex_opengl2_p.h>
74 #include <private/qpixmapdata_gl_p.h>
76 #ifndef QT_OPENGL_ES_2
77 #include <private/qpaintengine_opengl_p.h>
80 #ifndef GLX_ARB_multisample
81 #define GLX_SAMPLE_BUFFERS_ARB 100000
82 #define GLX_SAMPLES_ARB 100001
86 #include <private/qeglcontext_p.h>
95 extern Q_GUI_EXPORT bool qt_win_owndc_required;
97 QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
98 : QGraphicsSystem(), m_useX11GL(useX11GL)
100 #if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
101 // only override the system defaults if the user hasn't already
103 if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
104 // find a double buffered, RGBA visual that supports OpenGL
105 // and set that as the default visual for windows in Qt
108 spec[i++] = GLX_RGBA;
109 spec[i++] = GLX_DOUBLEBUFFER;
111 if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
112 spec[i++] = GLX_DEPTH_SIZE;
114 spec[i++] = GLX_STENCIL_SIZE;
116 spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
118 spec[i++] = GLX_SAMPLES_ARB;
124 XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
126 X11->visual_id = vi->visualid;
127 X11->visual_class = vi->c_class;
131 glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
132 format.setPlane(res);
133 glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
134 format.setDoubleBuffer(res);
135 glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
136 format.setDepth(res);
138 format.setDepthBufferSize(res);
139 glXGetConfig(X11->display, vi, GLX_RGBA, &res);
141 glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
142 format.setRedBufferSize(res);
143 glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
144 format.setGreenBufferSize(res);
145 glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
146 format.setBlueBufferSize(res);
147 glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
148 format.setAlpha(res);
150 format.setAlphaBufferSize(res);
151 glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
152 format.setAccum(res);
154 format.setAccumBufferSize(res);
155 glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
156 format.setStencil(res);
157 if (format.stencil())
158 format.setStencilBufferSize(res);
159 glXGetConfig(X11->display, vi, GLX_STEREO, &res);
160 format.setStereo(res);
161 glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
162 format.setSampleBuffers(res);
163 if (format.sampleBuffers()) {
164 glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
165 format.setSamples(res);
168 QGLWindowSurface::surfaceFormat = format;
171 printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
174 #elif defined(Q_WS_WIN)
175 QGLWindowSurface::surfaceFormat.setDoubleBuffer(true);
177 qt_win_owndc_required = true;
184 class QGLGlobalShareWidget
187 QGLGlobalShareWidget() : widget(0), initializing(false) {
191 QGLWidget *shareWidget() {
192 if (!initializing && !widget && !cleanedUp) {
194 widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer));
196 if (!widget->context()->isValid()) {
199 initializing = false;
203 widget->resize(1, 1);
205 // We don't need this internal widget to appear in QApplication::topLevelWidgets()
206 if (QWidgetPrivate::allWidgets)
207 QWidgetPrivate::allWidgets->remove(widget);
208 initializing = false;
213 // destroys the share widget and prevents recreation
215 QGLWidget *w = widget;
221 // destroys the share widget, but allows it to be recreated later on
226 QGLWidget *w = widget;
228 // prevent potential recursions
235 static bool cleanedUp;
243 bool QGLGlobalShareWidget::cleanedUp = false;
244 bool QGLGlobalShareWidget::created = false;
246 static void qt_cleanup_gl_share_widget();
247 Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
249 qAddPostRoutine(qt_cleanup_gl_share_widget);
252 static void qt_cleanup_gl_share_widget()
254 if (QGLGlobalShareWidget::created)
255 _qt_gl_share_widget()->cleanup();
258 QGLWidget* qt_gl_share_widget()
260 if (QGLGlobalShareWidget::cleanedUp)
262 return _qt_gl_share_widget()->shareWidget();
265 void qt_destroy_gl_share_widget()
267 if (QGLGlobalShareWidget::created)
268 _qt_gl_share_widget()->destroy();
271 const QGLContext *qt_gl_share_context()
273 QGLWidget *widget = qt_gl_share_widget();
275 return widget->context();
279 struct QGLWindowSurfacePrivate
281 QGLFramebufferObject *fbo;
288 int destructive_swap_buffers : 1;
289 int geometry_updated : 1;
294 QList<QGLContext **> contexts;
296 QRegion paintedRegion;
301 QList<QImage> buffers;
302 QGLWindowSurfaceGLPaintDevice glDevice;
303 QGLWindowSurface* q_ptr;
305 bool swap_region_support;
308 QGLFormat QGLWindowSurface::surfaceFormat;
309 QGLWindowSurface::SwapMode QGLWindowSurface::swapBehavior = QGLWindowSurface::AutomaticSwap;
311 void QGLWindowSurfaceGLPaintDevice::endPaint()
314 QGLPaintDevice::endPaint();
317 QSize QGLWindowSurfaceGLPaintDevice::size() const
322 QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
328 int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
330 return qt_paint_device_metric(d->q_ptr->window(), m);
333 QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
335 return qt_qgl_paint_engine();
338 QGLWindowSurface::QGLWindowSurface(QWidget *window)
339 : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
341 // Q_ASSERT(window->isTopLevel());
346 #if defined (QT_OPENGL_ES_2)
347 d_ptr->tried_fbo = true;
348 d_ptr->tried_pb = true;
350 d_ptr->tried_fbo = false;
351 d_ptr->tried_pb = false;
353 d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
354 d_ptr->glDevice.d = d_ptr;
356 d_ptr->geometry_updated = false;
357 d_ptr->did_paint = false;
358 d_ptr->swap_region_support = false;
361 QGLWindowSurface::~QGLWindowSurface()
364 glDeleteTextures(1, &d_ptr->tex_id);
365 #ifndef Q_WS_QPA // Don't delete the contexts. Destroying the window does that for us
366 foreach(QGLContext **ctx, d_ptr->contexts) {
375 if (QGLGlobalShareWidget::cleanedUp)
379 // Destroy the context if necessary.
380 if (qt_gl_share_widget() && !qt_gl_share_context()->isSharing())
381 qt_destroy_gl_share_widget();
385 void QGLWindowSurface::deleted(QObject *object)
387 QWidget *widget = qobject_cast<QWidget *>(object);
389 if (widget == window()) {
390 // Make sure that the fbo is destroyed before destroying its context.
395 #ifndef Q_WS_QPA //no need to specifically delete the QGLContext as it will be deleted by QWidget
396 QWidgetPrivate *widgetPrivate = widget->d_func();
397 if (widgetPrivate->extraData()) {
398 union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
399 voidPtrPtr = &widgetPrivate->extraData()->glContext;
400 int index = d_ptr->contexts.indexOf(ctxPtrPtr);
404 d_ptr->contexts.removeAt(index);
411 void QGLWindowSurface::hijackWindow(QWidget *widget)
413 QWidgetPrivate *widgetPrivate = widget->d_func();
414 widgetPrivate->createExtra();
415 if (widgetPrivate->extraData()->glContext)
418 QGLContext *ctx = NULL;
420 // For translucent top-level widgets we need alpha in the format.
421 if (widget->testAttribute(Qt::WA_TranslucentBackground)) {
422 QGLFormat modFormat(surfaceFormat);
423 modFormat.setSampleBuffers(false);
424 modFormat.setSamples(0);
425 modFormat.setAlpha(true);
426 ctx = new QGLContext(modFormat, widget);
428 ctx = new QGLContext(surfaceFormat, widget);
430 ctx->create(qt_gl_share_context());
432 if (!ctx->isValid()) {
438 static bool checkedForNOKSwapRegion = false;
439 static bool haveNOKSwapRegion = false;
441 if (!checkedForNOKSwapRegion) {
442 haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2");
443 checkedForNOKSwapRegion = true;
445 if (haveNOKSwapRegion)
446 qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
449 d_ptr->destructive_swap_buffers = true;
450 if (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
452 if (eglQuerySurface(ctx->d_func()->eglContext->display(), ctx->d_func()->eglSurface
453 , EGL_SWAP_BEHAVIOR, &swapBehavior)) {
454 d_ptr->destructive_swap_buffers = (swapBehavior != EGL_BUFFER_PRESERVED);
458 d_ptr->swap_region_support = haveNOKSwapRegion;
461 widgetPrivate->extraData()->glContext = ctx;
463 union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
465 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*)));
467 voidPtrPtr = &widgetPrivate->extraData()->glContext;
468 d_ptr->contexts << ctxPtrPtr;
471 qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
475 QGLContext *QGLWindowSurface::context() const
480 QPaintDevice *QGLWindowSurface::paintDevice()
485 // On symbian we always return glDevice, even if it's invalid
486 return &d_ptr->glDevice;
492 return &d_ptr->glDevice;
494 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
497 Q_ASSERT(d_ptr->fbo);
502 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
504 void QGLWindowSurface::beginPaint(const QRegion &)
506 d_ptr->did_paint = true;
511 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
516 if (ctx->d_func()->workaround_needsFullClearOnEveryFrame)
517 clearFlags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
518 else if (ctx->format().alpha())
519 clearFlags = GL_COLOR_BUFFER_BIT;
525 glClearColor(0.0, 0.0, 0.0, 0.0);
529 d_ptr->fbo->release();
533 void QGLWindowSurface::endPaint(const QRegion &rgn)
536 d_ptr->paintedRegion |= rgn;
538 d_ptr->buffers.clear();
541 static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect)
543 glDisable(GL_DEPTH_TEST);
544 glDisable(GL_SCISSOR_TEST);
547 glViewport(0, 0, viewport.width(), viewport.height());
549 QGLShaderProgram *blitProgram =
550 QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
552 blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
554 // The shader manager's blit program does not multiply the
555 // vertices by the pmv matrix, so we need to do the effect
556 // of the orthographic projection here ourselves.
558 qreal w = viewport.width();
559 qreal h = viewport.height();
560 r.setLeft((targetRect.left() / w) * 2.0f - 1.0f);
561 if (targetRect.right() == (viewport.width() - 1))
564 r.setRight((targetRect.right() / w) * 2.0f - 1.0f);
565 r.setBottom((targetRect.top() / h) * 2.0f - 1.0f);
566 if (targetRect.bottom() == (viewport.height() - 1))
569 r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f);
571 drawTexture(r, texture, texSize, sourceRect);
575 void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
577 //### Find out why d_ptr->geometry_updated isn't always false.
578 // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
579 // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
580 if (d_ptr->geometry_updated)
583 // did_paint is set to true in ::beginPaint. ::beginPaint means that we
584 // at least cleared the background (= painted something). In EGL API it's a
585 // mistake to call swapBuffers if nothing was painted unless
586 // EGL_BUFFER_PRESERVED is set. This check protects the flush func from
587 // being executed if it's for nothing.
588 if (!d_ptr->destructive_swap_buffers && !d_ptr->did_paint)
592 if (window() != widget) {
593 // For performance reasons we don't support
594 // flushing native child widgets on Symbian.
595 // It breaks overlapping native child widget
596 // rendering in some cases but we prefer performance.
602 QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
605 #if !defined(Q_WS_QPA)
606 if (!geometry().isValid())
609 if (!size().isValid())
613 // Needed to support native child-widgets...
614 hijackWindow(parent);
616 QRect br = rgn.boundingRect().translated(offset);
617 br = br.intersected(window()->rect());
618 QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
619 QRect rect = br.translated(-offset - wOffset);
621 const GLenum target = GL_TEXTURE_2D;
624 if (QGLWindowSurface::swapBehavior == QGLWindowSurface::KillSwap)
628 context()->makeCurrent();
630 if (context()->format().doubleBuffer()) {
631 #if !defined(QT_OPENGL_ES_2)
632 if (d_ptr->destructive_swap_buffers) {
633 glBindTexture(target, d_ptr->tex_id);
635 QVector<QRect> rects = d_ptr->paintedRegion.rects();
636 for (int i = 0; i < rects.size(); ++i) {
637 QRect br = rects.at(i);
641 const uint bottom = window()->height() - (br.y() + br.height());
642 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
645 glBindTexture(target, 0);
647 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
649 if (!dirtyRegion.isEmpty()) {
650 glMatrixMode(GL_MODELVIEW);
653 glMatrixMode(GL_PROJECTION);
656 glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
658 glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
660 glViewport(0, 0, window()->width(), window()->height());
662 QVector<QRect> rects = dirtyRegion.rects();
663 glColor4f(1, 1, 1, 1);
664 for (int i = 0; i < rects.size(); ++i) {
665 QRect rect = rects.at(i);
669 drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
674 bool doingPartialUpdate = false;
675 if (d_ptr->swap_region_support) {
676 if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AutomaticSwap)
677 doingPartialUpdate = br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2;
678 else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysPartialSwap)
679 doingPartialUpdate = true;
682 QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
683 if (widget != window()) {
684 if (initializeOffscreenTexture(window()->size()))
685 qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss";
686 glBindTexture(target, d_ptr->tex_id);
688 const uint bottom = window()->height() - (br.y() + br.height());
689 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
691 glBindTexture(target, 0);
694 if (doingPartialUpdate)
695 blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br);
697 blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset));
700 if (doingPartialUpdate)
701 ctx->d_func()->swapRegion(br);
705 d_ptr->paintedRegion = QRegion();
712 QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
713 QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
719 // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
720 Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
722 if (ctx != previous_ctx) {
726 QSize size = widget->rect().size();
727 if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
728 rect = parent->rect();
729 br = rect.translated(wOffset + offset);
730 size = parent->size();
733 glDisable(GL_SCISSOR_TEST);
735 if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) {
736 const int h = d_ptr->fbo->height();
738 const int sx0 = br.left();
739 const int sx1 = br.left() + br.width();
740 const int sy0 = h - (br.top() + br.height());
741 const int sy1 = h - br.top();
743 const int tx0 = rect.left();
744 const int tx1 = rect.left() + rect.width();
745 const int ty0 = parent->height() - (rect.top() + rect.height());
746 const int ty1 = parent->height() - rect.top();
748 if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
749 if (ctx->d_ptr->current_fbo != 0)
750 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
752 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
754 glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
759 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
761 #ifndef Q_OS_SYMBIAN // We don't have FBO pool on Symbian
762 // can't do sub-region blits with multisample FBOs
763 QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
765 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
766 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
768 glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
769 0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
773 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
774 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
776 glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
781 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
783 qgl_fbo_pool()->release(temp);
784 #endif // Q_OS_SYMBIAN
787 ctx->d_ptr->current_fbo = 0;
789 #if !defined(QT_OPENGL_ES_2)
793 texture = d_ptr->fbo->texture();
795 d_ptr->pb->makeCurrent();
796 glBindTexture(target, d_ptr->pb_tex_id);
797 const uint bottom = window()->height() - (br.y() + br.height());
798 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
799 texture = d_ptr->pb_tex_id;
800 glBindTexture(target, 0);
803 glDisable(GL_DEPTH_TEST);
806 d_ptr->fbo->release();
811 glMatrixMode(GL_MODELVIEW);
814 glMatrixMode(GL_PROJECTION);
817 glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
819 glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
821 glViewport(0, 0, size.width(), size.height());
823 glColor4f(1, 1, 1, 1);
824 drawTexture(rect, texture, window()->size(), br);
830 // OpenGL/ES 2.0 version of the fbo blit.
831 else if (d_ptr->fbo) {
834 if (d_ptr->fbo->isBound())
835 d_ptr->fbo->release();
837 blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br);
841 if (ctx->format().doubleBuffer())
846 d_ptr->did_paint = false;
850 #if !defined(Q_WS_QPA)
851 void QGLWindowSurface::setGeometry(const QRect &rect)
853 QWindowSurface::setGeometry(rect);
854 d_ptr->geometry_updated = true;
857 void QGLWindowSurface::resize(const QSize &size)
859 QWindowSurface::resize(size);
860 d_ptr->geometry_updated = true;
864 void QGLWindowSurface::updateGeometry() {
865 if (!d_ptr->geometry_updated)
867 d_ptr->geometry_updated = false;
870 QWidgetPrivate *wd = window()->d_func();
871 if (wd->extraData() && wd->extraData()->glContext) {
872 #ifdef Q_OS_SYMBIAN // Symbian needs to recreate the context when native window size changes
873 if (d_ptr->size != geometry().size()) {
874 QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
876 if (ctx == QGLContext::currentContext())
879 ctx->d_func()->destroyEglSurfaceForDevice();
881 // Delete other contexts (shouldn't happen too often, if at all)
882 while (d_ptr->contexts.size()) {
883 QGLContext **ctxPtrPtr = d_ptr->contexts.takeFirst();
884 if ((*ctxPtrPtr) != ctx)
887 union { QGLContext **ctxPtrPtr; void **voidPtrPtr; };
888 voidPtrPtr = &wd->extraData()->glContext;
889 d_ptr->contexts << ctxPtrPtr;
891 ctx->d_func()->eglSurface = ctx->d_func()->eglContext->createSurface(window());
893 // Swap behaviour has been checked already in previous hijackWindow call.
894 // Reset swap behaviour based on that flag.
895 if (d_ptr->destructive_swap_buffers) {
896 eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(),
897 EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
899 if (eglGetError() != EGL_SUCCESS)
900 qWarning("QGLWindowSurface::updateGeometry() - could not re-enable destroyed swap behaviour");
902 eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(),
903 EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
905 if (eglGetError() != EGL_SUCCESS)
906 qWarning("QGLWindowSurface::updateGeometry() - could not re-enable preserved swap behaviour");
910 hijack = false; // we already have gl context for widget
914 hijackWindow(window());
916 QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext);
922 ctx->updatePaintDevice();
925 QSize surfSize = geometry().size();
927 if (surfSize.width() <= 0 || surfSize.height() <= 0)
930 if (d_ptr->size == surfSize)
933 d_ptr->size = surfSize;
936 #ifndef QT_OPENGL_ES_2
937 if (d_ptr->destructive_swap_buffers)
938 initializeOffscreenTexture(surfSize);
943 const GLenum target = GL_TEXTURE_2D;
944 if (d_ptr->destructive_swap_buffers
945 && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
946 && (d_ptr->fbo || !d_ptr->tried_fbo)
947 && qt_gl_preferGL2Engine())
949 d_ptr->tried_fbo = true;
950 ctx->d_ptr->internal_context = true;
954 QGLFramebufferObjectFormat format;
955 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
956 format.setInternalTextureFormat(GLenum(GL_RGBA));
957 format.setTextureTarget(target);
959 if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
960 format.setSamples(8);
962 d_ptr->fbo = new QGLFramebufferObject(surfSize, format);
964 if (d_ptr->fbo->isValid()) {
965 qDebug() << "Created Window Surface FBO" << surfSize
966 << "with samples" << d_ptr->fbo->format().samples();
969 qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
975 #if !defined(QT_OPENGL_ES_2) && !defined(Q_WS_QPA) //QPA doesn't support pixelbuffers
976 if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
977 d_ptr->tried_pb = true;
980 d_ptr->pb->makeCurrent();
981 glDeleteTextures(1, &d_ptr->pb_tex_id);
986 d_ptr->pb = new QGLPixelBuffer(surfSize.width(), surfSize.height(),
987 QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
988 qt_gl_share_widget());
990 if (d_ptr->pb->isValid()) {
991 qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
992 d_ptr->pb->makeCurrent();
994 glGenTextures(1, &d_ptr->pb_tex_id);
995 glBindTexture(target, d_ptr->pb_tex_id);
996 glTexImage2D(target, 0, GL_RGBA, surfSize.width(), surfSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
998 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
999 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1000 glBindTexture(target, 0);
1002 glMatrixMode(GL_PROJECTION);
1004 glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
1006 d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
1009 qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
1014 #endif // !defined(QT_OPENGL_ES_2) !defined(Q_WS_QPA)
1018 #ifndef QT_OPENGL_ES_2
1019 if (d_ptr->destructive_swap_buffers)
1020 initializeOffscreenTexture(surfSize);
1022 #ifndef Q_OS_SYMBIAN
1023 qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;
1026 d_ptr->ctx->d_ptr->internal_context = true;
1029 bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size)
1031 if (size == d_ptr->textureSize)
1034 glGenTextures(1, &d_ptr->tex_id);
1035 glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id);
1036 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
1038 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1039 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1040 glBindTexture(GL_TEXTURE_2D, 0);
1042 d_ptr->textureSize = size;
1046 bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
1048 // this code randomly fails currently for unknown reasons
1054 d_ptr->pb->makeCurrent();
1056 QRect br = area.boundingRect();
1059 // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
1060 // ## maybe we should use glCopyTexSubImage insteadk
1061 if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
1064 glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
1065 glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
1069 const GLenum target = GL_TEXTURE_2D;
1071 glBindTexture(target, d_ptr->tex_id);
1072 glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
1073 glBindTexture(target, 0);
1075 drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
1080 static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
1082 const GLenum target = GL_TEXTURE_2D;
1083 QRectF src = br.isEmpty()
1084 ? QRectF(QPointF(), texSize)
1085 : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
1087 if (target == GL_TEXTURE_2D) {
1088 qreal width = texSize.width();
1089 qreal height = texSize.height();
1091 src.setLeft(src.left() / width);
1092 src.setRight(src.right() / width);
1093 src.setTop(src.top() / height);
1094 src.setBottom(src.bottom() / height);
1097 const GLfloat tx1 = src.left();
1098 const GLfloat tx2 = src.right();
1099 const GLfloat ty1 = src.top();
1100 const GLfloat ty2 = src.bottom();
1102 GLfloat texCoordArray[4*2] = {
1103 tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
1106 GLfloat vertexArray[4*2];
1107 extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp
1108 qt_add_rect_to_array(rect, vertexArray);
1110 #if !defined(QT_OPENGL_ES_2)
1111 glVertexPointer(2, GL_FLOAT, 0, vertexArray);
1112 glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
1114 glBindTexture(target, tex_id);
1117 glEnableClientState(GL_VERTEX_ARRAY);
1118 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1119 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1120 glDisableClientState(GL_VERTEX_ARRAY);
1121 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1124 glBindTexture(target, 0);
1126 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
1127 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
1129 glBindTexture(target, tex_id);
1131 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1132 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
1133 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1134 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
1135 glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
1137 glBindTexture(target, 0);
1141 QImage *QGLWindowSurface::buffer(const QWidget *widget)
1146 image = d_ptr->pb->toImage();
1147 else if (d_ptr->fbo)
1148 image = d_ptr->fbo->toImage();
1153 QRect rect = widget->rect();
1154 rect.translate(widget->mapTo(widget->window(), QPoint()));
1156 QImage subImage = image.copy(rect);
1157 d_ptr->buffers << subImage;
1158 return &d_ptr->buffers.last();
1161 QWindowSurface::WindowSurfaceFeatures QGLWindowSurface::features() const
1163 WindowSurfaceFeatures features = 0;
1164 if (!d_ptr->destructive_swap_buffers || d_ptr->swap_region_support)
1165 features |= PartialUpdates;
1166 if (!d_ptr->destructive_swap_buffers)
1167 features |= PreservedContents;