Added link to the OpenGL window example from the QOpenGLContext docs.
[profile/ivi/qtbase.git] / src / gui / kernel / qopenglcontext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <qpa/qplatformopenglcontext.h>
43 #include <qpa/qplatformintegration.h>
44 #include "qopenglcontext.h"
45 #include "qopenglcontext_p.h"
46 #include "qwindow.h"
47
48 #include <QtCore/QThreadStorage>
49 #include <QtCore/QThread>
50
51 #include <QtGui/private/qguiapplication_p.h>
52 #include <QtGui/private/qopengl_p.h>
53 #include <QtGui/private/qwindow_p.h>
54 #include <QtGui/QScreen>
55
56 #include <private/qopenglextensions_p.h>
57
58 #include <QDebug>
59
60 QT_BEGIN_NAMESPACE
61
62 class QGuiGLThreadContext
63 {
64 public:
65     ~QGuiGLThreadContext() {
66         if (context)
67             context->doneCurrent();
68     }
69     QOpenGLContext *context;
70 };
71
72 static QThreadStorage<QGuiGLThreadContext *> qwindow_context_storage;
73
74 #ifndef QT_NO_DEBUG
75 QHash<QOpenGLContext *, bool> QOpenGLContextPrivate::makeCurrentTracker;
76 QMutex QOpenGLContextPrivate::makeCurrentTrackerMutex;
77 #endif
78
79 /*!
80     \class QOpenGLContext
81     \inmodule QtGui
82     \since 5.0
83     \brief The QOpenGLContext class represents a native OpenGL context, enabling
84            OpenGL rendering on a QSurface.
85
86     QOpenGLContext represents the OpenGL state of an underlying OpenGL context.
87     To set up a context, set its screen and format such that they match those
88     of the surface or surfaces with which the context is meant to be used, if
89     necessary make it share resources with other contexts with
90     setShareContext(), and finally call create(). Use isValid() to check if the
91     context was successfully initialized.
92
93     A context can be made current against a given surface by calling
94     makeCurrent(). When OpenGL rendering is done, call swapBuffers() to swap
95     the front and back buffers of the surface, so that the newly rendered
96     content becomes visible. To be able to support certain platforms,
97     QOpenGLContext requires that you call makeCurrent() again before starting
98     rendering a new frame, after calling swapBuffers().
99
100     If the context is temporarily not needed, such as when the application is
101     not rendering, it can be useful to call destroy() to free resources.
102     However, if you do so you will need to call create() again before the
103     context can be used, and you might need to recreate any OpenGL resources
104     and reinitialize the OpenGL state. You can connect to the
105     aboutToBeDestroyed() signal to clean up any resources that have been
106     allocated with different ownership from the QOpenGLContext itself.
107
108     Once a QOpenGLContext has been made current, you can render to it in a
109     platform independent way by using Qt's OpenGL enablers such as
110     QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, and
111     QOpenGLFramebufferObject. It is also possible to use the platform's OpenGL
112     API directly, without using the Qt enablers, although potentially at the
113     cost of portability. The latter is necessary when wanting to use OpenGL 1.x
114     or OpenGL ES 1.x.
115
116     For more information about the OpenGL API, refer to the official
117     \l{OpenGL documentation}.
118
119     For an example of how to use QOpenGLContext see the
120     \l{gui/openglwindow}{OpenGL Window} example.
121
122     \section1 Thread affinity
123
124     QOpenGLContext can be moved to a different thread with moveToThread(). Do
125     not call makeCurrent() from a different thread than the one to which the
126     QOpenGLContext object belongs. A context can only be current in one thread
127     and against one surface at a time, and a thread only has one context
128     current at a time.
129
130     \section1 Context resource sharing
131
132     Resources, such as framebuffer objects, textures, and vertex buffer objects
133     can be shared between contexts.  Use setShareContext() before calling
134     create() to specify that the contexts should share these resources.
135     QOpenGLContext internally keeps track of a QOpenGLContextGroup object which
136     can be accessed with shareGroup(), and which can be used to find all the
137     contexts in a given share group. A share group consists of all contexts that
138     have been successfully initialized and are sharing with an existing context in
139     the share group. A non-sharing context has a share group consisting of a
140     single context.
141
142     \section1 Default framebuffer
143
144     On certain platforms, a framebuffer other than 0 might be the default frame
145     buffer depending on the current surface. Instead of calling
146     glBindFramebuffer(0), it is recommended that you use
147     glBindFramebuffer(ctx->defaultFramebufferObject()), to ensure that your
148     application is portable between different platforms. However, if you use
149     QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you.
150
151     \sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject
152 */
153
154 void QOpenGLContextPrivate::setCurrentContext(QOpenGLContext *context)
155 {
156     QGuiGLThreadContext *threadContext = qwindow_context_storage.localData();
157     if (!threadContext) {
158         if (!QThread::currentThread()) {
159             qWarning("No QTLS available. currentContext wont work");
160             return;
161         }
162         threadContext = new QGuiGLThreadContext;
163         qwindow_context_storage.setLocalData(threadContext);
164     }
165     threadContext->context = context;
166 }
167
168 int QOpenGLContextPrivate::maxTextureSize()
169 {
170     if (max_texture_size != -1)
171         return max_texture_size;
172
173     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
174
175 #if defined(QT_OPENGL_ES)
176     return max_texture_size;
177 #else
178     GLenum proxy = GL_PROXY_TEXTURE_2D;
179
180     GLint size;
181     GLint next = 64;
182     glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
183     glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size);
184     if (size == 0) {
185         return max_texture_size;
186     }
187     do {
188         size = next;
189         next = size * 2;
190
191         if (next > max_texture_size)
192             break;
193         glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
194         glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next);
195     } while (next > size);
196
197     max_texture_size = size;
198     return max_texture_size;
199 #endif
200 }
201
202 /*!
203     Returns the last context which called makeCurrent in the current thread,
204     or 0, if no context is current.
205 */
206 QOpenGLContext* QOpenGLContext::currentContext()
207 {
208     QGuiGLThreadContext *threadContext = qwindow_context_storage.localData();
209     if(threadContext) {
210         return threadContext->context;
211     }
212     return 0;
213 }
214
215 /*!
216     Returns true if the \a first and \a second contexts are sharing OpenGL resources.
217 */
218 bool QOpenGLContext::areSharing(QOpenGLContext *first, QOpenGLContext *second)
219 {
220     return first->shareGroup() == second->shareGroup();
221 }
222
223 /*!
224     Returns the underlying platform context.
225
226     \internal
227 */
228 QPlatformOpenGLContext *QOpenGLContext::handle() const
229 {
230     Q_D(const QOpenGLContext);
231     return d->platformGLContext;
232 }
233
234 /*!
235     Returns the underlying platform context with which this context is sharing.
236
237     \internal
238 */
239
240 QPlatformOpenGLContext *QOpenGLContext::shareHandle() const
241 {
242     Q_D(const QOpenGLContext);
243     if (d->shareContext)
244         return d->shareContext->handle();
245     return 0;
246 }
247
248 /*!
249     Creates a new OpenGL context instance with parent object \a parent.
250
251     Before it can be used you need to set the proper format and call create().
252
253     \sa create(), makeCurrent()
254 */
255 QOpenGLContext::QOpenGLContext(QObject *parent)
256     : QObject(*new QOpenGLContextPrivate(), parent)
257 {
258     Q_D(QOpenGLContext);
259     d->screen = QGuiApplication::primaryScreen();
260 }
261
262 /*!
263     Sets the \a format the OpenGL context should be compatible with. You need
264     to call create() before it takes effect.
265 */
266 void QOpenGLContext::setFormat(const QSurfaceFormat &format)
267 {
268     Q_D(QOpenGLContext);
269     d->requestedFormat = format;
270 }
271
272 /*!
273     Makes this context share textures, shaders, and other OpenGL resources
274     with \a shareContext. You need to call create() before it takes effect.
275 */
276 void QOpenGLContext::setShareContext(QOpenGLContext *shareContext)
277 {
278     Q_D(QOpenGLContext);
279     d->shareContext = shareContext;
280 }
281
282 /*!
283     Sets the \a screen the OpenGL context should be valid for. You need to call
284     create() before it takes effect.
285 */
286 void QOpenGLContext::setScreen(QScreen *screen)
287 {
288     Q_D(QOpenGLContext);
289     d->screen = screen;
290     if (!d->screen)
291         d->screen = QGuiApplication::primaryScreen();
292 }
293
294 /*!
295     Attempts to create the OpenGL context with the current configuration.
296
297     The current configuration includes the format, the share context, and the
298     screen.
299
300     Returns true if the native context was successfully created and is ready to
301     be used with makeCurrent(), swapBuffers(), etc.
302
303     \sa makeCurrent(), destroy()
304 */
305 bool QOpenGLContext::create()
306 {
307     destroy();
308
309     Q_D(QOpenGLContext);
310     d->platformGLContext = QGuiApplicationPrivate::platformIntegration()->createPlatformOpenGLContext(this);
311     if (!d->platformGLContext)
312         return false;
313     d->platformGLContext->setContext(this);
314     if (!d->platformGLContext->isSharing())
315         d->shareContext = 0;
316     d->shareGroup = d->shareContext ? d->shareContext->shareGroup() : new QOpenGLContextGroup;
317     d->shareGroup->d_func()->addContext(this);
318     return d->platformGLContext;
319 }
320
321 /*!
322     Destroy the underlying platform context associated with this context.
323
324     If any other context is directly or indirectly sharing resources with this
325     context, the shared resources, which includes vertex buffer objects, shader
326     objects, textures, and framebuffer objects, are not freed. However,
327     destroying the underlying platform context frees any state associated with
328     the context.
329
330     After destroy() has been called, you must call create() if you wish to
331     use the context again.
332
333     \note This implicitly calls doneCurrent() if the context is current.
334
335     \sa create()
336 */
337 void QOpenGLContext::destroy()
338 {
339     Q_D(QOpenGLContext);
340     if (d->platformGLContext)
341         emit aboutToBeDestroyed();
342     if (QOpenGLContext::currentContext() == this)
343         doneCurrent();
344     if (d->shareGroup)
345         d->shareGroup->d_func()->removeContext(this);
346     d->shareGroup = 0;
347     delete d->platformGLContext;
348     d->platformGLContext = 0;
349     delete d->functions;
350     d->functions = 0;
351 }
352
353 /*!
354     \fn void QOpenGLContext::aboutToBeDestroyed()
355
356     This signal is emitted before the underlying native OpenGL context is
357     destroyed, such that users may clean up OpenGL resources that might
358     otherwise be left dangling in the case of shared OpenGL contexts.
359
360     If you wish to make the context current in order to do clean-up, make sure
361     to only connect to the signal using a direct connection.
362 */
363
364 /*!
365     Destroys the QOpenGLContext object.
366
367     This implicitly calls destroy(), so if this is the current context for the
368     thread, doneCurrent() is also called.
369
370     \sa destroy()
371 */
372 QOpenGLContext::~QOpenGLContext()
373 {
374     destroy();
375
376 #ifndef QT_NO_DEBUG
377     QOpenGLContextPrivate::cleanMakeCurrentTracker(this);
378 #endif
379 }
380
381 /*!
382     Returns if this context is valid, i.e. has been successfully created.
383
384     \sa create()
385 */
386 bool QOpenGLContext::isValid() const
387 {
388     Q_D(const QOpenGLContext);
389     return d->platformGLContext && d->platformGLContext->isValid();
390 }
391
392 /*!
393     Get the QOpenGLFunctions instance for this context.
394
395     QOpenGLContext offers this as a convenient way to access QOpenGLFunctions
396     without having to manage it manually.
397
398     The context or a sharing context must be current.
399 */
400 QOpenGLFunctions *QOpenGLContext::functions() const
401 {
402     Q_D(const QOpenGLContext);
403     if (!d->functions)
404         const_cast<QOpenGLFunctions *&>(d->functions) = new QOpenGLExtensions(QOpenGLContext::currentContext());
405     return d->functions;
406 }
407
408 /*!
409     Returns the set of OpenGL extensions supported by this context.
410
411     The context or a sharing context must be current.
412
413     \sa hasExtension()
414 */
415 QSet<QByteArray> QOpenGLContext::extensions() const
416 {
417     Q_D(const QOpenGLContext);
418     if (d->extensionNames.isEmpty()) {
419         QOpenGLExtensionMatcher matcher;
420         d->extensionNames = matcher.extensions();
421     }
422
423     return d->extensionNames;
424 }
425
426 /*!
427     Returns true if this OpenGL context supports the specified OpenGL
428     \a extension, false otherwise.
429
430     The context or a sharing context must be current.
431
432     \sa extensions()
433 */
434 bool QOpenGLContext::hasExtension(const QByteArray &extension) const
435 {
436     return extensions().contains(extension);
437 }
438
439 /*!
440     Call this to get the default framebuffer object for the current surface.
441
442     On some platforms the default framebuffer object depends on the surface
443     being rendered to, and might be different from 0. Thus, instead of calling
444     glBindFramebuffer(0), you should call
445     glBindFramebuffer(ctx->defaultFramebufferObject()) if you want your
446     application to work across different Qt platforms.
447
448     If you use the glBindFramebuffer() in QOpenGLFunctions you do not have to
449     worry about this, as it automatically binds the current context's
450     defaultFramebufferObject() when 0 is passed.
451 */
452 GLuint QOpenGLContext::defaultFramebufferObject() const
453 {
454     if (!isValid())
455         return 0;
456
457     Q_D(const QOpenGLContext);
458     if (!d->surface || !d->surface->surfaceHandle())
459         return 0;
460
461     return d->platformGLContext->defaultFramebufferObject(d->surface->surfaceHandle());
462 }
463
464 /*!
465     Makes the context current in the current thread, against the given
466     \a surface. Returns true if successful.
467
468     If \a surface is 0 this is equivalent to calling doneCurrent().
469
470     Do not call this function from a different thread than the one the
471     QOpenGLContext instance lives in. If you wish to use QOpenGLContext from a
472     different thread you should first call make sure it's not current in the
473     current thread, by calling doneCurrent() if necessary. Then call
474     moveToThread(otherThread) before using it in the other thread.
475
476     \sa functions(), doneCurrent()
477 */
478 bool QOpenGLContext::makeCurrent(QSurface *surface)
479 {
480     Q_D(QOpenGLContext);
481     if (!isValid())
482         return false;
483
484     if (thread() != QThread::currentThread())
485         qFatal("Cannot make QOpenGLContext current in a different thread");
486
487     if (!surface) {
488         doneCurrent();
489         return true;
490     }
491
492     if (!surface->surfaceHandle())
493         return false;
494
495     if (surface->surfaceType() != QSurface::OpenGLSurface) {
496         qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface";
497         return false;
498     }
499
500
501     if (d->platformGLContext->makeCurrent(surface->surfaceHandle())) {
502         QOpenGLContextPrivate::setCurrentContext(this);
503         d->surface = surface;
504
505         d->shareGroup->d_func()->deletePendingResources(this);
506
507 #ifndef QT_NO_DEBUG
508         QOpenGLContextPrivate::toggleMakeCurrentTracker(this, true);
509 #endif
510
511         return true;
512     }
513
514     return false;
515 }
516
517 /*!
518     Convenience function for calling makeCurrent with a 0 surface.
519
520     This results in no context being current in the current thread.
521
522     \sa makeCurrent(), currentContext()
523 */
524 void QOpenGLContext::doneCurrent()
525 {
526     Q_D(QOpenGLContext);
527     if (!isValid())
528         return;
529
530     if (QOpenGLContext::currentContext() == this)
531         d->shareGroup->d_func()->deletePendingResources(this);
532
533     d->platformGLContext->doneCurrent();
534     QOpenGLContextPrivate::setCurrentContext(0);
535
536     d->surface = 0;
537 }
538
539 /*!
540     Returns the surface the context has been made current with.
541
542     This is the surface passed as an argument to makeCurrent().
543 */
544 QSurface *QOpenGLContext::surface() const
545 {
546     Q_D(const QOpenGLContext);
547     return d->surface;
548 }
549
550
551 /*!
552     Swap the back and front buffers of \a surface.
553
554     Call this to finish a frame of OpenGL rendering, and make sure to
555     call makeCurrent() again before you begin a new frame.
556
557     If you have bound a non-default framebuffer object, you need to
558     use bindDefaultFramebufferObject() to make sure that the default
559     framebuffer object is bound before calling swapBuffers(), as
560     some Qt platforms assume that the default framebuffer object is bound.
561 */
562 void QOpenGLContext::swapBuffers(QSurface *surface)
563 {
564     Q_D(QOpenGLContext);
565     if (!isValid())
566         return;
567
568     if (!surface) {
569         qWarning() << "QOpenGLContext::swapBuffers() called with null argument";
570         return;
571     }
572
573     if (surface->surfaceType() != QSurface::OpenGLSurface) {
574          qWarning() << "QOpenGLContext::swapBuffers() called with non-opengl surface";
575          return;
576     }
577
578     if (surface->surfaceClass() == QSurface::Window
579         && !qt_window_private(static_cast<QWindow *>(surface))->receivedExpose)
580     {
581         qWarning() << "QOpenGLContext::swapBuffers() called with non-exposed window, behavior is undefined";
582     }
583
584     QPlatformSurface *surfaceHandle = surface->surfaceHandle();
585     if (!surfaceHandle)
586         return;
587
588 #if !defined(QT_NO_DEBUG)
589     if (currentContext() != this)
590         qWarning() << "QOpenGLContext::swapBuffers() called with non-current surface";
591     else if (!QOpenGLContextPrivate::toggleMakeCurrentTracker(this, false))
592         qWarning() << "QOpenGLContext::swapBuffers() called without corresponding makeCurrent()";
593
594     GLint framebufferBinding = 0;
595     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &framebufferBinding);
596
597     GLint platformFramebuffer = GLint(d->platformGLContext->defaultFramebufferObject(surfaceHandle));
598     if (framebufferBinding != platformFramebuffer)
599         qWarning() << "QOpenGLContext::swapBuffers() called with non-default framebuffer object bound";
600 #endif
601     if (surface->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
602         glFlush();
603     d->platformGLContext->swapBuffers(surfaceHandle);
604 }
605
606 /*!
607     Resolves the function pointer to an OpenGL extension function, identified by \a procName
608
609     Returns 0 if no such function can be found.
610 */
611 QFunctionPointer QOpenGLContext::getProcAddress(const QByteArray &procName) const
612 {
613     Q_D(const QOpenGLContext);
614     if (!d->platformGLContext)
615         return 0;
616     return d->platformGLContext->getProcAddress(procName);
617 }
618
619 /*!
620     Returns the format of the underlying platform context, if create() has been called.
621
622     Otherwise, returns the requested format.
623 */
624 QSurfaceFormat QOpenGLContext::format() const
625 {
626     Q_D(const QOpenGLContext);
627     if (!d->platformGLContext)
628         return d->requestedFormat;
629     return d->platformGLContext->format();
630 }
631
632 /*!
633     Returns the share group this context belongs to.
634 */
635 QOpenGLContextGroup *QOpenGLContext::shareGroup() const
636 {
637     Q_D(const QOpenGLContext);
638     return d->shareGroup;
639 }
640
641 /*!
642     Returns the share context this context was created with.
643
644     If the underlying platform was not able to support the requested
645     sharing, this will return 0.
646 */
647 QOpenGLContext *QOpenGLContext::shareContext() const
648 {
649     Q_D(const QOpenGLContext);
650     return d->shareContext;
651 }
652
653 /*!
654     Returns the screen the context was created for.
655 */
656 QScreen *QOpenGLContext::screen() const
657 {
658     Q_D(const QOpenGLContext);
659     return d->screen;
660 }
661
662 /*!
663     internal: Needs to have a pointer to qGLContext. But since this is in QtGui we cant
664     have any type information.
665
666     \internal
667 */
668 void *QOpenGLContext::qGLContextHandle() const
669 {
670     Q_D(const QOpenGLContext);
671     return d->qGLContextHandle;
672 }
673
674 /*!
675     \internal
676 */
677 void QOpenGLContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *))
678 {
679     Q_D(QOpenGLContext);
680     d->qGLContextHandle = handle;
681     d->qGLContextDeleteFunction = qGLContextDeleteFunction;
682 }
683
684 /*!
685     \internal
686 */
687 void QOpenGLContext::deleteQGLContext()
688 {
689     Q_D(QOpenGLContext);
690     if (d->qGLContextDeleteFunction && d->qGLContextHandle) {
691         d->qGLContextDeleteFunction(d->qGLContextHandle);
692         d->qGLContextDeleteFunction = 0;
693         d->qGLContextHandle = 0;
694     }
695 }
696
697 /*!
698     \class QOpenGLContextGroup
699     \since 5.0
700     \brief The QOpenGLContextGroup class represents a group of contexts sharing
701     OpenGL resources.
702     \inmodule QtGui
703
704     QOpenGLContextGroup is automatically created and managed by QOpenGLContext
705     instances.  Its purpose is to identify all the contexts that are sharing
706     resources.
707
708     \sa QOpenGLContext::shareGroup()
709 */
710 QOpenGLContextGroup::QOpenGLContextGroup()
711     : QObject(*new QOpenGLContextGroupPrivate())
712 {
713 }
714
715 /*!
716     \internal
717 */
718 QOpenGLContextGroup::~QOpenGLContextGroup()
719 {
720     Q_D(QOpenGLContextGroup);
721     d->cleanup();
722 }
723
724 /*!
725     Returns all the QOpenGLContext objects in this share group.
726 */
727 QList<QOpenGLContext *> QOpenGLContextGroup::shares() const
728 {
729     Q_D(const QOpenGLContextGroup);
730     return d->m_shares;
731 }
732
733 /*!
734     Returns the QOpenGLContextGroup corresponding to the current context.
735
736     \sa QOpenGLContext::currentContext()
737 */
738 QOpenGLContextGroup *QOpenGLContextGroup::currentContextGroup()
739 {
740     QOpenGLContext *current = QOpenGLContext::currentContext();
741     return current ? current->shareGroup() : 0;
742 }
743
744 void QOpenGLContextGroupPrivate::addContext(QOpenGLContext *ctx)
745 {
746     QMutexLocker locker(&m_mutex);
747     m_refs.ref();
748     m_shares << ctx;
749 }
750
751 void QOpenGLContextGroupPrivate::removeContext(QOpenGLContext *ctx)
752 {
753     Q_Q(QOpenGLContextGroup);
754
755     QMutexLocker locker(&m_mutex);
756     m_shares.removeOne(ctx);
757
758     if (ctx == m_context && !m_shares.isEmpty())
759         m_context = m_shares.first();
760
761     if (!m_refs.deref()) {
762         cleanup();
763         q->deleteLater();
764     }
765 }
766
767 void QOpenGLContextGroupPrivate::cleanup()
768 {
769     Q_Q(QOpenGLContextGroup);
770     {
771         QHash<QOpenGLMultiGroupSharedResource *, QOpenGLSharedResource *>::const_iterator it, end;
772         end = m_resources.constEnd();
773         for (it = m_resources.constBegin(); it != end; ++it)
774             it.key()->cleanup(q, it.value());
775         m_resources.clear();
776     }
777
778     QList<QOpenGLSharedResource *>::iterator it = m_sharedResources.begin();
779     QList<QOpenGLSharedResource *>::iterator end = m_sharedResources.end();
780
781     while (it != end) {
782         (*it)->invalidateResource();
783         (*it)->m_group = 0;
784         ++it;
785     }
786
787     m_sharedResources.clear();
788
789     qDeleteAll(m_pendingDeletion.begin(), m_pendingDeletion.end());
790     m_pendingDeletion.clear();
791 }
792
793 void QOpenGLContextGroupPrivate::deletePendingResources(QOpenGLContext *ctx)
794 {
795     QMutexLocker locker(&m_mutex);
796
797     QList<QOpenGLSharedResource *> pending = m_pendingDeletion;
798     m_pendingDeletion.clear();
799
800     QList<QOpenGLSharedResource *>::iterator it = pending.begin();
801     QList<QOpenGLSharedResource *>::iterator end = pending.end();
802     while (it != end) {
803         (*it)->freeResource(ctx);
804         delete *it;
805         ++it;
806     }
807 }
808
809 /*!
810     \class QOpenGLSharedResource
811     \internal
812     \since 5.0
813     \brief The QOpenGLSharedResource class is used to keep track of resources
814     that are shared between OpenGL contexts (like textures, framebuffer
815     objects, shader programs, etc), and clean them up in a safe way when
816     they're no longer needed.
817     \inmodule QtGui
818
819     The QOpenGLSharedResource instance should never be deleted, instead free()
820     should be called when it's no longer needed. Thus it will be put on a queue
821     and freed at an appropriate time (when a context in the share group becomes
822     current).
823
824     The sub-class needs to implement two pure virtual functions. The first,
825     freeResource() must be implemented to actually do the freeing, for example
826     call glDeleteTextures() on a texture id. Qt makes sure a valid context in
827     the resource's share group is current at the time. The other,
828     invalidateResource(), is called by Qt in the circumstance when the last
829     context in the share group is destroyed before free() has been called. The
830     implementation of invalidateResource() should set any identifiers to 0 or
831     set a flag to prevent them from being used later on.
832 */
833 QOpenGLSharedResource::QOpenGLSharedResource(QOpenGLContextGroup *group)
834     : m_group(group)
835 {
836     QMutexLocker locker(&m_group->d_func()->m_mutex);
837     m_group->d_func()->m_sharedResources << this;
838 }
839
840 QOpenGLSharedResource::~QOpenGLSharedResource()
841 {
842 }
843
844 // schedule the resource for deletion at an appropriate time
845 void QOpenGLSharedResource::free()
846 {
847     if (!m_group) {
848         delete this;
849         return;
850     }
851
852     QMutexLocker locker(&m_group->d_func()->m_mutex);
853     m_group->d_func()->m_sharedResources.removeOne(this);
854     m_group->d_func()->m_pendingDeletion << this;
855
856     // can we delete right away?
857     QOpenGLContext *current = QOpenGLContext::currentContext();
858     if (current && current->shareGroup() == m_group) {
859         m_group->d_func()->deletePendingResources(current);
860     }
861 }
862
863 /*!
864     \class QOpenGLSharedResourceGuard
865     \internal
866     \since 5.0
867     \brief The QOpenGLSharedResourceGuard class is a convenience sub-class of
868     QOpenGLSharedResource to be used to track a single OpenGL object with a
869     GLuint identifier. The constructor takes a function pointer to a function
870     that will be used to free the resource if and when necessary.
871     \inmodule QtGui
872
873 */
874 void QOpenGLSharedResourceGuard::freeResource(QOpenGLContext *context)
875 {
876     if (m_id) {
877         QOpenGLFunctions functions(context);
878         m_func(&functions, m_id);
879         m_id = 0;
880     }
881 }
882
883 /*!
884     \class QOpenGLMultiGroupSharedResource
885     \internal
886     \since 5.0
887     \brief The QOpenGLMultiGroupSharedResource keeps track of a shared resource
888     that might be needed from multiple contexts, like a glyph cache or gradient
889     cache. One instance of the object is created for each group when necessary.
890     The shared resource instance should have a constructor that takes a
891     QOpenGLContext *. To get an instance for a given context one calls
892     T *QOpenGLMultiGroupSharedResource::value<T>(context), where T is a sub-class
893     of QOpenGLSharedResource.
894     \inmodule QtGui
895
896     You should not call free() on QOpenGLSharedResources owned by a
897     QOpenGLMultiGroupSharedResource instance.
898 */
899 QOpenGLMultiGroupSharedResource::QOpenGLMultiGroupSharedResource()
900     : active(0)
901 {
902 #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
903     qDebug("Creating context group resource object %p.", this);
904 #endif
905 }
906
907 QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
908 {
909 #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
910     qDebug("Deleting context group resource %p. Group size: %d.", this, m_groups.size());
911 #endif
912     for (int i = 0; i < m_groups.size(); ++i) {
913         if (!m_groups.at(i)->shares().isEmpty()) {
914             QOpenGLContext *context = m_groups.at(i)->shares().first();
915             QOpenGLSharedResource *resource = value(context);
916             if (resource)
917                 resource->free();
918         }
919         m_groups.at(i)->d_func()->m_resources.remove(this);
920         active.deref();
921     }
922 #ifndef QT_NO_DEBUG
923     if (active.load() != 0) {
924         qWarning("QtGui: Resources are still available at program shutdown.\n"
925                  "          This is possibly caused by a leaked QOpenGLWidget, \n"
926                  "          QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
927     }
928 #endif
929 }
930
931 void QOpenGLMultiGroupSharedResource::insert(QOpenGLContext *context, QOpenGLSharedResource *value)
932 {
933 #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
934     qDebug("Inserting context group resource %p for context %p, managed by %p.", value, context, this);
935 #endif
936     QOpenGLContextGroup *group = context->shareGroup();
937     Q_ASSERT(!group->d_func()->m_resources.contains(this));
938     group->d_func()->m_resources.insert(this, value);
939     m_groups.append(group);
940     active.ref();
941 }
942
943 QOpenGLSharedResource *QOpenGLMultiGroupSharedResource::value(QOpenGLContext *context)
944 {
945     QOpenGLContextGroup *group = context->shareGroup();
946     return group->d_func()->m_resources.value(this, 0);
947 }
948
949 QList<QOpenGLSharedResource *> QOpenGLMultiGroupSharedResource::resources() const
950 {
951     QList<QOpenGLSharedResource *> result;
952     for (QList<QOpenGLContextGroup *>::const_iterator it = m_groups.constBegin(); it != m_groups.constEnd(); ++it) {
953         QOpenGLSharedResource *resource = (*it)->d_func()->m_resources.value(const_cast<QOpenGLMultiGroupSharedResource *>(this), 0);
954         if (resource)
955             result << resource;
956     }
957     return result;
958 }
959
960 void QOpenGLMultiGroupSharedResource::cleanup(QOpenGLContextGroup *group, QOpenGLSharedResource *value)
961 {
962 #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG
963     qDebug("Cleaning up context group resource %p, for group %p in thread %p.", this, group, QThread::currentThread());
964 #endif
965     value->invalidateResource();
966     value->free();
967     active.deref();
968
969     Q_ASSERT(m_groups.contains(group));
970     m_groups.removeOne(group);
971 }
972
973 QT_END_NAMESPACE