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

Last change on this file since 158 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 19.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtOpenGL module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 "qdebug.h"
53
54#ifdef Q_WS_X11
55#include <private/qt_x11_p.h>
56#include <qx11info_x11.h>
57#include <private/qwidget_p.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#include <private/qpaintengine_opengl_p.h>
73
74#ifndef GLX_ARB_multisample
75#define GLX_SAMPLE_BUFFERS_ARB 100000
76#define GLX_SAMPLES_ARB 100001
77#endif
78
79QT_BEGIN_NAMESPACE
80
81//
82// QGLGraphicsSystem
83//
84#ifdef Q_WS_WIN
85Q_GUI_EXPORT bool qt_win_owndc_required;
86#endif
87QGLGraphicsSystem::QGLGraphicsSystem()
88 : QGraphicsSystem()
89{
90#if defined(Q_WS_X11) && !defined(QT_OPENGL_ES)
91 // only override the system defaults if the user hasn't already
92 // picked a visual
93 if (X11->visual == 0 && X11->visual_id == -1 && X11->visual_class == -1) {
94 // find a double buffered, RGBA visual that supports OpenGL
95 // and set that as the default visual for windows in Qt
96 int i = 0;
97 int spec[16];
98 spec[i++] = GLX_RGBA;
99#if 0
100 spec[i++] = GLX_DOUBLEBUFFER;
101 spec[i++] = GLX_DEPTH_SIZE;
102 spec[i++] = 8;
103 spec[i++] = GLX_STENCIL_SIZE;
104 spec[i++] = 8;
105 spec[i++] = GLX_SAMPLE_BUFFERS_ARB;
106 spec[i++] = 1;
107 spec[i++] = GLX_SAMPLES_ARB;
108 spec[i++] = 4;
109#endif
110 spec[i++] = XNone;
111
112 XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec);
113 if (vi) {
114 X11->visual_id = vi->visualid;
115 X11->visual_class = vi->c_class;
116
117 QGLFormat format;
118 int res;
119 glXGetConfig(X11->display, vi, GLX_LEVEL, &res);
120 format.setPlane(res);
121 glXGetConfig(X11->display, vi, GLX_DOUBLEBUFFER, &res);
122 format.setDoubleBuffer(res);
123 glXGetConfig(X11->display, vi, GLX_DEPTH_SIZE, &res);
124 format.setDepth(res);
125 if (format.depth())
126 format.setDepthBufferSize(res);
127 glXGetConfig(X11->display, vi, GLX_RGBA, &res);
128 format.setRgba(res);
129 glXGetConfig(X11->display, vi, GLX_RED_SIZE, &res);
130 format.setRedBufferSize(res);
131 glXGetConfig(X11->display, vi, GLX_GREEN_SIZE, &res);
132 format.setGreenBufferSize(res);
133 glXGetConfig(X11->display, vi, GLX_BLUE_SIZE, &res);
134 format.setBlueBufferSize(res);
135 glXGetConfig(X11->display, vi, GLX_ALPHA_SIZE, &res);
136 format.setAlpha(res);
137 if (format.alpha())
138 format.setAlphaBufferSize(res);
139 glXGetConfig(X11->display, vi, GLX_ACCUM_RED_SIZE, &res);
140 format.setAccum(res);
141 if (format.accum())
142 format.setAccumBufferSize(res);
143 glXGetConfig(X11->display, vi, GLX_STENCIL_SIZE, &res);
144 format.setStencil(res);
145 if (format.stencil())
146 format.setStencilBufferSize(res);
147 glXGetConfig(X11->display, vi, GLX_STEREO, &res);
148 format.setStereo(res);
149 glXGetConfig(X11->display, vi, GLX_SAMPLE_BUFFERS_ARB, &res);
150 format.setSampleBuffers(res);
151 if (format.sampleBuffers()) {
152 glXGetConfig(X11->display, vi, GLX_SAMPLES_ARB, &res);
153 format.setSamples(res);
154 }
155
156 QGLWindowSurface::surfaceFormat = format;
157 XFree(vi);
158
159 printf("using visual class %x, id %x\n", X11->visual_class, X11->visual_id);
160 }
161 }
162#elif defined(Q_WS_WIN)
163 QGLWindowSurface::surfaceFormat.setDoubleBuffer(false);
164
165 qt_win_owndc_required = true;
166#endif
167}
168
169//
170// QGLWindowSurface
171//
172
173class QGLGlobalShareWidget
174{
175public:
176 QGLGlobalShareWidget() : widget(0) {}
177
178 QGLWidget *shareWidget() {
179 if (!widget && !cleanedUp) {
180 widget = new QGLWidget;
181 }
182 return widget;
183 }
184
185 void cleanup() {
186 delete widget;
187 widget = 0;
188 cleanedUp = true;
189 }
190
191 static bool cleanedUp;
192
193private:
194 QGLWidget *widget;
195};
196
197bool QGLGlobalShareWidget::cleanedUp = false;
198
199static void qt_cleanup_gl_share_widget();
200Q_GLOBAL_STATIC_WITH_INITIALIZER(QGLGlobalShareWidget, _qt_gl_share_widget,
201 {
202 qAddPostRoutine(qt_cleanup_gl_share_widget);
203 })
204
205static void qt_cleanup_gl_share_widget()
206{
207 _qt_gl_share_widget()->cleanup();
208}
209
210QGLWidget* qt_gl_share_widget()
211{
212 if (QGLGlobalShareWidget::cleanedUp)
213 return 0;
214 return _qt_gl_share_widget()->shareWidget();
215}
216
217struct QGLWindowSurfacePrivate
218{
219 QGLFramebufferObject *fbo;
220 QGLPixelBuffer *pb;
221 GLuint tex_id;
222 GLuint pb_tex_id;
223
224 int tried_fbo : 1;
225 int tried_pb : 1;
226
227 QGLContext *ctx;
228
229 QList<QGLContext **> contexts;
230
231 QRegion paintedRegion;
232 QSize size;
233
234 QList<QImage> buffers;
235};
236
237QGLFormat QGLWindowSurface::surfaceFormat;
238
239QGLWindowSurface::QGLWindowSurface(QWidget *window)
240 : QWindowSurface(window), d_ptr(new QGLWindowSurfacePrivate)
241{
242 Q_ASSERT(window->isTopLevel());
243 QGLExtensions::init();
244 d_ptr->pb = 0;
245 d_ptr->fbo = 0;
246 d_ptr->ctx = 0;
247 d_ptr->tried_fbo = false;
248 d_ptr->tried_pb = false;
249}
250
251QGLWindowSurface::~QGLWindowSurface()
252{
253 if (d_ptr->ctx)
254 glDeleteTextures(1, &d_ptr->tex_id);
255 foreach(QGLContext **ctx, d_ptr->contexts) {
256 delete *ctx;
257 *ctx = 0;
258 }
259
260 delete d_ptr->pb;
261 delete d_ptr->fbo;
262 delete d_ptr;
263}
264
265void QGLWindowSurface::hijackWindow(QWidget *widget)
266{
267 QWidgetPrivate *widgetPrivate = widget->d_func();
268 widgetPrivate->createExtra();
269 if (widgetPrivate->extraData()->glContext)
270 return;
271
272 QGLContext *ctx = new QGLContext(surfaceFormat, widget);
273 ctx->create(qt_gl_share_widget()->context());
274#ifdef Q_WS_MAC
275 ctx->updatePaintDevice();
276#endif
277 widgetPrivate->extraData()->glContext = ctx;
278
279 union { QGLContext **ctxPtr; void **voidPtr; };
280
281 voidPtr = &widgetPrivate->extraData()->glContext;
282 d_ptr->contexts << ctxPtr;
283 qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size();
284}
285
286#if !defined(QT_OPENGL_ES_2)
287Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_window_surface_paintengine)
288#endif
289
290/*! \reimp */
291QPaintEngine *QGLWindowSurface::paintEngine() const
292{
293#if !defined(QT_OPENGL_ES_2)
294 return qt_gl_window_surface_paintengine();
295#else
296 return 0;
297#endif
298}
299
300int QGLWindowSurface::metric(PaintDeviceMetric m) const
301{
302 return window()->metric(m);
303}
304
305QGLContext *QGLWindowSurface::context() const
306{
307 return d_ptr->ctx;
308}
309
310QPaintDevice *QGLWindowSurface::paintDevice()
311{
312 if (d_ptr->pb)
313 return d_ptr->pb;
314
315 if (d_ptr->ctx)
316 return this;
317
318 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
319 ctx->makeCurrent();
320 return d_ptr->fbo;
321}
322
323static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &src = QRectF());
324
325void QGLWindowSurface::beginPaint(const QRegion &)
326{
327}
328
329void QGLWindowSurface::endPaint(const QRegion &rgn)
330{
331 if (context())
332 d_ptr->paintedRegion |= rgn;
333
334 d_ptr->buffers.clear();
335}
336
337void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
338{
339 QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget();
340 Q_ASSERT(parent);
341
342 hijackWindow(parent);
343
344 QRect br = rgn.boundingRect().translated(offset);
345 br = br.intersected(window()->rect());
346 QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft();
347 QRect rect = br.translated(-offset - wOffset);
348
349 const GLenum target = qt_gl_preferredTextureTarget();
350
351 if (context()) {
352 context()->makeCurrent();
353
354 if (context()->format().doubleBuffer()) {
355 glBindTexture(target, d_ptr->tex_id);
356
357 QVector<QRect> rects = d_ptr->paintedRegion.rects();
358 for (int i = 0; i < rects.size(); ++i) {
359 QRect br = rects.at(i);
360 if (br.isEmpty())
361 continue;
362
363 const uint bottom = window()->height() - (br.y() + br.height());
364 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
365 }
366
367 glBindTexture(target, 0);
368
369 QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion;
370
371 if (!dirtyRegion.isEmpty()) {
372 context()->makeCurrent();
373
374 glMatrixMode(GL_MODELVIEW);
375 glLoadIdentity();
376
377 glMatrixMode(GL_PROJECTION);
378 glLoadIdentity();
379#ifndef QT_OPENGL_ES
380 glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999);
381#else
382 glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999);
383#endif
384 glViewport(0, 0, window()->width(), window()->height());
385
386 QVector<QRect> rects = dirtyRegion.rects();
387 glColor4f(1, 1, 1, 1);
388 for (int i = 0; i < rects.size(); ++i) {
389 QRect rect = rects.at(i);
390 if (rect.isEmpty())
391 continue;
392
393 drawTexture(rect, d_ptr->tex_id, window()->size(), rect);
394 }
395 }
396 d_ptr->paintedRegion = QRegion();
397
398 context()->swapBuffers();
399 } else {
400 glFlush();
401 }
402
403 return;
404 }
405
406 QGLContext *ctx = reinterpret_cast<QGLContext *>(parent->d_func()->extraData()->glContext);
407 GLuint texture;
408 if (d_ptr->fbo) {
409 texture = d_ptr->fbo->texture();
410 } else {
411 d_ptr->pb->makeCurrent();
412 glBindTexture(target, d_ptr->pb_tex_id);
413 const uint bottom = window()->height() - (br.y() + br.height());
414 glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height());
415 texture = d_ptr->pb_tex_id;
416 glBindTexture(target, 0);
417 }
418
419 QSize size = widget->rect().size();
420 if (ctx->format().doubleBuffer()) {
421 rect = parent->rect();
422 br = rect.translated(wOffset);
423 size = parent->size();
424 }
425
426 ctx->makeCurrent();
427#ifdef Q_WS_MAC
428 ctx->updatePaintDevice();
429#endif
430 if (d_ptr->fbo)
431 d_ptr->fbo->release();
432
433 glMatrixMode(GL_MODELVIEW);
434 glLoadIdentity();
435
436 glMatrixMode(GL_PROJECTION);
437 glLoadIdentity();
438#ifndef QT_OPENGL_ES
439 glOrtho(0, size.width(), size.height(), 0, -999999, 999999);
440#else
441 glOrthof(0, size.width(), size.height(), 0, -999999, 999999);
442#endif
443 glViewport(0, 0, size.width(), size.height());
444
445 glColor4f(1, 1, 1, 1);
446 drawTexture(rect, texture, window()->size(), br);
447
448 if (ctx->format().doubleBuffer())
449 ctx->swapBuffers();
450 else
451 glFlush();
452
453 if (d_ptr->fbo)
454 d_ptr->fbo->bind();
455}
456
457void QGLWindowSurface::setGeometry(const QRect &rect)
458{
459 QWindowSurface::setGeometry(rect);
460
461 const GLenum target = qt_gl_preferredTextureTarget();
462
463 if (rect.width() <= 0 || rect.height() <= 0)
464 return;
465
466 if (d_ptr->size == rect.size())
467 return;
468
469 d_ptr->size = rect.size();
470
471 if (d_ptr->ctx) {
472 glBindTexture(target, d_ptr->tex_id);
473 glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
474 glBindTexture(target, 0);
475 return;
476 }
477
478 if (d_ptr->pb || !d_ptr->tried_pb) {
479 d_ptr->tried_pb = true;
480
481 if (d_ptr->pb) {
482 d_ptr->pb->makeCurrent();
483 glDeleteTextures(1, &d_ptr->pb_tex_id);
484 }
485
486 delete d_ptr->pb;
487
488 d_ptr->pb = new QGLPixelBuffer(rect.width(), rect.height(),
489 QGLFormat(QGL::SampleBuffers | QGL::StencilBuffer | QGL::DepthBuffer),
490 qt_gl_share_widget());
491
492 if (d_ptr->pb->isValid()) {
493 qDebug() << "PB Sample buffers:" << d_ptr->pb->format().sampleBuffers();
494 d_ptr->pb->makeCurrent();
495
496 glGenTextures(1, &d_ptr->pb_tex_id);
497 glBindTexture(target, d_ptr->pb_tex_id);
498 glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
499
500 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
501 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
502 glBindTexture(target, 0);
503
504 glMatrixMode(GL_PROJECTION);
505 glLoadIdentity();
506#ifndef QT_OPENGL_ES
507 glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
508#else
509 glOrthof(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999);
510#endif
511
512 d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true;
513 return;
514 } else {
515 qDebug() << "QGLWindowSurface: Failed to create valid pixelbuffer, falling back";
516 delete d_ptr->pb;
517 d_ptr->pb = 0;
518 }
519 }
520
521 if ((QGLExtensions::glExtensions & QGLExtensions::FramebufferObject) && (d_ptr->fbo || !d_ptr->tried_fbo)) {
522 d_ptr->tried_fbo = true;
523 hijackWindow(window());
524 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
525 ctx->d_ptr->internal_context = true;
526 ctx->makeCurrent();
527 delete d_ptr->fbo;
528 d_ptr->fbo = new QGLFramebufferObject(rect.size(), QGLFramebufferObject::CombinedDepthStencil,
529 GLenum(target), GLenum(GL_RGBA));
530
531 d_ptr->fbo->bind();
532 if (d_ptr->fbo->isValid()) {
533 return;
534 } else {
535 qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back";
536 delete d_ptr->fbo;
537 d_ptr->fbo = 0;
538 }
539 }
540
541 hijackWindow(window());
542 QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext);
543 ctx->makeCurrent();
544
545 glGenTextures(1, &d_ptr->tex_id);
546 glBindTexture(target, d_ptr->tex_id);
547 glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
548
549 glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
550 glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
551 glBindTexture(target, 0);
552
553 qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;;
554 d_ptr->ctx = ctx;
555 d_ptr->ctx->d_ptr->internal_context = true;
556}
557
558bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy)
559{
560 // this code randomly fails currently for unknown reasons
561 return false;
562
563 if (!d_ptr->pb)
564 return false;
565
566 d_ptr->pb->makeCurrent();
567
568 QRect br = area.boundingRect();
569
570#if 0
571 // ## workaround driver issue (scrolling by these deltas is unbearably slow for some reason)
572 // ## maybe we should use glCopyTexSubImage insteadk
573 if (dx == 1 || dx == -1 || dy == 1 || dy == -1 || dy == 2)
574 return false;
575
576 glRasterPos2i(br.x() + dx, br.y() + br.height() + dy);
577 glCopyPixels(br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), GL_COLOR);
578 return true;
579#endif
580
581 const GLenum target = qt_gl_preferredTextureTarget();
582
583 glBindTexture(target, d_ptr->tex_id);
584 glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0);
585 glBindTexture(target, 0);
586
587 drawTexture(br.translated(dx, dy), d_ptr->tex_id, window()->size());
588
589 return true;
590}
591
592static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br)
593{
594 const GLenum target = qt_gl_preferredTextureTarget();
595 QRectF src = br.isEmpty()
596 ? QRectF(QPointF(), texSize)
597 : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size());
598
599 if (target == GL_TEXTURE_2D) {
600 qreal width = texSize.width();
601 qreal height = texSize.height();
602
603 src.setLeft(src.left() / width);
604 src.setRight(src.right() / width);
605 src.setTop(src.top() / height);
606 src.setBottom(src.bottom() / height);
607 }
608
609 const q_vertexType tx1 = f2vt(src.left());
610 const q_vertexType tx2 = f2vt(src.right());
611 const q_vertexType ty1 = f2vt(src.top());
612 const q_vertexType ty2 = f2vt(src.bottom());
613
614 q_vertexType texCoordArray[4*2] = {
615 tx1, ty2, tx2, ty2, tx2, ty1, tx1, ty1
616 };
617
618 q_vertexType vertexArray[4*2];
619 extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); // qpaintengine_opengl.cpp
620 qt_add_rect_to_array(rect, vertexArray);
621
622 glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray);
623 glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray);
624
625 glBindTexture(target, tex_id);
626 glEnable(target);
627
628 glEnableClientState(GL_VERTEX_ARRAY);
629 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
630 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
631 glDisableClientState(GL_VERTEX_ARRAY);
632 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
633
634 glDisable(target);
635 glBindTexture(target, 0);
636}
637
638QImage *QGLWindowSurface::buffer(const QWidget *widget)
639{
640 QImage image;
641
642 if (d_ptr->pb)
643 image = d_ptr->pb->toImage();
644 else if (d_ptr->fbo)
645 image = d_ptr->fbo->toImage();
646
647 if (image.isNull())
648 return 0;
649
650 QRect rect = widget->rect();
651 rect.translate(widget->mapTo(widget->window(), QPoint()));
652
653 QImage subImage = image.copy(rect);
654 d_ptr->buffers << subImage;
655 return &d_ptr->buffers.last();
656}
657
658
659
660QT_END_NAMESPACE
661
Note: See TracBrowser for help on using the repository browser.