1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtOpenGL module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
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 | |
89 | QT_BEGIN_NAMESPACE |
90 | |
91 | // |
92 | // QGLGraphicsSystem |
93 | // |
94 | #ifdef Q_WS_WIN |
95 | extern Q_GUI_EXPORT bool qt_win_owndc_required; |
96 | #endif |
97 | QGLGraphicsSystem::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 | class QGLGlobalShareWidget |
185 | { |
186 | public: |
187 | QGLGlobalShareWidget() : widget(0), init(false) { |
188 | created = true; |
189 | } |
190 | |
191 | QGLWidget *shareWidget() { |
192 | if (!init && !widget && !cleanedUp) { |
193 | init = true; |
194 | widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer)); |
195 | #ifdef Q_OS_SYMBIAN |
196 | if (!widget->context()->isValid()) { |
197 | delete widget; |
198 | widget = 0; |
199 | init = false; |
200 | return 0; |
201 | } |
202 | #endif |
203 | |
204 | widget->resize(1, 1); |
205 | |
206 | // We don't need this internal widget to appear in QApplication::topLevelWidgets() |
207 | if (QWidgetPrivate::allWidgets) |
208 | QWidgetPrivate::allWidgets->remove(widget); |
209 | init = false; |
210 | } |
211 | return widget; |
212 | } |
213 | |
214 | // destroys the share widget and prevents recreation |
215 | void cleanup() { |
216 | QGLWidget *w = widget; |
217 | cleanedUp = true; |
218 | widget = 0; |
219 | delete w; |
220 | } |
221 | |
222 | // destroys the share widget, but allows it to be recreated later on |
223 | void destroy() { |
224 | if (cleanedUp) |
225 | return; |
226 | |
227 | QGLWidget *w = widget; |
228 | |
229 | // prevent potential recursions |
230 | cleanedUp = true; |
231 | widget = 0; |
232 | delete w; |
233 | cleanedUp = false; |
234 | } |
235 | |
236 | bool initializing() |
237 | { |
238 | return init; |
239 | } |
240 | |
241 | static bool cleanedUp; |
242 | static bool created; |
243 | |
244 | private: |
245 | QGLWidget *widget; |
246 | bool init; |
247 | }; |
248 | |
249 | bool QGLGlobalShareWidget::cleanedUp = false; |
250 | bool QGLGlobalShareWidget::created = false; |
251 | |
252 | static void qt_cleanup_gl_share_widget(); |
253 | Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget, |
254 | { |
255 | qAddPostRoutine(qt_cleanup_gl_share_widget); |
256 | }) |
257 | |
258 | static void qt_cleanup_gl_share_widget() |
259 | { |
260 | if (QGLGlobalShareWidget::created) |
261 | _qt_gl_share_widget()->cleanup(); |
262 | } |
263 | |
264 | QGLWidget* qt_gl_share_widget() |
265 | { |
266 | if (QGLGlobalShareWidget::cleanedUp) |
267 | return 0; |
268 | return _qt_gl_share_widget()->shareWidget(); |
269 | } |
270 | |
271 | void qt_destroy_gl_share_widget() |
272 | { |
273 | if (QGLGlobalShareWidget::created) |
274 | _qt_gl_share_widget()->destroy(); |
275 | } |
276 | |
277 | bool qt_initializing_gl_share_widget() |
278 | { |
279 | if (QGLGlobalShareWidget::created) |
280 | return _qt_gl_share_widget()->initializing(); |
281 | return false; |
282 | } |
283 | |
284 | const QGLContext *qt_gl_share_context() |
285 | { |
286 | QGLWidget *widget = qt_gl_share_widget(); |
287 | if (widget) |
288 | return widget->context(); |
289 | return 0; |
290 | } |
291 | |
292 | struct QGLWindowSurfacePrivate |
293 | { |
294 | QGLFramebufferObject *fbo; |
295 | QGLPixelBuffer *pb; |
296 | GLuint tex_id; |
297 | GLuint pb_tex_id; |
298 | |
299 | int tried_fbo : 1; |
300 | int tried_pb : 1; |
301 | int destructive_swap_buffers : 1; |
302 | int geometry_updated : 1; |
303 | int did_paint : 1; |
304 | |
305 | QGLContext *ctx; |
306 | |
307 | QList<QGLContext **> contexts; |
308 | |
309 | QRegion paintedRegion; |
310 | QSize size; |
311 | |
312 | QSize textureSize; |
313 | |
314 | QList<QImage> buffers; |
315 | QGLWindowSurfaceGLPaintDevice glDevice; |
316 | QGLWindowSurface* q_ptr; |
317 | |
318 | bool swap_region_support; |
319 | }; |
320 | |
321 | QGLFormat QGLWindowSurface::surfaceFormat; |
322 | QGLWindowSurface::SwapMode QGLWindowSurface::swapBehavior = QGLWindowSurface::AutomaticSwap; |
323 | |
324 | QSize QGLWindowSurfaceGLPaintDevice::size() const |
325 | { |
326 | return d->size; |
327 | } |
328 | |
329 | QGLContext* QGLWindowSurfaceGLPaintDevice::context() const |
330 | { |
331 | return d->ctx; |
332 | } |
333 | |
334 | |
335 | int QGLWindowSurfaceGLPaintDevice::metric(PaintDeviceMetric m) const |
336 | { |
337 | return qt_paint_device_metric(d->q_ptr->window(), m); |
338 | } |
339 | |
340 | QPaintEngine *QGLWindowSurfaceGLPaintDevice::paintEngine() const |
341 | { |
342 | return qt_qgl_paint_engine(); |
343 | } |
344 | |
345 | QGLWindowSurface::QGLWindowSurface(QWidget *window) |
346 | : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate) |
347 | { |
348 | // Q_ASSERT(window->isTopLevel()); |
349 | d_ptr->pb = 0; |
350 | d_ptr->fbo = 0; |
351 | d_ptr->ctx = 0; |
352 | d_ptr->tex_id = 0; |
353 | #if defined (QT_OPENGL_ES_2) |
354 | d_ptr->tried_fbo = true; |
355 | d_ptr->tried_pb = true; |
356 | #else |
357 | d_ptr->tried_fbo = false; |
358 | d_ptr->tried_pb = false; |
359 | #endif |
360 | d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE" ).isNull(); |
361 | d_ptr->glDevice.d = d_ptr; |
362 | d_ptr->q_ptr = this; |
363 | d_ptr->geometry_updated = false; |
364 | d_ptr->did_paint = false; |
365 | d_ptr->swap_region_support = false; |
366 | } |
367 | |
368 | QGLWindowSurface::~QGLWindowSurface() |
369 | { |
370 | if (d_ptr->ctx) |
371 | glDeleteTextures(1, &d_ptr->tex_id); |
372 | #ifndef Q_WS_QPA // Don't delete the contexts. Destroying the window does that for us |
373 | foreach(QGLContext **ctx, d_ptr->contexts) { |
374 | delete *ctx; |
375 | *ctx = 0; |
376 | } |
377 | #endif |
378 | delete d_ptr->pb; |
379 | delete d_ptr->fbo; |
380 | delete d_ptr; |
381 | |
382 | if (QGLGlobalShareWidget::cleanedUp) |
383 | return; |
384 | |
385 | #ifdef Q_OS_SYMBIAN |
386 | // Destroy the context if necessary. |
387 | if (qt_gl_share_widget() && !qt_gl_share_context()->isSharing()) |
388 | qt_destroy_gl_share_widget(); |
389 | #endif |
390 | } |
391 | |
392 | void QGLWindowSurface::deleted(QObject *object) |
393 | { |
394 | QWidget *widget = qobject_cast<QWidget *>(object); |
395 | if (widget) { |
396 | if (widget == window()) { |
397 | // Make sure that the fbo is destroyed before destroying its context. |
398 | delete d_ptr->fbo; |
399 | d_ptr->fbo = 0; |
400 | } |
401 | |
402 | #ifndef Q_WS_QPA //no need to specifically delete the QGLContext as it will be deleted by QWidget |
403 | QWidgetPrivate *widgetPrivate = widget->d_func(); |
404 | if (widgetPrivate->extraData()) { |
405 | union { QGLContext **ctxPtrPtr; void **voidPtrPtr; }; |
406 | voidPtrPtr = &widgetPrivate->extraData()->glContext; |
407 | int index = d_ptr->contexts.indexOf(ctxPtrPtr); |
408 | if (index != -1) { |
409 | delete *ctxPtrPtr; |
410 | *ctxPtrPtr = 0; |
411 | d_ptr->contexts.removeAt(index); |
412 | } |
413 | } |
414 | #endif |
415 | } |
416 | } |
417 | |
418 | void QGLWindowSurface::hijackWindow(QWidget *widget) |
419 | { |
420 | QWidgetPrivate *widgetPrivate = widget->d_func(); |
421 | widgetPrivate->createExtra(); |
422 | if (widgetPrivate->extraData()->glContext) |
423 | return; |
424 | |
425 | QGLContext *ctx = NULL; |
426 | |
427 | // For translucent top-level widgets we need alpha in the format. |
428 | if (widget->testAttribute(Qt::WA_TranslucentBackground)) { |
429 | QGLFormat modFormat(surfaceFormat); |
430 | modFormat.setSampleBuffers(false); |
431 | modFormat.setSamples(0); |
432 | modFormat.setAlpha(true); |
433 | ctx = new QGLContext(modFormat, widget); |
434 | } else |
435 | ctx = new QGLContext(surfaceFormat, widget); |
436 | |
437 | ctx->create(qt_gl_share_context()); |
438 | #ifdef Q_OS_SYMBIAN |
439 | if (!ctx->isValid()) { |
440 | delete ctx; |
441 | return; |
442 | } |
443 | #endif |
444 | #ifndef QT_NO_EGL |
445 | static bool checkedForNOKSwapRegion = false; |
446 | static bool haveNOKSwapRegion = false; |
447 | |
448 | if (!checkedForNOKSwapRegion) { |
449 | haveNOKSwapRegion = QEgl::hasExtension("EGL_NOK_swap_region2" ); |
450 | checkedForNOKSwapRegion = true; |
451 | |
452 | if (haveNOKSwapRegion) |
453 | qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates." ; |
454 | } |
455 | |
456 | d_ptr->destructive_swap_buffers = true; |
457 | if (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT) { |
458 | EGLint swapBehavior; |
459 | if (eglQuerySurface(ctx->d_func()->eglContext->display(), ctx->d_func()->eglSurface |
460 | , EGL_SWAP_BEHAVIOR, &swapBehavior)) { |
461 | d_ptr->destructive_swap_buffers = (swapBehavior != EGL_BUFFER_PRESERVED); |
462 | } |
463 | } |
464 | |
465 | d_ptr->swap_region_support = haveNOKSwapRegion; |
466 | #endif |
467 | |
468 | widgetPrivate->extraData()->glContext = ctx; |
469 | |
470 | union { QGLContext **ctxPtrPtr; void **voidPtrPtr; }; |
471 | |
472 | connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(deleted(QObject*))); |
473 | |
474 | voidPtrPtr = &widgetPrivate->extraData()->glContext; |
475 | d_ptr->contexts << ctxPtrPtr; |
476 | |
477 | #ifndef Q_OS_SYMBIAN |
478 | qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size(); |
479 | #endif |
480 | } |
481 | |
482 | QGLContext *QGLWindowSurface::context() const |
483 | { |
484 | return d_ptr->ctx; |
485 | } |
486 | |
487 | QPaintDevice *QGLWindowSurface::paintDevice() |
488 | { |
489 | updateGeometry(); |
490 | |
491 | #ifdef Q_OS_SYMBIAN |
492 | // On symbian we always return glDevice, even if it's invalid |
493 | return &d_ptr->glDevice; |
494 | #else |
495 | if (d_ptr->pb) |
496 | return d_ptr->pb; |
497 | |
498 | if (d_ptr->ctx) |
499 | return &d_ptr->glDevice; |
500 | |
501 | QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext); |
502 | ctx->makeCurrent(); |
503 | |
504 | Q_ASSERT(d_ptr->fbo); |
505 | return d_ptr->fbo; |
506 | #endif |
507 | } |
508 | |
509 | static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF()); |
510 | |
511 | void QGLWindowSurface::beginPaint(const QRegion &) |
512 | { |
513 | d_ptr->did_paint = true; |
514 | updateGeometry(); |
515 | |
516 | int clearFlags = 0; |
517 | |
518 | QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext); |
519 | |
520 | if (!ctx) |
521 | return; |
522 | |
523 | if (ctx->d_func()->workaround_needsFullClearOnEveryFrame) |
524 | clearFlags = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; |
525 | else if (ctx->format().alpha()) |
526 | clearFlags = GL_COLOR_BUFFER_BIT; |
527 | |
528 | if (clearFlags) { |
529 | if (d_ptr->fbo) |
530 | d_ptr->fbo->bind(); |
531 | |
532 | glClearColor(0.0, 0.0, 0.0, 0.0); |
533 | glClear(clearFlags); |
534 | |
535 | if (d_ptr->fbo) |
536 | d_ptr->fbo->release(); |
537 | } |
538 | } |
539 | |
540 | void QGLWindowSurface::endPaint(const QRegion &rgn) |
541 | { |
542 | if (context()) |
543 | d_ptr->paintedRegion |= rgn; |
544 | |
545 | d_ptr->buffers.clear(); |
546 | } |
547 | |
548 | static void blitTexture(QGLContext *ctx, GLuint texture, const QSize &viewport, const QSize &texSize, const QRect &targetRect, const QRect &sourceRect) |
549 | { |
550 | glDisable(GL_DEPTH_TEST); |
551 | glDisable(GL_SCISSOR_TEST); |
552 | glDisable(GL_BLEND); |
553 | |
554 | glViewport(0, 0, viewport.width(), viewport.height()); |
555 | |
556 | QGLShaderProgram *blitProgram = |
557 | QGLEngineSharedShaders::shadersForContext(ctx)->blitProgram(); |
558 | blitProgram->bind(); |
559 | blitProgram->setUniformValue("imageTexture" , 0 /*QT_IMAGE_TEXTURE_UNIT*/); |
560 | |
561 | // The shader manager's blit program does not multiply the |
562 | // vertices by the pmv matrix, so we need to do the effect |
563 | // of the orthographic projection here ourselves. |
564 | QRectF r; |
565 | qreal w = viewport.width(); |
566 | qreal h = viewport.height(); |
567 | r.setLeft((targetRect.left() / w) * 2.0f - 1.0f); |
568 | if (targetRect.right() == (viewport.width() - 1)) |
569 | r.setRight(1.0f); |
570 | else |
571 | r.setRight((targetRect.right() / w) * 2.0f - 1.0f); |
572 | r.setBottom((targetRect.top() / h) * 2.0f - 1.0f); |
573 | if (targetRect.bottom() == (viewport.height() - 1)) |
574 | r.setTop(1.0f); |
575 | else |
576 | r.setTop((targetRect.bottom() / w) * 2.0f - 1.0f); |
577 | |
578 | drawTexture(r, texture, texSize, sourceRect); |
579 | } |
580 | |
581 | |
582 | void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset) |
583 | { |
584 | //### Find out why d_ptr->geometry_updated isn't always false. |
585 | // flush() should not be called when d_ptr->geometry_updated is true. It assumes that either |
586 | // d_ptr->fbo or d_ptr->pb is allocated and has the correct size. |
587 | if (d_ptr->geometry_updated) |
588 | return; |
589 | |
590 | // did_paint is set to true in ::beginPaint. ::beginPaint means that we |
591 | // at least cleared the background (= painted something). In EGL API it's a |
592 | // mistake to call swapBuffers if nothing was painted unless |
593 | // EGL_BUFFER_PRESERVED is set. This check protects the flush func from |
594 | // being executed if it's for nothing. |
595 | if (!d_ptr->destructive_swap_buffers && !d_ptr->did_paint) |
596 | return; |
597 | |
598 | #ifdef Q_OS_SYMBIAN |
599 | if (window() != widget) { |
600 | // For performance reasons we don't support |
601 | // flushing native child widgets on Symbian. |
602 | // It breaks overlapping native child widget |
603 | // rendering in some cases but we prefer performance. |
604 | return; |
605 | } |
606 | #endif |
607 | |
608 | |
609 | QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget(); |
610 | Q_ASSERT(parent); |
611 | |
612 | #if !defined(Q_WS_QPA) |
613 | if (!geometry().isValid()) |
614 | return; |
615 | #else |
616 | if (!size().isValid()) |
617 | return; |
618 | #endif |
619 | |
620 | // Needed to support native child-widgets... |
621 | hijackWindow(parent); |
622 | |
623 | QRect br = rgn.boundingRect().translated(offset); |
624 | br = br.intersected(window()->rect()); |
625 | QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft(); |
626 | QRect rect = br.translated(-offset - wOffset); |
627 | |
628 | const GLenum target = GL_TEXTURE_2D; |
629 | Q_UNUSED(target); |
630 | |
631 | if (QGLWindowSurface::swapBehavior == QGLWindowSurface::KillSwap) |
632 | return; |
633 | |
634 | if (context()) { |
635 | context()->makeCurrent(); |
636 | |
637 | if (context()->format().doubleBuffer()) { |
638 | #if !defined(QT_OPENGL_ES_2) |
639 | if (d_ptr->destructive_swap_buffers) { |
640 | glBindTexture(target, d_ptr->tex_id); |
641 | |
642 | QVector<QRect> rects = d_ptr->paintedRegion.rects(); |
643 | for (int i = 0; i < rects.size(); ++i) { |
644 | QRect br = rects.at(i); |
645 | if (br.isEmpty()) |
646 | continue; |
647 | |
648 | const uint bottom = window()->height() - (br.y() + br.height()); |
649 | glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); |
650 | } |
651 | |
652 | glBindTexture(target, 0); |
653 | |
654 | QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion; |
655 | |
656 | if (!dirtyRegion.isEmpty()) { |
657 | glMatrixMode(GL_MODELVIEW); |
658 | glLoadIdentity(); |
659 | |
660 | glMatrixMode(GL_PROJECTION); |
661 | glLoadIdentity(); |
662 | #ifndef QT_OPENGL_ES |
663 | glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999); |
664 | #else |
665 | glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999); |
666 | #endif |
667 | glViewport(0, 0, window()->width(), window()->height()); |
668 | |
669 | QVector<QRect> rects = dirtyRegion.rects(); |
670 | glColor4f(1, 1, 1, 1); |
671 | for (int i = 0; i < rects.size(); ++i) { |
672 | QRect rect = rects.at(i); |
673 | if (rect.isEmpty()) |
674 | continue; |
675 | |
676 | drawTexture(rect, d_ptr->tex_id, window()->size(), rect); |
677 | } |
678 | } |
679 | } |
680 | #endif |
681 | bool doingPartialUpdate = false; |
682 | if (d_ptr->swap_region_support) { |
683 | if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AutomaticSwap) |
684 | doingPartialUpdate = br.width() * br.height() < parent->geometry().width() * parent->geometry().height() * 0.2; |
685 | else if (QGLWindowSurface::swapBehavior == QGLWindowSurface::AlwaysPartialSwap) |
686 | doingPartialUpdate = true; |
687 | } |
688 | |
689 | QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext); |
690 | #ifdef Q_OS_SYMBIAN |
691 | if (!ctx) |
692 | return; |
693 | #endif |
694 | |
695 | if (widget != window()) { |
696 | if (initializeOffscreenTexture(window()->size())) |
697 | qWarning() << "QGLWindowSurface: Flushing to native child widget, may lead to significant performance loss" ; |
698 | glBindTexture(target, d_ptr->tex_id); |
699 | |
700 | const uint bottom = window()->height() - (br.y() + br.height()); |
701 | glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); |
702 | |
703 | glBindTexture(target, 0); |
704 | |
705 | ctx->makeCurrent(); |
706 | if (doingPartialUpdate) |
707 | blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), rect, br); |
708 | else |
709 | blitTexture(ctx, d_ptr->tex_id, parent->size(), window()->size(), parent->rect(), parent->rect().translated(offset + wOffset)); |
710 | } |
711 | |
712 | if (doingPartialUpdate) |
713 | ctx->d_func()->swapRegion(br); |
714 | else |
715 | ctx->swapBuffers(); |
716 | |
717 | d_ptr->paintedRegion = QRegion(); |
718 | } else { |
719 | glFlush(); |
720 | } |
721 | return; |
722 | } |
723 | |
724 | QGLContext *previous_ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
725 | QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext); |
726 | #ifdef Q_OS_SYMBIAN |
727 | if (!ctx) |
728 | return; |
729 | #endif |
730 | |
731 | // QPainter::end() should have unbound the fbo, otherwise something is very wrong... |
732 | Q_ASSERT(!d_ptr->fbo || !d_ptr->fbo->isBound()); |
733 | |
734 | if (ctx != previous_ctx) { |
735 | ctx->makeCurrent(); |
736 | } |
737 | |
738 | QSize size = widget->rect().size(); |
739 | if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) { |
740 | rect = parent->rect(); |
741 | br = rect.translated(wOffset + offset); |
742 | size = parent->size(); |
743 | } |
744 | |
745 | glDisable(GL_SCISSOR_TEST); |
746 | |
747 | if (d_ptr->fbo && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit)) { |
748 | const int h = d_ptr->fbo->height(); |
749 | |
750 | const int sx0 = br.left(); |
751 | const int sx1 = br.left() + br.width(); |
752 | const int sy0 = h - (br.top() + br.height()); |
753 | const int sy1 = h - br.top(); |
754 | |
755 | const int tx0 = rect.left(); |
756 | const int tx1 = rect.left() + rect.width(); |
757 | const int ty0 = parent->height() - (rect.top() + rect.height()); |
758 | const int ty1 = parent->height() - rect.top(); |
759 | |
760 | if (window() == parent || d_ptr->fbo->format().samples() <= 1) { |
761 | if (ctx->d_ptr->current_fbo != 0) |
762 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); |
763 | |
764 | glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle()); |
765 | |
766 | glBlitFramebufferEXT(sx0, sy0, sx1, sy1, |
767 | tx0, ty0, tx1, ty1, |
768 | GL_COLOR_BUFFER_BIT, |
769 | GL_NEAREST); |
770 | |
771 | glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); |
772 | } else { |
773 | #ifndef Q_OS_SYMBIAN // We don't have FBO pool on Symbian |
774 | // can't do sub-region blits with multisample FBOs |
775 | QGLFramebufferObject *temp = qgl_fbo_pool()->acquire(d_ptr->fbo->size(), QGLFramebufferObjectFormat()); |
776 | |
777 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, temp->handle()); |
778 | glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, d_ptr->fbo->handle()); |
779 | |
780 | glBlitFramebufferEXT(0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(), |
781 | 0, 0, d_ptr->fbo->width(), d_ptr->fbo->height(), |
782 | GL_COLOR_BUFFER_BIT, |
783 | GL_NEAREST); |
784 | |
785 | glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, temp->handle()); |
786 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); |
787 | |
788 | glBlitFramebufferEXT(sx0, sy0, sx1, sy1, |
789 | tx0, ty0, tx1, ty1, |
790 | GL_COLOR_BUFFER_BIT, |
791 | GL_NEAREST); |
792 | |
793 | glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, 0); |
794 | |
795 | qgl_fbo_pool()->release(temp); |
796 | #endif // Q_OS_SYMBIAN |
797 | } |
798 | |
799 | ctx->d_ptr->current_fbo = 0; |
800 | } |
801 | #if !defined(QT_OPENGL_ES_2) |
802 | else { |
803 | GLuint texture; |
804 | if (d_ptr->fbo) { |
805 | texture = d_ptr->fbo->texture(); |
806 | } else { |
807 | d_ptr->pb->makeCurrent(); |
808 | glBindTexture(target, d_ptr->pb_tex_id); |
809 | const uint bottom = window()->height() - (br.y() + br.height()); |
810 | glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); |
811 | texture = d_ptr->pb_tex_id; |
812 | glBindTexture(target, 0); |
813 | } |
814 | |
815 | glDisable(GL_DEPTH_TEST); |
816 | |
817 | if (d_ptr->fbo) { |
818 | d_ptr->fbo->release(); |
819 | } else { |
820 | ctx->makeCurrent(); |
821 | } |
822 | |
823 | glMatrixMode(GL_MODELVIEW); |
824 | glLoadIdentity(); |
825 | |
826 | glMatrixMode(GL_PROJECTION); |
827 | glLoadIdentity(); |
828 | #ifndef QT_OPENGL_ES |
829 | glOrtho(0, size.width(), size.height(), 0, -999999, 999999); |
830 | #else |
831 | glOrthof(0, size.width(), size.height(), 0, -999999, 999999); |
832 | #endif |
833 | glViewport(0, 0, size.width(), size.height()); |
834 | |
835 | glColor4f(1, 1, 1, 1); |
836 | drawTexture(rect, texture, window()->size(), br); |
837 | |
838 | if (d_ptr->fbo) |
839 | d_ptr->fbo->bind(); |
840 | } |
841 | #else |
842 | // OpenGL/ES 2.0 version of the fbo blit. |
843 | else if (d_ptr->fbo) { |
844 | Q_UNUSED(target); |
845 | |
846 | if (d_ptr->fbo->isBound()) |
847 | d_ptr->fbo->release(); |
848 | |
849 | blitTexture(ctx, d_ptr->fbo->texture(), size, window()->size(), rect, br); |
850 | } |
851 | #endif |
852 | |
853 | if (ctx->format().doubleBuffer()) |
854 | ctx->swapBuffers(); |
855 | else |
856 | glFlush(); |
857 | |
858 | d_ptr->did_paint = false; |
859 | } |
860 | |
861 | |
862 | #if !defined(Q_WS_QPA) |
863 | void QGLWindowSurface::setGeometry(const QRect &rect) |
864 | { |
865 | QWindowSurface::setGeometry(rect); |
866 | d_ptr->geometry_updated = true; |
867 | } |
868 | #else |
869 | void QGLWindowSurface::resize(const QSize &size) |
870 | { |
871 | QWindowSurface::resize(size); |
872 | d_ptr->geometry_updated = true; |
873 | } |
874 | #endif |
875 | |
876 | void QGLWindowSurface::updateGeometry() { |
877 | if (!d_ptr->geometry_updated) |
878 | return; |
879 | d_ptr->geometry_updated = false; |
880 | |
881 | bool hijack(true); |
882 | QWidgetPrivate *wd = window()->d_func(); |
883 | if (wd->extraData() && wd->extraData()->glContext) { |
884 | #ifdef Q_OS_SYMBIAN // Symbian needs to recreate the context when native window size changes |
885 | if (d_ptr->size != geometry().size()) { |
886 | QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext); |
887 | |
888 | if (ctx == QGLContext::currentContext()) |
889 | ctx->doneCurrent(); |
890 | |
891 | ctx->d_func()->destroyEglSurfaceForDevice(); |
892 | |
893 | // Delete other contexts (shouldn't happen too often, if at all) |
894 | while (d_ptr->contexts.size()) { |
895 | QGLContext **ctxPtrPtr = d_ptr->contexts.takeFirst(); |
896 | if ((*ctxPtrPtr) != ctx) |
897 | delete *ctxPtrPtr; |
898 | } |
899 | union { QGLContext **ctxPtrPtr; void **voidPtrPtr; }; |
900 | voidPtrPtr = &wd->extraData()->glContext; |
901 | d_ptr->contexts << ctxPtrPtr; |
902 | |
903 | ctx->d_func()->eglSurface = ctx->d_func()->eglContext->createSurface(window()); |
904 | |
905 | // Swap behaviour has been checked already in previous hijackWindow call. |
906 | // Reset swap behaviour based on that flag. |
907 | if (d_ptr->destructive_swap_buffers) { |
908 | eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(), |
909 | EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); |
910 | |
911 | if (eglGetError() != EGL_SUCCESS) |
912 | qWarning("QGLWindowSurface::updateGeometry() - could not re-enable destroyed swap behaviour" ); |
913 | } else { |
914 | eglSurfaceAttrib(QEgl::display(), ctx->d_func()->eglSurfaceForDevice(), |
915 | EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); |
916 | |
917 | if (eglGetError() != EGL_SUCCESS) |
918 | qWarning("QGLWindowSurface::updateGeometry() - could not re-enable preserved swap behaviour" ); |
919 | } |
920 | } |
921 | #endif |
922 | hijack = false; // we already have gl context for widget |
923 | } |
924 | |
925 | if (hijack) |
926 | hijackWindow(window()); |
927 | |
928 | QGLContext *ctx = reinterpret_cast<QGLContext *>(wd->extraData()->glContext); |
929 | #ifdef Q_OS_SYMBIAN |
930 | if (!ctx) |
931 | return; |
932 | #endif |
933 | #ifdef Q_WS_MAC |
934 | ctx->updatePaintDevice(); |
935 | #endif |
936 | |
937 | QSize surfSize = geometry().size(); |
938 | |
939 | if (surfSize.width() <= 0 || surfSize.height() <= 0) |
940 | return; |
941 | |
942 | if (d_ptr->size == surfSize) |
943 | return; |
944 | |
945 | d_ptr->size = surfSize; |
946 | |
947 | if (d_ptr->ctx) { |
948 | #ifndef QT_OPENGL_ES_2 |
949 | if (d_ptr->destructive_swap_buffers) |
950 | initializeOffscreenTexture(surfSize); |
951 | #endif |
952 | return; |
953 | } |
954 | |
955 | const GLenum target = GL_TEXTURE_2D; |
956 | if (d_ptr->destructive_swap_buffers |
957 | && (QGLExtensions::glExtensions() & QGLExtensions::FramebufferObject) |
958 | && (d_ptr->fbo || !d_ptr->tried_fbo) |
959 | && qt_gl_preferGL2Engine()) |
960 | { |
961 | d_ptr->tried_fbo = true; |
962 | ctx->d_ptr->internal_context = true; |
963 | ctx->makeCurrent(); |
964 | delete d_ptr->fbo; |
965 | |
966 | QGLFramebufferObjectFormat format; |
967 | format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); |
968 | format.setInternalTextureFormat(GLenum(GL_RGBA)); |
969 | format.setTextureTarget(target); |
970 | |
971 | if (QGLExtensions::glExtensions() & QGLExtensions::FramebufferBlit) |
972 | format.setSamples(8); |
973 | |
974 | d_ptr->fbo = new QGLFramebufferObject(surfSize, format); |
975 | |
976 | if (d_ptr->fbo->isValid()) { |
977 | qDebug() << "Created Window Surface FBO" << surfSize |
978 | << "with samples" << d_ptr->fbo->format().samples(); |
979 | return; |
980 | } else { |
981 | qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back" ; |
982 | delete d_ptr->fbo; |
983 | d_ptr->fbo = 0; |
984 | } |
985 | } |
986 | |
987 | #if !defined(QT_OPENGL_ES_2) && !defined(Q_WS_QPA) //QPA doesn't support pixelbuffers |
988 | if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) { |
989 | d_ptr->tried_pb = true; |
990 | |
991 | if (d_ptr->pb) { |
992 | d_ptr->pb->makeCurrent(); |
993 | glDeleteTextures(1, &d_ptr->pb_tex_id); |
994 | } |
995 | |
996 | delete d_ptr->pb; |
997 | |
998 | d_ptr->pb = new QGLPixelBuffer(surfSize.width(), surfSize.height(), |
999 | QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer), |
1000 | qt_gl_share_widget()); |
1001 | |
1002 | if (d_ptr->pb->isValid()) { |
1003 | qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers(); |
1004 | d_ptr->pb->makeCurrent(); |
1005 | |
1006 | glGenTextures(1, &d_ptr->pb_tex_id); |
1007 | glBindTexture(target, d_ptr->pb_tex_id); |
1008 | glTexImage2D(target, 0, GL_RGBA, surfSize.width(), surfSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
1009 | |
1010 | glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
1011 | glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
1012 | glBindTexture(target, 0); |
1013 | |
1014 | glMatrixMode(GL_PROJECTION); |
1015 | glLoadIdentity(); |
1016 | glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999); |
1017 | |
1018 | d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true; |
1019 | return; |
1020 | } else { |
1021 | qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back" ; |
1022 | delete d_ptr->pb; |
1023 | d_ptr->pb = 0; |
1024 | } |
1025 | } |
1026 | #endif // !defined(QT_OPENGL_ES_2) !defined(Q_WS_QPA) |
1027 | |
1028 | ctx->makeCurrent(); |
1029 | |
1030 | #ifndef QT_OPENGL_ES_2 |
1031 | if (d_ptr->destructive_swap_buffers) |
1032 | initializeOffscreenTexture(surfSize); |
1033 | #endif |
1034 | #ifndef Q_OS_SYMBIAN |
1035 | qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this; |
1036 | #endif |
1037 | d_ptr->ctx = ctx; |
1038 | d_ptr->ctx->d_ptr->internal_context = true; |
1039 | } |
1040 | |
1041 | bool QGLWindowSurface::initializeOffscreenTexture(const QSize &size) |
1042 | { |
1043 | if (size == d_ptr->textureSize) |
1044 | return false; |
1045 | |
1046 | glGenTextures(1, &d_ptr->tex_id); |
1047 | glBindTexture(GL_TEXTURE_2D, d_ptr->tex_id); |
1048 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); |
1049 | |
1050 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
1051 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
1052 | glBindTexture(GL_TEXTURE_2D, 0); |
1053 | |
1054 | d_ptr->textureSize = size; |
1055 | return true; |
1056 | } |
1057 | |
1058 | bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy) |
1059 | { |
1060 | // this code randomly fails currently for unknown reasons |
1061 | return false; |
1062 | |
1063 | if (!d_ptr->pb) |
1064 | return false; |
1065 | |
1066 | d_ptr->pb->makeCurrent(); |
1067 | |
1068 | QRect br = area.boundingRect(); |
1069 | |
1070 | #if 0 |
1071 | // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason) |
1072 | // ## maybe we should use glCopyTexSubImage insteadk |
1073 | if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2) |
1074 | return false; |
1075 | |
1076 | glRasterPos2i(br.x() + dx, br.y() + br.height() + dy); |
1077 | glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR); |
1078 | return true; |
1079 | #endif |
1080 | |
1081 | const GLenum target = GL_TEXTURE_2D; |
1082 | |
1083 | glBindTexture(target, d_ptr->tex_id); |
1084 | glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0); |
1085 | glBindTexture(target, 0); |
1086 | |
1087 | drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size()); |
1088 | |
1089 | return true; |
1090 | } |
1091 | |
1092 | static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br) |
1093 | { |
1094 | const GLenum target = GL_TEXTURE_2D; |
1095 | QRectF src = br.isEmpty() |
1096 | ? QRectF(QPointF(), texSize) |
1097 | : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size()); |
1098 | |
1099 | if (target == GL_TEXTURE_2D) { |
1100 | qreal width = texSize.width(); |
1101 | qreal height = texSize.height(); |
1102 | |
1103 | src.setLeft(src.left() / width); |
1104 | src.setRight(src.right() / width); |
1105 | src.setTop(src.top() / height); |
1106 | src.setBottom(src.bottom() / height); |
1107 | } |
1108 | |
1109 | const GLfloat tx1 = src.left(); |
1110 | const GLfloat tx2 = src.right(); |
1111 | const GLfloat ty1 = src.top(); |
1112 | const GLfloat ty2 = src.bottom(); |
1113 | |
1114 | GLfloat texCoordArray[4*2] = { |
1115 | tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1 |
1116 | }; |
1117 | |
1118 | GLfloat vertexArray[4*2]; |
1119 | extern void qt_add_rect_to_array(const QRectF &r, GLfloat *array); // qpaintengine_opengl.cpp |
1120 | qt_add_rect_to_array(rect, vertexArray); |
1121 | |
1122 | #if !defined(QT_OPENGL_ES_2) |
1123 | glVertexPointer(2, GL_FLOAT, 0, vertexArray); |
1124 | glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray); |
1125 | |
1126 | glBindTexture(target, tex_id); |
1127 | glEnable(target); |
1128 | |
1129 | glEnableClientState(GL_VERTEX_ARRAY); |
1130 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
1131 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
1132 | glDisableClientState(GL_VERTEX_ARRAY); |
1133 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
1134 | |
1135 | glDisable(target); |
1136 | glBindTexture(target, 0); |
1137 | #else |
1138 | glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray); |
1139 | glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, texCoordArray); |
1140 | |
1141 | glBindTexture(target, tex_id); |
1142 | |
1143 | glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); |
1144 | glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); |
1145 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
1146 | glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); |
1147 | glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); |
1148 | |
1149 | glBindTexture(target, 0); |
1150 | #endif |
1151 | } |
1152 | |
1153 | QImage *QGLWindowSurface::buffer(const QWidget *widget) |
1154 | { |
1155 | QImage image; |
1156 | |
1157 | if (d_ptr->pb) |
1158 | image = d_ptr->pb->toImage(); |
1159 | else if (d_ptr->fbo) |
1160 | image = d_ptr->fbo->toImage(); |
1161 | |
1162 | if (image.isNull()) |
1163 | return 0; |
1164 | |
1165 | QRect rect = widget->rect(); |
1166 | rect.translate(widget->mapTo(widget->window(), QPoint())); |
1167 | |
1168 | QImage subImage = image.copy(rect); |
1169 | d_ptr->buffers << subImage; |
1170 | return &d_ptr->buffers.last(); |
1171 | } |
1172 | |
1173 | QWindowSurface::WindowSurfaceFeatures QGLWindowSurface::features() const |
1174 | { |
1175 | WindowSurfaceFeatures features = 0; |
1176 | if (!d_ptr->destructive_swap_buffers || d_ptr->swap_region_support) |
1177 | features |= PartialUpdates; |
1178 | if (!d_ptr->destructive_swap_buffers) |
1179 | features |= PreservedContents; |
1180 | return features; |
1181 | } |
1182 | |
1183 | QT_END_NAMESPACE |
1184 | |
1185 | |