1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** This file contains pre-release code and may not be distributed.
11 ** You may use this file in accordance with the terms and conditions
12 ** contained in the either Technology Preview License Agreement or the
13 ** Beta Release License Agreement.
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you are unsure which license is appropriate for your use, please
37 ** contact the sales department at http://www.qtsoftware.com/contact.
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>
55 #include <private/qt_x11_p.h>
56 #include <qx11info_x11.h>
57 #include <private/qwidget_p.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>
75 #ifndef QT_OPENGL_ES_2
76 #include <private/qpaintengine_opengl_p.h>
79 #ifndef GLX_ARB_multisample
80 #define GLX_SAMPLE_BUFFERS_ARB 100000
81 #define GLX_SAMPLES_ARB 100001
84 #ifdef QT_OPENGL_ES_1_CL
94 extern Q_GUI_EXPORT
bool qt_win_owndc_required
;
96 QGLGraphicsSystem::QGLGraphicsSystem()
99 #if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
100 // only override the system defaults if the user hasn't already
102 if (X11
->visual
== 0 && X11
->visual_id
== -1 && X11
->visual_class
== -1) {
103 // find a double buffered, RGBA visual that supports OpenGL
104 // and set that as the default visual for windows in Qt
107 spec
[i
++] = GLX_RGBA
;
108 spec
[i
++] = GLX_DOUBLEBUFFER
;
110 if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
111 spec
[i
++] = GLX_DEPTH_SIZE
;
113 spec
[i
++] = GLX_STENCIL_SIZE
;
115 spec
[i
++] = GLX_SAMPLE_BUFFERS_ARB
;
117 spec
[i
++] = GLX_SAMPLES_ARB
;
123 XVisualInfo
*vi
= glXChooseVisual(X11
->display
, X11
->defaultScreen
, spec
);
125 X11
->visual_id
= vi
->visualid
;
126 X11
->visual_class
= vi
->c_class
;
130 glXGetConfig(X11
->display
, vi
, GLX_LEVEL
, &res
);
131 format
.setPlane(res
);
132 glXGetConfig(X11
->display
, vi
, GLX_DOUBLEBUFFER
, &res
);
133 format
.setDoubleBuffer(res
);
134 glXGetConfig(X11
->display
, vi
, GLX_DEPTH_SIZE
, &res
);
135 format
.setDepth(res
);
137 format
.setDepthBufferSize(res
);
138 glXGetConfig(X11
->display
, vi
, GLX_RGBA
, &res
);
140 glXGetConfig(X11
->display
, vi
, GLX_RED_SIZE
, &res
);
141 format
.setRedBufferSize(res
);
142 glXGetConfig(X11
->display
, vi
, GLX_GREEN_SIZE
, &res
);
143 format
.setGreenBufferSize(res
);
144 glXGetConfig(X11
->display
, vi
, GLX_BLUE_SIZE
, &res
);
145 format
.setBlueBufferSize(res
);
146 glXGetConfig(X11
->display
, vi
, GLX_ALPHA_SIZE
, &res
);
147 format
.setAlpha(res
);
149 format
.setAlphaBufferSize(res
);
150 glXGetConfig(X11
->display
, vi
, GLX_ACCUM_RED_SIZE
, &res
);
151 format
.setAccum(res
);
153 format
.setAccumBufferSize(res
);
154 glXGetConfig(X11
->display
, vi
, GLX_STENCIL_SIZE
, &res
);
155 format
.setStencil(res
);
156 if (format
.stencil())
157 format
.setStencilBufferSize(res
);
158 glXGetConfig(X11
->display
, vi
, GLX_STEREO
, &res
);
159 format
.setStereo(res
);
160 glXGetConfig(X11
->display
, vi
, GLX_SAMPLE_BUFFERS_ARB
, &res
);
161 format
.setSampleBuffers(res
);
162 if (format
.sampleBuffers()) {
163 glXGetConfig(X11
->display
, vi
, GLX_SAMPLES_ARB
, &res
);
164 format
.setSamples(res
);
167 QGLWindowSurface::surfaceFormat
= format
;
170 printf("using visual class %x, id %x\n", X11
->visual_class
, X11
->visual_id
);
173 #elif defined(Q_WS_WIN)
174 QGLWindowSurface::surfaceFormat
.setDoubleBuffer(false);
176 qt_win_owndc_required
= true;
184 class QGLGlobalShareWidget
187 QGLGlobalShareWidget() : widget(0), initializing(false) {}
189 QGLWidget
*shareWidget() {
190 if (!initializing
&& !widget
&& !cleanedUp
) {
192 widget
= new QGLWidget
;
193 initializing
= false;
199 QGLWidget
*w
= widget
;
205 static bool cleanedUp
;
212 bool QGLGlobalShareWidget::cleanedUp
= false;
214 static void qt_cleanup_gl_share_widget();
215 Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget
, _qt_gl_share_widget
,
217 qAddPostRoutine(qt_cleanup_gl_share_widget
);
220 static void qt_cleanup_gl_share_widget()
222 _qt_gl_share_widget()->cleanup();
225 QGLWidget
* qt_gl_share_widget()
227 if (QGLGlobalShareWidget::cleanedUp
)
229 return _qt_gl_share_widget()->shareWidget();
232 struct QGLWindowSurfacePrivate
234 QGLFramebufferObject
*fbo
;
241 int destructive_swap_buffers
: 1;
245 QList
<QGLContext
**> contexts
;
247 QRegion paintedRegion
;
250 QList
<QImage
> buffers
;
253 QGLFormat
QGLWindowSurface::surfaceFormat
;
255 QGLWindowSurface::QGLWindowSurface(QWidget
*window
)
256 : QWindowSurface(window
), d_ptr(new QGLWindowSurfacePrivate
)
258 Q_ASSERT(window
->isTopLevel());
259 QGLExtensions::init();
263 d_ptr
->tried_fbo
= false;
264 d_ptr
->tried_pb
= false;
265 d_ptr
->destructive_swap_buffers
= qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
268 QGLWindowSurface::~QGLWindowSurface()
271 glDeleteTextures(1, &d_ptr
->tex_id
);
272 foreach(QGLContext
**ctx
, d_ptr
->contexts
) {
282 void QGLWindowSurface::deleted(QObject
*object
)
284 QWidget
*widget
= qobject_cast
<QWidget
*>(object
);
286 QWidgetPrivate
*widgetPrivate
= widget
->d_func();
287 if (widgetPrivate
->extraData()) {
288 union { QGLContext
**ctxPtr
; void **voidPtr
; };
289 voidPtr
= &widgetPrivate
->extraData()->glContext
;
290 int index
= d_ptr
->contexts
.indexOf(ctxPtr
);
294 d_ptr
->contexts
.removeAt(index
);
300 void QGLWindowSurface::hijackWindow(QWidget
*widget
)
302 QWidgetPrivate
*widgetPrivate
= widget
->d_func();
303 widgetPrivate
->createExtra();
304 if (widgetPrivate
->extraData()->glContext
)
307 QGLContext
*ctx
= new QGLContext(surfaceFormat
, widget
);
308 ctx
->create(qt_gl_share_widget()->context());
310 ctx
->updatePaintDevice();
312 widgetPrivate
->extraData()->glContext
= ctx
;
314 union { QGLContext
**ctxPtr
; void **voidPtr
; };
316 connect(widget
, SIGNAL(destroyed(QObject
*)), this, SLOT(deleted(QObject
*)));
318 voidPtr
= &widgetPrivate
->extraData()->glContext
;
319 d_ptr
->contexts
<< ctxPtr
;
320 qDebug() << "hijackWindow() context created for" << widget
<< d_ptr
->contexts
.size();
323 Q_GLOBAL_STATIC(QGL2PaintEngineEx
, qt_gl_window_surface_2_engine
)
325 #if !defined (QT_OPENGL_ES_2)
326 Q_GLOBAL_STATIC(QOpenGLPaintEngine
, qt_gl_window_surface_engine
)
330 QPaintEngine
*QGLWindowSurface::paintEngine() const
332 #if !defined(QT_OPENGL_ES_2)
333 if (!qt_gl_preferGL2Engine())
334 return qt_gl_window_surface_engine();
336 return qt_gl_window_surface_2_engine();
339 int QGLWindowSurface::metric(PaintDeviceMetric m
) const
341 return window()->metric(m
);
344 QGLContext
*QGLWindowSurface::context() const
349 QPaintDevice
*QGLWindowSurface::paintDevice()
359 QGLContext
*ctx
= reinterpret_cast<QGLContext
*>(window()->d_func()->extraData()->glContext
);
364 static void drawTexture(const QRectF
&rect
, GLuint tex_id
, const QSize
&texSize
, const QRectF
&src
= QRectF());
366 void QGLWindowSurface::beginPaint(const QRegion
&)
371 void QGLWindowSurface::endPaint(const QRegion
&rgn
)
374 d_ptr
->paintedRegion
|= rgn
;
376 d_ptr
->buffers
.clear();
379 void QGLWindowSurface::flush(QWidget
*widget
, const QRegion
&rgn
, const QPoint
&offset
)
381 QWidget
*parent
= widget
->internalWinId() ? widget
: widget
->nativeParentWidget();
384 if (!geometry().isValid())
387 hijackWindow(parent
);
389 QRect br
= rgn
.boundingRect().translated(offset
);
390 br
= br
.intersected(window()->rect());
391 QPoint wOffset
= qt_qwidget_data(parent
)->wrect
.topLeft();
392 QRect rect
= br
.translated(-offset
- wOffset
);
394 const GLenum target
= GL_TEXTURE_2D
;
397 context()->makeCurrent();
399 if (context()->format().doubleBuffer()) {
400 #if !defined(QT_OPENGL_ES_2)
401 if (d_ptr
->destructive_swap_buffers
) {
402 glBindTexture(target
, d_ptr
->tex_id
);
404 QVector
<QRect
> rects
= d_ptr
->paintedRegion
.rects();
405 for (int i
= 0; i
< rects
.size(); ++i
) {
406 QRect br
= rects
.at(i
);
410 const uint bottom
= window()->height() - (br
.y() + br
.height());
411 glCopyTexSubImage2D(target
, 0, br
.x(), bottom
, br
.x(), bottom
, br
.width(), br
.height());
414 glBindTexture(target
, 0);
416 QRegion dirtyRegion
= QRegion(window()->rect()) - d_ptr
->paintedRegion
;
418 if (!dirtyRegion
.isEmpty()) {
419 context()->makeCurrent();
421 glMatrixMode(GL_MODELVIEW
);
424 glMatrixMode(GL_PROJECTION
);
427 glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
429 glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
431 glViewport(0, 0, window()->width(), window()->height());
433 QVector
<QRect
> rects
= dirtyRegion
.rects();
434 glColor4f(1, 1, 1, 1);
435 for (int i
= 0; i
< rects
.size(); ++i
) {
436 QRect rect
= rects
.at(i
);
440 drawTexture(rect
, d_ptr
->tex_id
, window()->size(), rect
);
445 d_ptr
->paintedRegion
= QRegion();
447 context()->swapBuffers();
455 QGLContext
*ctx
= reinterpret_cast<QGLContext
*>(parent
->d_func()->extraData()->glContext
);
458 texture
= d_ptr
->fbo
->texture();
460 d_ptr
->pb
->makeCurrent();
461 glBindTexture(target
, d_ptr
->pb_tex_id
);
462 const uint bottom
= window()->height() - (br
.y() + br
.height());
463 glCopyTexSubImage2D(target
, 0, br
.x(), bottom
, br
.x(), bottom
, br
.width(), br
.height());
464 texture
= d_ptr
->pb_tex_id
;
465 glBindTexture(target
, 0);
468 QSize size
= widget
->rect().size();
469 if (d_ptr
->destructive_swap_buffers
&& ctx
->format().doubleBuffer()) {
470 rect
= parent
->rect();
471 br
= rect
.translated(wOffset
);
472 size
= parent
->size();
475 glDisable(GL_SCISSOR_TEST
);
477 if (d_ptr
->fbo
&& QGLExtensions::glExtensions
& QGLExtensions::FramebufferBlit
) {
478 const int h
= d_ptr
->fbo
->height();
480 const int x0
= rect
.left();
481 const int x1
= rect
.left() + rect
.width();
482 const int y0
= h
- (rect
.top() + rect
.height());
483 const int y1
= h
- rect
.top();
485 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT
, 0);
487 glBlitFramebufferEXT(x0
, y0
, x1
, y1
,
492 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT
, d_ptr
->fbo
->handle());
494 #if !defined(QT_OPENGL_ES_2)
496 glDisable(GL_DEPTH_TEST
);
499 d_ptr
->fbo
->release();
503 ctx
->updatePaintDevice();
507 glMatrixMode(GL_MODELVIEW
);
510 glMatrixMode(GL_PROJECTION
);
513 glOrtho(0, size
.width(), size
.height(), 0, -999999, 999999);
515 glOrthof(0, size
.width(), size
.height(), 0, -999999, 999999);
517 glViewport(0, 0, size
.width(), size
.height());
519 glColor4f(1, 1, 1, 1);
520 drawTexture(rect
, texture
, window()->size(), br
);
527 if (ctx
->format().doubleBuffer())
533 void QGLWindowSurface::updateGeometry()
535 QRect rect
= QWindowSurface::geometry();
537 const GLenum target
= GL_TEXTURE_2D
;
539 if (rect
.width() <= 0 || rect
.height() <= 0)
542 if (d_ptr
->size
== rect
.size())
545 d_ptr
->size
= rect
.size();
548 if (d_ptr
->destructive_swap_buffers
) {
549 glBindTexture(target
, d_ptr
->tex_id
);
550 glTexImage2D(target
, 0, GL_RGBA
, rect
.width(), rect
.height(), 0, GL_RGB
, GL_UNSIGNED_BYTE
, 0);
551 glBindTexture(target
, 0);
556 if (d_ptr
->destructive_swap_buffers
557 && (QGLExtensions::glExtensions
& QGLExtensions::FramebufferObject
)
558 #ifdef QT_OPENGL_ES_2
559 && (QGLExtensions::glExtensions
& QGLExtensions::FramebufferBlit
)
561 && (d_ptr
->fbo
|| !d_ptr
->tried_fbo
))
563 d_ptr
->tried_fbo
= true;
564 hijackWindow(window());
565 QGLContext
*ctx
= reinterpret_cast<QGLContext
*>(window()->d_func()->extraData()->glContext
);
566 ctx
->d_ptr
->internal_context
= true;
570 QGLFramebufferObjectFormat format
;
571 format
.setAttachment(QGLFramebufferObject::CombinedDepthStencil
);
572 format
.setInternalFormat(GL_RGBA
);
573 format
.setTextureTarget(target
);
575 if (QGLExtensions::glExtensions
& QGLExtensions::FramebufferBlit
)
576 format
.setSamples(8);
578 d_ptr
->fbo
= new QGLFramebufferObject(rect
.size(), format
);
580 if (d_ptr
->fbo
->isValid()) {
581 qDebug() << "Created Window Surface FBO" << rect
.size()
582 << "with samples" << d_ptr
->fbo
->format().samples();
585 qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
591 #if !defined(QT_OPENGL_ES_2)
592 if (d_ptr
->destructive_swap_buffers
&& (d_ptr
->pb
|| !d_ptr
->tried_pb
)) {
593 d_ptr
->tried_pb
= true;
596 d_ptr
->pb
->makeCurrent();
597 glDeleteTextures(1, &d_ptr
->pb_tex_id
);
602 d_ptr
->pb
= new QGLPixelBuffer(rect
.width(), rect
.height(),
603 QGLFormat(QGL::SampleBuffers
| QGL::StencilBuffer
| QGL::DepthBuffer
),
604 qt_gl_share_widget());
606 if (d_ptr
->pb
->isValid()) {
607 qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr
->pb
->format().sampleBuffers();
608 d_ptr
->pb
->makeCurrent();
610 glGenTextures(1, &d_ptr
->pb_tex_id
);
611 glBindTexture(target
, d_ptr
->pb_tex_id
);
612 glTexImage2D(target
, 0, GL_RGBA
, rect
.width(), rect
.height(), 0, GL_RGBA
, GL_UNSIGNED_BYTE
, 0);
614 glTexParameterf(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
615 glTexParameterf(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
616 glBindTexture(target
, 0);
618 glMatrixMode(GL_PROJECTION
);
621 glOrtho(0, d_ptr
->pb
->width(), d_ptr
->pb
->height(), 0, -999999, 999999);
623 glOrthof(0, d_ptr
->pb
->width(), d_ptr
->pb
->height(), 0, -999999, 999999);
626 d_ptr
->pb
->d_ptr
->qctx
->d_func()->internal_context
= true;
629 qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
634 #endif // !defined(QT_OPENGL_ES_2)
636 hijackWindow(window());
637 QGLContext
*ctx
= reinterpret_cast<QGLContext
*>(window()->d_func()->extraData()->glContext
);
640 if (d_ptr
->destructive_swap_buffers
) {
641 glGenTextures(1, &d_ptr
->tex_id
);
642 glBindTexture(target
, d_ptr
->tex_id
);
643 glTexImage2D(target
, 0, GL_RGBA
, rect
.width(), rect
.height(), 0, GL_RGB
, GL_UNSIGNED_BYTE
, 0);
645 glTexParameterf(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
646 glTexParameterf(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
647 glBindTexture(target
, 0);
650 qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
652 d_ptr
->ctx
->d_ptr
->internal_context
= true;
655 void QGLWindowSurface::setGeometry(const QRect
&rect
)
657 QWindowSurface::setGeometry(rect
);
660 bool QGLWindowSurface::scroll(const QRegion
&area
, int dx
, int dy
)
662 // this code randomly fails currently for unknown reasons
668 d_ptr
->pb
->makeCurrent();
670 QRect br
= area
.boundingRect();
673 // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
674 // ## maybe we should use glCopyTexSubImage insteadk
675 if (dx
== 1 || dx
== -1 || dy
== 1 || dy
== -1 || dy
== 2)
678 glRasterPos2i(br
.x() + dx
, br
.y() + br
.height() + dy
);
679 glCopyPixels(br
.x(), d_ptr
->pb
->height() - (br
.y() + br
.height()), br
.width(), br
.height(), GL_COLOR
);
683 const GLenum target
= GL_TEXTURE_2D
;
685 glBindTexture(target
, d_ptr
->tex_id
);
686 glCopyTexImage2D(target
, 0, GL_RGBA
, br
.x(), d_ptr
->pb
->height() - (br
.y() + br
.height()), br
.width(), br
.height(), 0);
687 glBindTexture(target
, 0);
689 drawTexture(br
.translated(dx
, dy
), d_ptr
->tex_id
, window()->size());
694 static void drawTexture(const QRectF
&rect
, GLuint tex_id
, const QSize
&texSize
, const QRectF
&br
)
696 const GLenum target
= GL_TEXTURE_2D
;
697 QRectF src
= br
.isEmpty()
698 ? QRectF(QPointF(), texSize
)
699 : QRectF(QPointF(br
.x(), texSize
.height() - br
.bottom()), br
.size());
701 if (target
== GL_TEXTURE_2D
) {
702 qreal width
= texSize
.width();
703 qreal height
= texSize
.height();
705 src
.setLeft(src
.left() / width
);
706 src
.setRight(src
.right() / width
);
707 src
.setTop(src
.top() / height
);
708 src
.setBottom(src
.bottom() / height
);
711 const q_vertexType tx1
= f2vt(src
.left());
712 const q_vertexType tx2
= f2vt(src
.right());
713 const q_vertexType ty1
= f2vt(src
.top());
714 const q_vertexType ty2
= f2vt(src
.bottom());
716 q_vertexType texCoordArray
[4*2] = {
717 tx1
, ty2
, tx2
, ty2
, tx2
, ty1
, tx1
, ty1
720 q_vertexType vertexArray
[4*2];
721 extern void qt_add_rect_to_array(const QRectF
&r
, q_vertexType
*array
); // qpaintengine_opengl.cpp
722 qt_add_rect_to_array(rect
, vertexArray
);
724 #if !defined(QT_OPENGL_ES_2)
725 glVertexPointer(2, q_vertexTypeEnum
, 0, vertexArray
);
726 glTexCoordPointer(2, q_vertexTypeEnum
, 0, texCoordArray
);
728 glBindTexture(target
, tex_id
);
731 glEnableClientState(GL_VERTEX_ARRAY
);
732 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
733 glDrawArrays(GL_TRIANGLE_FAN
, 0, 4);
734 glDisableClientState(GL_VERTEX_ARRAY
);
735 glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
739 glBindTexture(target
, 0);
742 QImage
*QGLWindowSurface::buffer(const QWidget
*widget
)
747 image
= d_ptr
->pb
->toImage();
749 image
= d_ptr
->fbo
->toImage();
754 QRect rect
= widget
->rect();
755 rect
.translate(widget
->mapTo(widget
->window(), QPoint()));
757 QImage subImage
= image
.copy(rect
);
758 d_ptr
->buffers
<< subImage
;
759 return &d_ptr
->buffers
.last();