From 68c9a2f82d873068c3adbde520c76d81d0a2dd62 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 3 Aug 2014 15:22:43 +0200 Subject: Enhance QOpenGLWidget docs about resource management Change-Id: Idd1181a34055237b13643dbc58e855db411d0a7c Reviewed-by: Gunnar Sletta --- .../code/doc_gui_widgets_qopenglwidget.cpp | 71 ++++++++++++++++++++++ src/widgets/kernel/qopenglwidget.cpp | 57 ++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-) (limited to 'src/widgets') diff --git a/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp index bc279e4406..0814b5f30f 100644 --- a/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp +++ b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp @@ -107,3 +107,74 @@ widget->setFormat(format); // must be called before the widget or its parent win } ... //! [3] + +//! [4] +class MyGLWidget : public QOpenGLWidget +{ + ... + +private: + QOpenGLVertexArrayObject m_vao; + QOpenGLBuffer m_vbo; + QOpenGLShaderProgram *m_program; + QOpenGLShader *m_shader; + QOpenGLTexture *m_texture; +}; + +MyGLWidget::MyGLWidget() + : m_program(0), m_shader(0), m_texture(0) +{ + // No OpenGL resource initialization is done here. +} + +MyGLWidget::~MyGLWidget() +{ + // Make sure the context is current and then explicitly + // destroy all underlying OpenGL resources. + makeCurrent(); + + delete m_texture; + delete m_shader; + delete m_program; + + m_vbo.destroy(); + m_vao.destroy(); + + doneCurrent(); +} + +void MyGLWidget::initializeGL() +{ + m_vao.create(); + if (m_vao.isCreated()) + m_vao.bind(); + + m_vbo.create(); + m_vbo.bind(); + m_vbo.allocate(...); + + m_texture = new QOpenGLTexture(QImage(...)); + + m_shader = new QOpenGLShader(...); + m_program = new QOpenGLShaderProgram(...); + + ... +} +//! [4] + +//! [5] +void MyGLWidget::initializeGL() +{ + // context() and QOpenGLContext::currentContext() are equivalent when called from initializeGL or paintGL. + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup); +} + +void MyGLWidget::cleanup() +{ + makeCurrent(); + delete m_texture; + m_texture = 0; + ... + doneCurrent(); +} +//! [5] diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 907f69dfbd..615e23b216 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -269,6 +269,54 @@ QT_BEGIN_NAMESPACE created later. Some other drivers may behave in unexpected ways when trying to utilize shared resources between different threads. + \section1 Resource initialization and cleanup + + The QOpenGLWidget's associated OpenGL context is guaranteed to be current + whenever initializeGL() and paintGL() are invoked. Do not attempt to create + OpenGL resources before initializeGL() is called. For example, attempting to + compile shaders, initialize vertex buffer objects or upload texture data will + fail when done in a subclass's constructor. These operations must be deferred + to initializeGL(). Some of Qt's OpenGL helper classes, like QOpenGLBuffer or + QOpenGLVertexArrayObject, have a matching deferred behavior: they can be + instantiated without a context, but all initialization is deferred until a + create(), or similar, call. This means that they can be used as normal + (non-pointer) member variables in a QOpenGLWidget subclass, but the create() + or similar function can only be called from initializeGL(). Be aware however + that not all classes are designed like this. When in doubt, make the member + variable a pointer and create and destroy the instance dynamically in + initializeGL() and the destructor, respectively. + + Releasing the resources also needs the context to be current. Therefore + destructors that perform such cleanup are expected to call makeCurrent() + before moving on to destroy any OpenGL resources or wrappers. Avoid deferred + deletion via \l{QObject::deleteLater()}{deleteLater()} or the parenting + mechanism of QObject. There is no guarantee the correct context will be + current at the time the instance in question is really destroyed. + + A typical subclass will therefore often look like the following when it comes + to resource initialization and destruction: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 4 + + This is naturally not the only possible solution. One alternative is to use + the \l{QOpenGLContext::aboutToBeDestroyed()}{aboutToBeDestroyed()} signal of + QOpenGLContext. By connecting a slot, using direct connection, to this signal, + it is possible to perform cleanup whenever the the underlying native context + handle, or the entire QOpenGLContext instance, is going to be released. The + following snippet is in principal equivalent to the previous one: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 5 + + Proper cleanup is especially important due to context sharing. Even though + each QOpenGLWidget's associated context is destroyed together with the + QOpenGLWidget, the sharable resources in that context, like textures, will + stay valid until the top-level window, in which the QOpenGLWidget lived, is + destroyed. Additionally, some Qt modules may trigger an even wider scope for + sharing contexts, potentially leading to keeping the resources in question + alive for the entire lifetime of the application. Therefore the safest and + most robust is always to perform explicit cleanup for all resources and + resource wrappers used in the QOpenGLWidget. + \section1 Limitations Putting other widgets underneath and making the QOpenGLWidget transparent will @@ -748,10 +796,13 @@ void QOpenGLWidget::paintGL() } /*! - \internal - - Handles resize events that are passed in the \a event parameter. + Handles resize events that are passed in the \a e event parameter. Calls the virtual function resizeGL(). + + \note Avoid overriding this function in derived classes. If that is not + feasible, make sure that QOpenGLWidget's implementation is invoked + too. Otherwise the underlying framebuffer object and related resources will + not get resized properly and will lead to incorrect rendering. */ void QOpenGLWidget::resizeEvent(QResizeEvent *e) { -- cgit v1.2.3