source: trunk/src/opengl/qwindowsurface_gl.cpp@ 1023

Last change on this file since 1023 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 31.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtOpenGL module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
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.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtGui/QApplication>
43#include <QtGui/QColormap>
44#include <QtGui/QDesktopWidget>
45#include <QtGui/QPaintDevice>
46#include <QtGui/QWidget>
47
48#include <qglframebufferobject.h>
49#include <qglpixelbuffer.h>
50#include <qcolormap.h>
51#include <qdesktopwidget.h>
52#include <private/qwidget_p.h>
53#include "qdebug.h"
54
55#ifdef Q_WS_X11
56#include <private/qt_x11_p.h>
57#include <qx11info_x11.h>
58
59#ifndef QT_OPENGL_ES
60#include <GL/glx.h>
61#include <X11/Xlib.h>
62#endif
63#endif //Q_WS_X11
64
65#include <private/qglextensions_p.h>
66#include <private/qwindowsurface_gl_p.h>
67
68#include <private/qgl_p.h>
69
70#include <private/qglpixelbuffer_p.h>
71#include <private/qgraphicssystem_gl_p.h>
72
73#include <private/qpaintengineex_opengl2_p.h>
74#include <private/qpixmapdata_gl_p.h>
75
76#ifndef QT_OPENGL_ES_2
77#include <private/qpaintengine_opengl_p.h>
78#endif
79
80#ifndef GLX_ARB_multisample
81#define GLX_SAMPLE_BUFFERS_ARB 100000
82#define GLX_SAMPLES_ARB 100001
83#endif
84
85#ifndef QT_NO_EGL
86#include <private/qeglcontext_p.h>
87#endif
88
89QT_BEGIN_NAMESPACE
90
91//
92// QGLGraphicsSystem
93//
94#ifdef Q_WS_WIN
95extern Q_GUI_EXPORT bool qt_win_owndc_required;
96#endif
97QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL)
98 : QGraphicsSystem(), m_useX11GL(useX11GL)
99{
100#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
101 // only override the system defaults if the user hasn't already
102 // picked a visual
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
106 int i = 0;
107 int spec[16];
108 spec[i++] = GLX_RGBA;
109 spec[i++] = GLX_DOUBLEBUFFER;
110
111 if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) {
112 spec[i++] = GLX_DEPTH_SIZE;
113 spec[i++] = 8;
114 spec[i++] = GLX_STENCIL_SIZE;
115 spec[i++] = 8;
116 spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
117 spec[i++] = 1;
118 spec[i++] = GLX_SAMPLES_ARB;
119 spec[i++] = 4;
120 }
121
122 spec[i++] = XNone;
123
124 XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
125 if (vi) {
126 X11->visual_id = vi->visualid;
127 X11->visual_class = vi->c_class;
128
129 QGLFormat format;
130 int res;
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);
137 if (format.depth())
138 format.setDepthBufferSize(res);
139 glXGetConfig(X11->display, vi, GLX_RGBA, &res);
140 format.setRgba(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);
149 if (format.alpha())
150 format.setAlphaBufferSize(res);
151 glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
152 format.setAccum(res);
153 if (format.accum())
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);
166 }
167
168 QGLWindowSurface::surfaceFormat = format;
169 XFree(vi);
170
171 printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
172 }
173 }
174#elif defined(Q_WS_WIN)
175 QGLWindowSurface::surfaceFormat.setDoubleBuffer(true);
176
177 qt_win_owndc_required = true;
178#endif
179}
180
181//
182// QGLWindowSurface
183//
184
185class QGLGlobalShareWidget
186{
187public:
188 QGLGlobalShareWidget() : widget(0), initializing(false) {}
189
190 QGLWidget *shareWidget() {
191 if (!initializing && !widget && !cleanedUp) {
192 initializing = true;
193
194 widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer));
195 widget->resize(1, 1);
196
197 // We don't need this internal widget to appear in QApplication::topLevelWidgets()
198 if (QWidgetPrivate::allWidgets)
199 QWidgetPrivate::allWidgets->remove(widget);
200 initializing = false;
201 }
202 return widget;
203 }
204
205 // destroys the share widget and prevents recreation
206 void cleanup() {
207 QGLWidget *w = widget;
208 cleanedUp = true;
209 widget = 0;
210 delete w;
211 }
212
213 // destroys the share widget, but allows it to be recreated later on
214 void destroy() {
215 if (cleanedUp)
216 return;
217
218 QGLWidget *w = widget;
219
220 // prevent potential recursions
221 cleanedUp = true;
222 widget = 0;
223 delete w;
224 cleanedUp = false;
225 }
226
227 static bool cleanedUp;
228
229private:
230 QGLWidget *widget;
231 bool initializing;
232};
233
234bool QGLGlobalShareWidget::cleanedUp = false;
235
236static void qt_cleanup_gl_share_widget();
237Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
238 {
239 qAddPostRoutine(qt_cleanup_gl_share_widget);
240 })
241
242static void qt_cleanup_gl_share_widget()
243{
244 _qt_gl_share_widget()->cleanup();
245}
246
247QGLWidget* qt_gl_share_widget()
248{
249 if (QGLGlobalShareWidget::cleanedUp)
250 return 0;
251 return _qt_gl_share_widget()->shareWidget();
252}
253
254void qt_destroy_gl_share_widget()
255{
256 _qt_gl_share_widget()->destroy();
257}
258
259struct QGLWindowSurfacePrivate
260{
261 QGLFramebufferObject *fbo;
262 QGLPixelBuffer *pb;
263 GLuint tex_id;
264 GLuint pb_tex_id;
265
266 int tried_fbo : 1;
267 int tried_pb : 1;
268 int destructive_swap_buffers : 1;
269 int geometry_updated : 1;
270 int did_paint : 1;
271
272 QGLContext *ctx;
273
274 QList<QGLContext **> contexts;
275
276 QRegion paintedRegion;
277 QSize size;
278
279 QSize textureSize;
280
281 QList<QImage> buffers;
282 QGLWindowSurfaceGLPaintDevice glDevice;
283 QGLWindowSurface* q_ptr;
284};
285
286QGLFormat QGLWindowSurface::surfaceFormat;
287QGLWindowSurface::SwapMode QGLWindowSurface::swapBehavior = QGLWindowSurface::AutomaticSwap;
288
289void QGLWindowSurfaceGLPaintDevice::endPaint()
290{
291 glFlush();
292 QGLPaintDevice::endPaint();
293}
294
295QSize QGLWindowSurfaceGLPaintDevice::size() const
296{
297 return d->size;
298}
299
300QGLContext* QGLWindowSurfaceGLPaintDevice::context() const
301{
302 return d->ctx;
303}
304
305
306int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const
307{
308 return qt_paint_device_metric(d->q_ptr->window(), m);
309}
310
311QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const
312{
313 return qt_qgl_paint_engine();
314}
315
316QGLWindowSurface::QGLWindowSurface(QWidget *window)
317 : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
318{
319 Q_ASSERT(window->isTopLevel());
320 d_ptr->pb = 0;
321 d_ptr->fbo = 0;
322 d_ptr->ctx = 0;
323 d_ptr->tex_id = 0;
324#if defined (QT_OPENGL_ES_2)
325 d_ptr->tried_fbo = true;
326 d_ptr->tried_pb = true;
327#else
328 d_ptr->tried_fbo = false;
329 d_ptr->tried_pb = false;
330#endif
331 d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull();
332 d_ptr->glDevice.d = d_ptr;
333 d_ptr->q_ptr = this;
334 d_ptr->geometry_updated = false;
335 d_ptr->did_paint = false;
336}
337
338QGLWindowSurface::~QGLWindowSurface()
339{
340 if (d_ptr->ctx)
341 glDeleteTextures(1, &d_ptr->tex_id);
342 foreach(QGLContext **ctx, d_ptr->contexts) {
343 delete *ctx;
344 *ctx = 0;
345 }
346
347 delete d_ptr->pb;
348 delete d_ptr->fbo;
349 delete d_ptr;
350}
351
352void QGLWindowSurface::deleted(QObject *object)
353{
354 QWidget *widget = qobject_cast<QWidget *>(object);
355 if (widget) {
356 if (widget == window()) {
357 // Make sure that the fbo is destroyed before destroying its context.
358 delete d_ptr->fbo;
359 d_ptr->fbo = 0;
360 }
361
362 QWidgetPrivate *widgetPrivate = widget->d_func();
363 if (widgetPrivate->extraData()) {
364 union { QGLContext **ctxPtr; void **voidPtr; };
365 voidPtr = &widgetPrivate->extraData()->glContext;
366 int index = d_ptr->contexts.indexOf(ctxPtr);
367 if (index != -1) {
368 delete *ctxPtr;
369 *ctxPtr = 0;
370 d_ptr->contexts.removeAt(index);
371 }
372 }
373 }
374}
375
376void QGLWindowSurface::hijackWindow(QWidget *widget)
377{
378 QWidgetPrivate *widgetPrivate = widget->d_func();
379 widgetPrivate->createExtra();
380 if (widgetPrivate->extraData()->glContext)
381 return;
382
383 QGLContext *ctx = NULL;
384
385 // For translucent top-level widgets we need alpha in the format.
386 if (widget->testAttribute(Qt::WA_TranslucentBackground)) {
387 QGLFormat modFormat(surfaceFormat);
388 modFormat.setSampleBuffers(false);
389 modFormat.setSamples(0);
390 modFormat.setAlpha(true);
391 ctx = new QGLContext(modFormat, widget);
392 } else
393 ctx = new QGLContext(surfaceFormat, widget);
394
395 ctx->create(qt_gl_share_widget()->context());
396
397#ifndef QT_NO_EGL
398 static bool checkedForNOKSwapRegion = false;
399 static bool haveNOKSwapRegion = false;
400
401 if (!checkedForNOKSwapRegion) {
402 haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2");
403 checkedForNOKSwapRegion = true;
404
405 if (haveNOKSwapRegion)
406 qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates.";
407 }
408
409 if (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED &&
410 ! haveNOKSwapRegion)
411 setPartialUpdateSupport(false); // Force full-screen updates
412 else
413 setPartialUpdateSupport(true);
414#endif
415
416 widgetPrivate->extraData()->glContext = ctx;
417
418 union { QGLContext **ctxPtr; void **voidPtr; };
419
420 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*)));
421
422 voidPtr = &widgetPrivate->extraData()->glContext;
423 d_ptr->contexts << ctxPtr;
424 qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
425}
426
427QGLContext *QGLWindowSurface::context() const
428{
429 return d_ptr->ctx;
430}
431
432QPaintDevice *QGLWindowSurface::paintDevice()
433{
434 updateGeometry();
435
436 if (d_ptr->pb)
437 return d_ptr->pb;
438
439 if (d_ptr->ctx)
440 return &d_ptr->glDevice;
441
442 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
443 ctx->makeCurrent();
444
445 Q_ASSERT(d_ptr->fbo);
446 return d_ptr->fbo;
447}
448
449static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
450
451void QGLWindowSurface::beginPaint(const QRegion &)
452{
453 if (!context())
454 return;
455
456 int clearFlags = 0;
457
458 if (context()->d_func()->workaround_needsFullClearOnEveryFrame)
459 clearFlags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
460 else if (context()->format().alpha())
461 clearFlags = GL_COLOR_BUFFER_BIT;
462
463 if (clearFlags) {
464 glClearColor(0.0, 0.0, 0.0, 0.0);
465 glClear(clearFlags);
466 }
467
468 d_ptr->did_paint = true;
469}
470
471void QGLWindowSurface::endPaint(const QRegion &rgn)
472{
473 if (context())
474 d_ptr->paintedRegion |= rgn;
475
476 d_ptr->buffers.clear();
477}
478
479static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect)
480{
481 glDisable(GL_DEPTH_TEST);
482 glDisable(GL_SCISSOR_TEST);
483 glDisable(GL_BLEND);
484
485 glViewport(0, 0, viewport.width(), viewport.height());
486
487 QGLShaderProgram *blitProgram =
488 QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram();
489 blitProgram->bind();
490 blitProgram->setUniformValue("imageTexture", 0 /*QT_IMAGE_TEXTURE_UNIT*/);
491
492 // The shader manager's blit program does not multiply the
493 // vertices by the pmv matrix, so we need to do the effect
494 // of the orthographic projection here ourselves.
495 QRectF r;
496 qreal w = viewport.width();
497 qreal h = viewport.height();
498 r.setLeft((targetRect.left() / w) * 2.0f - 1.0f);
499 if (targetRect.right() == (viewport.width() - 1))
500 r.setRight(1.0f);
501 else
502 r.setRight((targetRect.right() / w) * 2.0f - 1.0f);
503 r.setBottom((targetRect.top() / h) * 2.0f - 1.0f);
504 if (targetRect.bottom() == (viewport.height() - 1))
505 r.setTop(1.0f);
506 else
507 r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f);
508
509 drawTexture(r, texture, texSize, sourceRect);
510}
511
512
513void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
514{
515 //### Find out why d_ptr->geometry_updated isn't always false.
516 // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either
517 // d_ptr->fbo or d_ptr->pb is allocated and has the correct size.
518 if (d_ptr->geometry_updated)
519 return;
520
521 // did_paint is set to true in ::beginPaint. ::beginPaint means that we
522 // at least cleared the background (= painted something). In EGL API it's a
523 // mistake to call swapBuffers if nothing was painted unless
524 // EGL_BUFFER_PRESERVED is set. This check protects the flush func from
525 // being executed if it's for nothing.
526 if (!hasPartialUpdateSupport() && !d_ptr->did_paint)
527 return;
528
529 QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
530 Q_ASSERT(parent);
531
532 if (!geometry().isValid())
533 return;
534
535 // Needed to support native child-widgets...
536 hijackWindow(parent);
537
538 QRect br = rgn.boundingRect().translated(offset);
539 br = br.intersected(window()->rect());
540 QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
541 QRect rect = br.translated(-offset - wOffset);
542
543 const GLenum target = GL_TEXTURE_2D;
544 Q_UNUSED(target);
545
546 if (QGLWindowSurface::swapBehavior == QGLWindowSurface::KillSwap)
547 return;
548
549 if (context()) {
550 context()->makeCurrent();
551
552 if (context()->format().doubleBuffer()) {
553#if !defined(QT_OPENGL_ES_2)
554 if (d_ptr->destructive_swap_buffers) {
555 glBindTexture(target, d_ptr->tex_id);
556
557 QVector<QRect> rects = d_ptr->paintedRegion.rects();
558 for (int i = 0; i < rects.size(); ++i) {
559 QRect br = rects.at(i);
560 if (br.isEmpty())
561 continue;
562
563 const uint bottom = window()->height() - (br.y() + br.height());
564 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
565 }
566
567 glBindTexture(target, 0);
568
569 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
570
571 if (!dirtyRegion.isEmpty()) {
572 glMatrixMode(GL_MODELVIEW);
573 glLoadIdentity();
574
575 glMatrixMode(GL_PROJECTION);
576 glLoadIdentity();
577#ifndef QT_OPENGL_ES
578 glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
579#else
580 glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
581#endif
582 glViewport(0, 0, window()->width(), window()->height());
583
584 QVector<QRect> rects = dirtyRegion.rects();
585 glColor4f(1, 1, 1, 1);
586 for (int i = 0; i < rects.size(); ++i) {
587 QRect rect = rects.at(i);
588 if (rect.isEmpty())
589 continue;
590
591 drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
592 }
593 }
594 }
595#endif
596 bool doingPartialUpdate = false;
597 if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AutomaticSwap)
598 doingPartialUpdate = hasPartialUpdateSupport() && br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2;
599 else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysFullSwap)
600 doingPartialUpdate = false;
601 else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysPartialSwap)
602 doingPartialUpdate = hasPartialUpdateSupport();
603
604 QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
605 if (widget != window()) {
606 if (initializeOffscreenTexture(window()->size()))
607 qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss";
608 glBindTexture(target, d_ptr->tex_id);
609
610 const uint bottom = window()->height() - (br.y() + br.height());
611 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
612
613 glBindTexture(target, 0);
614
615 ctx->makeCurrent();
616 if (doingPartialUpdate)
617 blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br);
618 else
619 blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset));
620 }
621
622 if (doingPartialUpdate)
623 ctx->d_func()->swapRegion(br);
624 else
625 ctx->swapBuffers();
626
627 d_ptr->paintedRegion = QRegion();
628 } else {
629 glFlush();
630 }
631
632 return;
633 }
634
635 QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext());
636 QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
637
638 // QPainter::end() should have unbound the fbo, otherwise something is very wrong...
639 Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound());
640
641 if (ctx != previous_ctx) {
642 ctx->makeCurrent();
643 }
644
645 QSize size = widget->rect().size();
646 if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) {
647 rect = parent->rect();
648 br = rect.translated(wOffset + offset);
649 size = parent->size();
650 }
651
652 glDisable(GL_SCISSOR_TEST);
653
654 if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) {
655 const int h = d_ptr->fbo->height();
656
657 const int sx0 = br.left();
658 const int sx1 = br.left() + br.width();
659 const int sy0 = h - (br.top() + br.height());
660 const int sy1 = h - br.top();
661
662 const int tx0 = rect.left();
663 const int tx1 = rect.left() + rect.width();
664 const int ty0 = parent->height() - (rect.top() + rect.height());
665 const int ty1 = parent->height() - rect.top();
666
667 if (window() == parent || d_ptr->fbo->format().samples() <= 1) {
668 if (ctx->d_ptr->current_fbo != 0)
669 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
670
671 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
672
673 glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
674 tx0, ty0, tx1, ty1,
675 GL_COLOR_BUFFER_BIT,
676 GL_NEAREST);
677
678 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
679 } else {
680 // can't do sub-region blits with multisample FBOs
681 QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat());
682
683 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle());
684 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle());
685
686 glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
687 0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(),
688 GL_COLOR_BUFFER_BIT,
689 GL_NEAREST);
690
691 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle());
692 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0);
693
694 glBlitFramebufferEXT(sx0, sy0, sx1, sy1,
695 tx0, ty0, tx1, ty1,
696 GL_COLOR_BUFFER_BIT,
697 GL_NEAREST);
698
699 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0);
700
701 qgl_fbo_pool()->release(temp);
702 }
703
704 ctx->d_ptr->current_fbo = 0;
705 }
706#if !defined(QT_OPENGL_ES_2)
707 else {
708 GLuint texture;
709 if (d_ptr->fbo) {
710 texture = d_ptr->fbo->texture();
711 } else {
712 d_ptr->pb->makeCurrent();
713 glBindTexture(target, d_ptr->pb_tex_id);
714 const uint bottom = window()->height() - (br.y() + br.height());
715 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
716 texture = d_ptr->pb_tex_id;
717 glBindTexture(target, 0);
718 }
719
720 glDisable(GL_DEPTH_TEST);
721
722 if (d_ptr->fbo) {
723 d_ptr->fbo->release();
724 } else {
725 ctx->makeCurrent();
726 }
727
728 glMatrixMode(GL_MODELVIEW);
729 glLoadIdentity();
730
731 glMatrixMode(GL_PROJECTION);
732 glLoadIdentity();
733#ifndef QT_OPENGL_ES
734 glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
735#else
736 glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
737#endif
738 glViewport(0, 0, size.width(), size.height());
739
740 glColor4f(1, 1, 1, 1);
741 drawTexture(rect, texture, window()->size(), br);
742
743 if (d_ptr->fbo)
744 d_ptr->fbo->bind();
745 }
746#else
747 // OpenGL/ES 2.0 version of the fbo blit.
748 else if (d_ptr->fbo) {
749 Q_UNUSED(target);
750
751 if (d_ptr->fbo->isBound())
752 d_ptr->fbo->release();
753
754 blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br);
755 }
756#endif
757
758 if (ctx->format().doubleBuffer())
759 ctx->swapBuffers();
760 else
761 glFlush();
762
763 d_ptr->did_paint = false;
764}
765
766
767void QGLWindowSurface::setGeometry(const QRect &rect)
768{
769 QWindowSurface::setGeometry(rect);
770 d_ptr->geometry_updated = true;
771}
772
773
774void QGLWindowSurface::updateGeometry() {
775 if (!d_ptr->geometry_updated)
776 return;
777 d_ptr->geometry_updated = false;
778
779 QRect rect = geometry();
780 hijackWindow(window());
781 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
782
783#ifdef Q_WS_MAC
784 ctx->updatePaintDevice();
785#endif
786
787 const GLenum target = GL_TEXTURE_2D;
788
789 if (rect.width() <= 0 || rect.height() <= 0)
790 return;
791
792 if (d_ptr->size == rect.size())
793 return;
794
795 d_ptr->size = rect.size();
796
797 if (d_ptr->ctx) {
798#ifndef QT_OPENGL_ES_2
799 if (d_ptr->destructive_swap_buffers)
800 initializeOffscreenTexture(rect.size());
801#endif
802 return;
803 }
804
805 if (d_ptr->destructive_swap_buffers
806 && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject)
807 && (d_ptr->fbo || !d_ptr->tried_fbo)
808 && qt_gl_preferGL2Engine())
809 {
810 d_ptr->tried_fbo = true;
811 ctx->d_ptr->internal_context = true;
812 ctx->makeCurrent();
813 delete d_ptr->fbo;
814
815 QGLFramebufferObjectFormat format;
816 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
817 format.setInternalTextureFormat(GLenum(GL_RGBA));
818 format.setTextureTarget(target);
819
820 if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)
821 format.setSamples(8);
822
823 d_ptr->fbo = new QGLFramebufferObject(rect.size(), format);
824
825 if (d_ptr->fbo->isValid()) {
826 qDebug() << "Created Window Surface FBO" << rect.size()
827 << "with samples" << d_ptr->fbo->format().samples();
828 return;
829 } else {
830 qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
831 delete d_ptr->fbo;
832 d_ptr->fbo = 0;
833 }
834 }
835
836#if !defined(QT_OPENGL_ES_2)
837 if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) {
838 d_ptr->tried_pb = true;
839
840 if (d_ptr->pb) {
841 d_ptr->pb->makeCurrent();
842 glDeleteTextures(1, &d_ptr->pb_tex_id);
843 }
844
845 delete d_ptr->pb;
846
847 d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
848 QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
849 qt_gl_share_widget());
850
851 if (d_ptr->pb->isValid()) {
852 qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers();
853 d_ptr->pb->makeCurrent();
854
855 glGenTextures(1, &d_ptr->pb_tex_id);
856 glBindTexture(target, d_ptr->pb_tex_id);
857 glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
858
859 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
860 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
861 glBindTexture(target, 0);
862
863 glMatrixMode(GL_PROJECTION);
864 glLoadIdentity();
865 glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
866
867 d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
868 return;
869 } else {
870 qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
871 delete d_ptr->pb;
872 d_ptr->pb = 0;
873 }
874 }
875#endif // !defined(QT_OPENGL_ES_2)
876
877 ctx->makeCurrent();
878
879#ifndef QT_OPENGL_ES_2
880 if (d_ptr->destructive_swap_buffers)
881 initializeOffscreenTexture(rect.size());
882#endif
883
884 qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
885 d_ptr->ctx = ctx;
886 d_ptr->ctx->d_ptr->internal_context = true;
887}
888
889bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size)
890{
891 if (size == d_ptr->textureSize)
892 return false;
893
894 glGenTextures(1, &d_ptr->tex_id);
895 glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id);
896 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
897
898 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
899 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
900 glBindTexture(GL_TEXTURE_2D, 0);
901
902 d_ptr->textureSize = size;
903 return true;
904}
905
906bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
907{
908 // this code randomly fails currently for unknown reasons
909 return false;
910
911 if (!d_ptr->pb)
912 return false;
913
914 d_ptr->pb->makeCurrent();
915
916 QRect br = area.boundingRect();
917
918#if 0
919 // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
920 // ## maybe we should use glCopyTexSubImage insteadk
921 if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
922 return false;
923
924 glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
925 glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
926 return true;
927#endif
928
929 const GLenum target = GL_TEXTURE_2D;
930
931 glBindTexture(target, d_ptr->tex_id);
932 glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
933 glBindTexture(target, 0);
934
935 drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
936
937 return true;
938}
939
940static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
941{
942 const GLenum target = GL_TEXTURE_2D;
943 QRectF src = br.isEmpty()
944 ? QRectF(QPointF(), texSize)
945 : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
946
947 if (target == GL_TEXTURE_2D) {
948 qreal width = texSize.width();
949 qreal height = texSize.height();
950
951 src.setLeft(src.left() / width);
952 src.setRight(src.right() / width);
953 src.setTop(src.top() / height);
954 src.setBottom(src.bottom() / height);
955 }
956
957 const GLfloat tx1 = src.left();
958 const GLfloat tx2 = src.right();
959 const GLfloat ty1 = src.top();
960 const GLfloat ty2 = src.bottom();
961
962 GLfloat texCoordArray[4*2] = {
963 tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
964 };
965
966 GLfloat vertexArray[4*2];
967 extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp
968 qt_add_rect_to_array(rect, vertexArray);
969
970#if !defined(QT_OPENGL_ES_2)
971 glVertexPointer(2, GL_FLOAT, 0, vertexArray);
972 glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);
973
974 glBindTexture(target, tex_id);
975 glEnable(target);
976
977 glEnableClientState(GL_VERTEX_ARRAY);
978 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
979 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
980 glDisableClientState(GL_VERTEX_ARRAY);
981 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
982
983 glDisable(target);
984 glBindTexture(target, 0);
985#else
986 glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray);
987 glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray);
988
989 glBindTexture(target, tex_id);
990
991 glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
992 glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
993 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
994 glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
995 glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR);
996
997 glBindTexture(target, 0);
998#endif
999}
1000
1001QImage *QGLWindowSurface::buffer(const QWidget *widget)
1002{
1003 QImage image;
1004
1005 if (d_ptr->pb)
1006 image = d_ptr->pb->toImage();
1007 else if (d_ptr->fbo)
1008 image = d_ptr->fbo->toImage();
1009
1010 if (image.isNull())
1011 return 0;
1012
1013 QRect rect = widget->rect();
1014 rect.translate(widget->mapTo(widget->window(), QPoint()));
1015
1016 QImage subImage = image.copy(rect);
1017 d_ptr->buffers << subImage;
1018 return &d_ptr->buffers.last();
1019}
1020
1021
1022
1023QT_END_NAMESPACE
1024
Note: See TracBrowser for help on using the repository browser.