Let QPlatformBackingStore handle its own QOpenGLContext

The resources allocated by QPlatformBackingStore are owned by the class,
and should be allocated in a context the class also owns. This removes
the asymmetry of having to pass in a context to composeAndFlush, while
having to make the same context current before destroying the platform
backingstore.

The context owned by QPlatformBackingStore is shared with the associated
window though a new QWindowPrivate::shareContext() API.

The result is that on e.g. iOS, the backingstore does not need to tie
the resource allocation of QPlatformBackingStore to the global share
context, but can instead tie them to the per-window context, and hence
clean them up after each window is closed.

Task-number: QTBUG-56653
Change-Id: Ic1bcae50dafeeafaa8d16a7febd83b840ec6367a
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-10-03 19:24:50 +02:00
parent aeee3be166
commit 8e70241dcc
13 changed files with 50 additions and 63 deletions

View File

@ -45,6 +45,7 @@
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
#include <qpa/qplatformopenglcontext.h> #include <qpa/qplatformopenglcontext.h>
#include "qopenglcontext.h" #include "qopenglcontext.h"
#include "qopenglcontext_p.h"
#endif #endif
#include "qscreen.h" #include "qscreen.h"
@ -2633,6 +2634,11 @@ QWindow *QWindowPrivate::topLevelWindow() const
return window; return window;
} }
QOpenGLContext *QWindowPrivate::shareContext() const
{
return qt_gl_global_share_context();
};
/*! /*!
Creates a local representation of a window created by another process or by Creates a local representation of a window created by another process or by
using native libraries below Qt. using native libraries below Qt.

View File

@ -130,6 +130,8 @@ public:
QWindow *topLevelWindow() const; QWindow *topLevelWindow() const;
virtual QOpenGLContext *shareContext() const;
virtual QWindow *eventReceiver() { Q_Q(QWindow); return q; } virtual QWindow *eventReceiver() { Q_Q(QWindow); return q; }
virtual void setVisible(bool visible); virtual void setVisible(bool visible);

View File

@ -50,6 +50,7 @@
#include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLFunctions>
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
#include <QtGui/qopengltextureblitter.h> #include <QtGui/qopengltextureblitter.h>
#include <QtGui/qoffscreensurface.h>
#endif #endif
#include <qpa/qplatformgraphicsbuffer.h> #include <qpa/qplatformgraphicsbuffer.h>
#include <qpa/qplatformgraphicsbufferhelper.h> #include <qpa/qplatformgraphicsbufferhelper.h>
@ -95,14 +96,15 @@ public:
~QPlatformBackingStorePrivate() ~QPlatformBackingStorePrivate()
{ {
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (context) {
if (ctx) { QOffscreenSurface offscreenSurface;
offscreenSurface.setFormat(context->format());
offscreenSurface.create();
context->makeCurrent(&offscreenSurface);
if (textureId) if (textureId)
ctx->functions()->glDeleteTextures(1, &textureId); context->functions()->glDeleteTextures(1, &textureId);
if (blitter) if (blitter)
blitter->destroy(); blitter->destroy();
} else if (textureId || blitter) {
qWarning("No context current during QPlatformBackingStore destruction, OpenGL resources not released");
} }
delete blitter; delete blitter;
#endif #endif
@ -110,6 +112,7 @@ public:
QWindow *window; QWindow *window;
QBackingStore *backingStore; QBackingStore *backingStore;
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
QScopedPointer<QOpenGLContext> context;
mutable GLuint textureId; mutable GLuint textureId;
mutable QSize textureSize; mutable QSize textureSize;
mutable bool needsSwizzle; mutable bool needsSwizzle;
@ -316,20 +319,31 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx,
void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &region, void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &region,
const QPoint &offset, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground) bool translucentBackground)
{ {
if (!qt_window_private(window)->receivedExpose) if (!qt_window_private(window)->receivedExpose)
return; return;
if (!context->makeCurrent(window)) { if (!d_ptr->context) {
d_ptr->context.reset(new QOpenGLContext);
d_ptr->context->setFormat(d_ptr->window->requestedFormat());
d_ptr->context->setScreen(d_ptr->window->screen());
d_ptr->context->setShareContext(qt_window_private(d_ptr->window)->shareContext());
if (!d_ptr->context->create()) {
qWarning("composeAndFlush: QOpenGLContext creation failed");
return;
}
}
if (!d_ptr->context->makeCurrent(window)) {
qWarning("composeAndFlush: makeCurrent() failed"); qWarning("composeAndFlush: makeCurrent() failed");
return; return;
} }
QWindowPrivate::get(window)->lastComposeTime.start(); QWindowPrivate::get(window)->lastComposeTime.start();
QOpenGLFunctions *funcs = context->functions(); QOpenGLFunctions *funcs = d_ptr->context->functions();
funcs->glViewport(0, 0, window->width() * window->devicePixelRatio(), window->height() * window->devicePixelRatio()); funcs->glViewport(0, 0, window->width() * window->devicePixelRatio(), window->height() * window->devicePixelRatio());
funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1); funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1);
funcs->glClear(GL_COLOR_BUFFER_BIT); funcs->glClear(GL_COLOR_BUFFER_BIT);
@ -435,7 +449,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
funcs->glDisable(GL_BLEND); funcs->glDisable(GL_BLEND);
d_ptr->blitter->release(); d_ptr->blitter->release();
context->swapBuffers(window); d_ptr->context->swapBuffers(window);
} }
#endif #endif
/*! /*!

View File

@ -120,7 +120,7 @@ public:
virtual void flush(QWindow *window, const QRegion &region, const QPoint &offset) = 0; virtual void flush(QWindow *window, const QRegion &region, const QPoint &offset) = 0;
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
virtual void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset, virtual void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground); bool translucentBackground);
#endif #endif
virtual QImage toImage() const; virtual QImage toImage() const;

View File

@ -202,14 +202,13 @@ void QOpenGLCompositorBackingStore::flush(QWindow *window, const QRegion &region
} }
void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset, void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground) bool translucentBackground)
{ {
// QOpenGLWidget/QQuickWidget content provided as textures. The raster content goes on top. // QOpenGLWidget/QQuickWidget content provided as textures. The raster content goes on top.
Q_UNUSED(region); Q_UNUSED(region);
Q_UNUSED(offset); Q_UNUSED(offset);
Q_UNUSED(context);
Q_UNUSED(translucentBackground); Q_UNUSED(translucentBackground);
QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
@ -218,7 +217,7 @@ void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegi
// The compositor's context and the context to which QOpenGLWidget/QQuickWidget // The compositor's context and the context to which QOpenGLWidget/QQuickWidget
// textures belong are not the same. They share resources, though. // textures belong are not the same. They share resources, though.
Q_ASSERT(context->shareGroup() == dstCtx->shareGroup()); Q_ASSERT(qt_window_private(window)->shareContext()->shareGroup() == dstCtx->shareGroup());
QWindow *dstWin = compositor->targetWindow(); QWindow *dstWin = compositor->targetWindow();
if (!dstWin) if (!dstWin)

View File

@ -75,7 +75,7 @@ public:
QImage toImage() const Q_DECL_OVERRIDE; QImage toImage() const Q_DECL_OVERRIDE;
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset, void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground) Q_DECL_OVERRIDE; bool translucentBackground) Q_DECL_OVERRIDE;
const QPlatformTextureList *textures() const { return m_textures; } const QPlatformTextureList *textures() const { return m_textures; }

View File

@ -55,9 +55,6 @@ public:
~QIOSBackingStore(); ~QIOSBackingStore();
void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE; void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
private:
QOpenGLContext *m_context;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -55,7 +55,6 @@ QT_BEGIN_NAMESPACE
*/ */
QIOSBackingStore::QIOSBackingStore(QWindow *window) QIOSBackingStore::QIOSBackingStore(QWindow *window)
: QRasterBackingStore(window) : QRasterBackingStore(window)
, m_context(new QOpenGLContext)
{ {
// We use the surface both for raster operations and for GL drawing (when // We use the surface both for raster operations and for GL drawing (when
// we blit the raster image), so the type needs to cover both use cases. // we blit the raster image), so the type needs to cover both use cases.
@ -64,22 +63,10 @@ QIOSBackingStore::QIOSBackingStore(QWindow *window)
Q_ASSERT_X(window->surfaceType() != QSurface::OpenGLSurface, "QIOSBackingStore", Q_ASSERT_X(window->surfaceType() != QSurface::OpenGLSurface, "QIOSBackingStore",
"QBackingStore on iOS can only be used with raster-enabled surfaces."); "QBackingStore on iOS can only be used with raster-enabled surfaces.");
m_context->setFormat(window->requestedFormat());
m_context->setScreen(window->screen());
Q_ASSERT(QOpenGLContext::globalShareContext());
m_context->setShareContext(QOpenGLContext::globalShareContext());
m_context->create();
} }
QIOSBackingStore::~QIOSBackingStore() QIOSBackingStore::~QIOSBackingStore()
{ {
// We're using composeAndFlush from QPlatformBackingStore, which
// need to clean up any textures in its destructor, so make the
// context current and keep it alive until QPlatformBackingStore
// has cleaned up everything.
m_context->makeCurrent(window());
m_context->deleteLater();
} }
void QIOSBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset) void QIOSBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
@ -98,7 +85,7 @@ void QIOSBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
} }
static QPlatformTextureList emptyTextureList; static QPlatformTextureList emptyTextureList;
composeAndFlush(window, region, offset, &emptyTextureList, m_context, false); composeAndFlush(window, region, offset, &emptyTextureList, false);
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -600,10 +600,10 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset, void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground) bool translucentBackground)
{ {
QPlatformBackingStore::composeAndFlush(window, region, offset, textures, context, translucentBackground); QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (platformWindow->needsSync()) { if (platformWindow->needsSync()) {

View File

@ -61,7 +61,7 @@ public:
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override; void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset, void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context, QPlatformTextureList *textures,
bool translucentBackground) override; bool translucentBackground) override;
#endif #endif
QImage toImage() const override; QImage toImage() const override;

View File

@ -1876,39 +1876,15 @@ static void deleteBackingStore(QWidgetPrivate *d)
{ {
QTLWExtra *topData = d->topData(); QTLWExtra *topData = d->topData();
// The context must be current when destroying the backing store as it may attempt to
// release resources like textures and shader programs. The window may not be suitable
// anymore as there will often not be a platform window underneath at this stage. Fall
// back to a QOffscreenSurface in this case.
QScopedPointer<QOffscreenSurface> tempSurface;
#ifndef QT_NO_OPENGL
if (d->textureChildSeen && topData->shareContext) {
if (topData->window->handle()) {
topData->shareContext->makeCurrent(topData->window);
} else {
tempSurface.reset(new QOffscreenSurface);
tempSurface->setFormat(topData->shareContext->format());
tempSurface->create();
topData->shareContext->makeCurrent(tempSurface.data());
}
}
#endif
delete topData->backingStore; delete topData->backingStore;
topData->backingStore = 0; topData->backingStore = 0;
#ifndef QT_NO_OPENGL
if (d->textureChildSeen && topData->shareContext)
topData->shareContext->doneCurrent();
#endif
} }
void QWidgetPrivate::deleteTLSysExtra() void QWidgetPrivate::deleteTLSysExtra()
{ {
if (extra && extra->topextra) { if (extra && extra->topextra) {
//the qplatformbackingstore may hold a reference to the window, so the backingstore //the qplatformbackingstore may hold a reference to the window, so the backingstore
//needs to be deleted first. If the backingstore holds GL resources, we need to //needs to be deleted first.
// make the context current here. This is taken care of by deleteBackingStore().
extra->topextra->backingStoreTracker.destroy(); extra->topextra->backingStoreTracker.destroy();
deleteBackingStore(this); deleteBackingStore(this);

View File

@ -143,10 +143,8 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBack
// WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
// to rely on translucency, in order to decide if it should clear to transparent or opaque. // to rely on translucency, in order to decide if it should clear to transparent or opaque.
const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground); const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
// Use the tlw's context, not widget's. The difference is important with native child backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset,
// widgets where tlw != widget. widgetTextures, translucentBackground);
backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset, widgetTextures,
tlw->d_func()->shareContext(), translucentBackground);
widget->window()->d_func()->sendComposeStatus(widget->window(), true); widget->window()->d_func()->sendComposeStatus(widget->window(), true);
} else } else
#endif #endif

View File

@ -96,6 +96,7 @@ public:
} }
QRectF closestAcceptableGeometry(const QRectF &rect) const Q_DECL_OVERRIDE; QRectF closestAcceptableGeometry(const QRectF &rect) const Q_DECL_OVERRIDE;
QOpenGLContext *shareContext() const override;
}; };
QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
@ -127,6 +128,13 @@ QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
return result; return result;
} }
QOpenGLContext *QWidgetWindowPrivate::shareContext() const
{
Q_Q(const QWidgetWindow);
const QWidgetPrivate *widgetPrivate = QWidgetPrivate::get(q->widget());
return widgetPrivate->shareContext();
}
QWidgetWindow::QWidgetWindow(QWidget *widget) QWidgetWindow::QWidgetWindow(QWidget *widget)
: QWindow(*new QWidgetWindowPrivate(), 0) : QWindow(*new QWidgetWindowPrivate(), 0)
, m_widget(widget) , m_widget(widget)