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