/src/opengl/qwindowsurface_gl.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 1185 lines · 882 code · 219 blank · 84 comment · 172 complexity · 0aa3ea9e310f6fe3df3bd2a99c7462d6 MD5 · raw file

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