Lazily create global share context when Qt::AA_ShareOpenGLContexts is set

The requirement to set Qt::AA_ShareOpenGLContexts before creating QGuiApp
was forcing users to also set the default surface format before QGuiApp,
which prevents us from initializing a default surface format based on
the platform integration.

By creating the global share context lazily when requested via the
Qt::AA_ShareOpenGLContext application attribute we open up this
possibility.

Change-Id: I958639c997e96321013b1080c31e2533a36c13ff
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Tor Arne Vestbø 2025-05-05 14:35:48 +02:00
parent 868fc456ac
commit 4a7ccb65f0
11 changed files with 35 additions and 36 deletions

View File

@ -1013,7 +1013,6 @@ void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on)
case Qt::AA_UseDesktopOpenGL: case Qt::AA_UseDesktopOpenGL:
case Qt::AA_UseOpenGLES: case Qt::AA_UseOpenGLES:
case Qt::AA_UseSoftwareOpenGL: case Qt::AA_UseSoftwareOpenGL:
case Qt::AA_ShareOpenGLContexts:
#ifdef QT_BOOTSTRAPPED #ifdef QT_BOOTSTRAPPED
qWarning("Attribute %d must be set before QCoreApplication is created.", qWarning("Attribute %d must be set before QCoreApplication is created.",
attribute); attribute);

View File

@ -727,8 +727,7 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv) QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv)
: QCoreApplicationPrivate(argc, argv), : QCoreApplicationPrivate(argc, argv),
inputMethod(nullptr), inputMethod(nullptr),
lastTouchType(QEvent::TouchEnd), lastTouchType(QEvent::TouchEnd)
ownGlobalShareContext(false)
{ {
self = this; self = this;
application_type = QCoreApplicationPrivate::Gui; application_type = QCoreApplicationPrivate::Gui;
@ -1740,17 +1739,6 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init()
qRegisterGuiGetInterpolator(); qRegisterGuiGetInterpolator();
#endif #endif
// set a global share context when enabled unless there is already one
#ifndef QT_NO_OPENGL
if (qApp->testAttribute(Qt::AA_ShareOpenGLContexts) && !qt_gl_global_share_context()) {
QOpenGLContext *ctx = new QOpenGLContext;
ctx->setFormat(QSurfaceFormat::defaultFormat());
ctx->create();
qt_gl_set_global_share_context(ctx);
ownGlobalShareContext = true;
}
#endif
QWindowSystemInterfacePrivate::eventTime.start(); QWindowSystemInterfacePrivate::eventTime.start();
is_app_running = true; is_app_running = true;

View File

@ -342,6 +342,10 @@ public:
static QThreadPool *qtGuiThreadPool(); static QThreadPool *qtGuiThreadPool();
#ifndef QT_NO_OPENGL
bool ownGlobalShareContext = false;
#endif
protected: protected:
virtual void handleThemeChanged(); virtual void handleThemeChanged();
@ -366,8 +370,6 @@ private:
#endif #endif
std::shared_ptr<QColorTrcLut> m_a32ColorProfile; std::shared_ptr<QColorTrcLut> m_a32ColorProfile;
bool ownGlobalShareContext;
static QInputDeviceManager *m_inputDeviceManager; static QInputDeviceManager *m_inputDeviceManager;
// Cache the maximum device pixel ratio, to iterate through the screen list // Cache the maximum device pixel ratio, to iterate through the screen list

View File

@ -959,10 +959,6 @@ bool QOpenGLContext::supportsThreadedOpenGL()
This is useful if you need to upload OpenGL objects (buffers, textures, This is useful if you need to upload OpenGL objects (buffers, textures,
etc.) before creating or showing a QOpenGLWidget or QQuickWidget. etc.) before creating or showing a QOpenGLWidget or QQuickWidget.
\note You must set the Qt::AA_ShareOpenGLContexts flag on QGuiApplication
before creating the QGuiApplication object, otherwise Qt may not create a
global shared context.
\warning Do not attempt to make the context returned by this function \warning Do not attempt to make the context returned by this function
current on any surface. Instead, you can create a new context which shares current on any surface. Instead, you can create a new context which shares
with the global one, and then make the new context current. with the global one, and then make the new context current.
@ -972,6 +968,14 @@ bool QOpenGLContext::supportsThreadedOpenGL()
QOpenGLContext *QOpenGLContext::globalShareContext() QOpenGLContext *QOpenGLContext::globalShareContext()
{ {
Q_ASSERT(qGuiApp); Q_ASSERT(qGuiApp);
// Lazily create a global share context when enabled unless there is already one
if (!qt_gl_global_share_context() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
QOpenGLContext *ctx = new QOpenGLContext;
ctx->setFormat(QSurfaceFormat::defaultFormat());
ctx->create();
qt_gl_set_global_share_context(ctx);
QGuiApplicationPrivate::instance()->ownGlobalShareContext = true;
}
return qt_gl_global_share_context(); return qt_gl_global_share_context();
} }

View File

@ -9,6 +9,10 @@
#include <QtGui/qcolorspace.h> #include <QtGui/qcolorspace.h>
#include <QtGui/qguiapplication.h> #include <QtGui/qguiapplication.h>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qopenglcontext_p.h>
#endif
#ifdef major #ifdef major
#undef major #undef major
#endif #endif
@ -807,12 +811,6 @@ Q_GLOBAL_STATIC(QSurfaceFormat, qt_default_surface_format)
and surfaces, even the ones created internally by Qt, will use the same and surfaces, even the ones created internally by Qt, will use the same
format. format.
\note When setting Qt::AA_ShareOpenGLContexts, it is strongly recommended to
place the call to this function before the construction of the
QGuiApplication or QApplication. Otherwise \a format will not be applied to
the global share context and therefore issues may arise with context sharing
afterwards.
\since 5.4 \since 5.4
\sa defaultFormat() \sa defaultFormat()
*/ */
@ -820,7 +818,7 @@ void QSurfaceFormat::setDefaultFormat(const QSurfaceFormat &format)
{ {
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
if (qApp) { if (qApp) {
QOpenGLContext *globalContext = QOpenGLContext::globalShareContext(); QOpenGLContext *globalContext = qt_gl_global_share_context();
if (globalContext && globalContext->isValid()) { if (globalContext && globalContext->isValid()) {
qWarning("Warning: Setting a new default format with a different version or profile " qWarning("Warning: Setting a new default format with a different version or profile "
"after the global shared context is created may cause issues with context " "after the global shared context is created may cause issues with context "

View File

@ -74,7 +74,7 @@ bool QBackingStoreRhiSupport::create()
params.fallbackSurface = surface; params.fallbackSurface = surface;
params.window = m_window; params.window = m_window;
params.format = m_format; params.format = m_format;
params.shareContext = qt_gl_global_share_context(); params.shareContext = QOpenGLContext::globalShareContext();
rhi = QRhi::create(QRhi::OpenGLES2, &params, flags); rhi = QRhi::create(QRhi::OpenGLES2, &params, flags);
} }
#endif #endif

View File

@ -771,7 +771,7 @@ bool QRhiGles2::create(QRhi::Flags flags)
if (maybeShareContext) { if (maybeShareContext) {
ctx->setShareContext(maybeShareContext); ctx->setShareContext(maybeShareContext);
ctx->setScreen(maybeShareContext->screen()); ctx->setScreen(maybeShareContext->screen());
} else if (QOpenGLContext *shareContext = qt_gl_global_share_context()) { } else if (QOpenGLContext *shareContext = QOpenGLContext::globalShareContext()) {
ctx->setShareContext(shareContext); ctx->setShareContext(shareContext);
ctx->setScreen(shareContext->screen()); ctx->setScreen(shareContext->screen());
} else if (maybeWindow) { } else if (maybeWindow) {

View File

@ -161,7 +161,7 @@ public:
, shareContext(shareContext) , shareContext(shareContext)
{ {
if (!shareContext) if (!shareContext)
this->shareContext = qt_gl_global_share_context(); this->shareContext = QOpenGLContext::globalShareContext();
} }
~QOpenGLWindowPrivate(); ~QOpenGLWindowPrivate();

View File

@ -856,7 +856,7 @@ void QOpenGLWidgetPrivate::initialize()
context = new QOpenGLContext; context = new QOpenGLContext;
context->setFormat(requestedFormat); context->setFormat(requestedFormat);
QOpenGLContext *shareContext = contextFromRhi ? contextFromRhi : qt_gl_global_share_context(); QOpenGLContext *shareContext = contextFromRhi ? contextFromRhi : QOpenGLContext::globalShareContext();
if (shareContext) { if (shareContext) {
context->setShareContext(shareContext); context->setShareContext(shareContext);
context->setScreen(shareContext->screen()); context->setScreen(shareContext->screen());

View File

@ -108,14 +108,14 @@ void QEglFSWindow::setBackingStore(QOpenGLCompositorBackingStore *backingStore)
#ifndef QT_NO_OPENGL #ifndef QT_NO_OPENGL
if (!m_rasterCompositingContext) { if (!m_rasterCompositingContext) {
m_rasterCompositingContext = new QOpenGLContext; m_rasterCompositingContext = new QOpenGLContext;
m_rasterCompositingContext->setShareContext(qt_gl_global_share_context()); m_rasterCompositingContext->setShareContext(QOpenGLContext::globalShareContext());
m_rasterCompositingContext->setFormat(m_format); m_rasterCompositingContext->setFormat(m_format);
m_rasterCompositingContext->setScreen(window()->screen()); m_rasterCompositingContext->setScreen(window()->screen());
if (Q_UNLIKELY(!m_rasterCompositingContext->create())) if (Q_UNLIKELY(!m_rasterCompositingContext->create()))
qFatal("EGLFS: Failed to create compositing context"); qFatal("EGLFS: Failed to create compositing context");
// If there is a "root" window into which raster and QOpenGLWidget content is // If there is a "root" window into which raster and QOpenGLWidget content is
// composited, all other contexts must share with its context. // composited, all other contexts must share with its context.
if (!qt_gl_global_share_context()) if (!QOpenGLContext::globalShareContext())
qt_gl_set_global_share_context(m_rasterCompositingContext); qt_gl_set_global_share_context(m_rasterCompositingContext);
} }
QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
@ -145,7 +145,7 @@ void QEglFSWindow::destroy()
if (compositor->windows().isEmpty()) { if (compositor->windows().isEmpty()) {
compositor->destroy(); compositor->destroy();
if (qt_gl_global_share_context() == m_rasterCompositingContext) if (QOpenGLContext::globalShareContext() == m_rasterCompositingContext)
qt_gl_set_global_share_context(nullptr); qt_gl_set_global_share_context(nullptr);
delete m_rasterCompositingContext; delete m_rasterCompositingContext;
} else { } else {

View File

@ -1273,7 +1273,7 @@ void tst_QGuiApplication::globalShareContext()
int argc = 1; int argc = 1;
char *argv[] = { const_cast<char*>("tst_qguiapplication") }; char *argv[] = { const_cast<char*>("tst_qguiapplication") };
QScopedPointer<QGuiApplication> app(new QGuiApplication(argc, argv)); QScopedPointer<QGuiApplication> app(new QGuiApplication(argc, argv));
QOpenGLContext *ctx = qt_gl_global_share_context(); QOpenGLContext *ctx = QOpenGLContext::globalShareContext();
QVERIFY(ctx); QVERIFY(ctx);
app.reset(); app.reset();
ctx = qt_gl_global_share_context(); ctx = qt_gl_global_share_context();
@ -1282,8 +1282,16 @@ void tst_QGuiApplication::globalShareContext()
// Test that there is no global share context by default. // Test that there is no global share context by default.
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, false); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, false);
app.reset(new QGuiApplication(argc, argv)); app.reset(new QGuiApplication(argc, argv));
ctx = qt_gl_global_share_context(); ctx = QOpenGLContext::globalShareContext();
QVERIFY(!ctx); QVERIFY(!ctx);
// Test that setting AA_ShareOpenGLContexts can happen after creating
// QGuiApplication.
app.reset();
app.reset(new QGuiApplication(argc, argv));
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
ctx = QOpenGLContext::globalShareContext();
QVERIFY(ctx);
#else #else
QSKIP("No OpenGL support"); QSKIP("No OpenGL support");
#endif #endif