diff options
author | Mikolaj Boc <[email protected]> | 2023-03-07 12:58:40 +0100 |
---|---|---|
committer | Mikolaj Boc <[email protected]> | 2023-03-12 16:00:02 +0100 |
commit | 68526277771f232bba0a02383cbacda382cc829d (patch) | |
tree | 75752906269e514c6d67fd21bafd4e3c1dc5aa89 /src | |
parent | cdbfaf1b65538a1cdaf4cf0def2ac7728f11c945 (diff) |
Reuse the existing canvas context for offscreen surfaces
Due to the nature of WebGL contexts, which cannot be reassigned to
targets other than they were created for, we will now reuse the created
canvas context for offscreen surfaces, since those (hopefully) mostly
operate on textures anyway. If this is not done, any switch to an
offscreen surface for the main RHI context invalidates resources on
contexts in a share group with it.
Fixes: QTBUG-111617
Change-Id: I9752f7eec396a3ef11414881f5f79f26e1e2c859
Reviewed-by: Morten Johan Sørvig <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmopenglcontext.cpp | 74 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmopenglcontext.h | 15 |
2 files changed, 60 insertions, 29 deletions
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 80e842f83dc..fe92f34eca9 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -35,19 +35,10 @@ QWasmOpenGLContext::QWasmOpenGLContext(QOpenGLContext *context) QWasmOpenGLContext::~QWasmOpenGLContext() { - if (!m_webGLContext) - return; - // Destroy GL context. Work around bug in emscripten_webgl_destroy_context // which removes all event handlers on the canvas by temporarily replacing the function // that does the removal with a function that does nothing. - emscripten::val jsEvents = emscripten::val::module_property("JSEvents"); - emscripten::val savedRemoveAllHandlersOnTargetFunction = - jsEvents["removeAllHandlersOnTarget"]; - jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing")); - emscripten_webgl_destroy_context(m_webGLContext); - jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction); - m_webGLContext = 0; + destroyWebGLContext(m_ownedWebGLContext.handle); } bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format) @@ -60,30 +51,58 @@ bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format) (format.majorVersion() == 3 && format.minorVersion() == 0)); } -bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface) +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE +QWasmOpenGLContext::obtainEmscriptenContext(QPlatformSurface *surface) { - if (m_webGLContext && m_surface == surface) - return true; + if (m_ownedWebGLContext.surface == surface) + return m_ownedWebGLContext.handle; - m_surface = surface; if (surface->surface()->surfaceClass() == QSurface::Offscreen) { if (const auto *shareContext = m_qGlContext->shareContext()) { // Since there are no resource sharing capabilities with WebGL whatsoever, we use the // same actual underlaying WebGL context. This is not perfect, but it works in most // cases. - m_webGLContext = - static_cast<QWasmOpenGLContext *>(shareContext->handle())->m_webGLContext; + return static_cast<QWasmOpenGLContext *>(shareContext->handle()) + ->m_ownedWebGLContext.handle; } else { - // The non-shared offscreen context is heavily limited on WASM, but we provide it anyway - // for potential pixel readbacks. - m_webGLContext = createEmscriptenContext( - static_cast<QWasmOffscreenSurface *>(surface)->id(), m_requestedFormat); + // Reuse the existing context for offscreen drawing, even if it happens to be a canvas + // context. This is because it is impossible to re-home an existing context to the + // new surface and works as an emulation measure. + if (m_ownedWebGLContext.handle) + return m_ownedWebGLContext.handle; + + // The non-shared offscreen context is heavily limited on WASM, but we provide it + // anyway for potential pixel readbacks. + m_ownedWebGLContext = + QOpenGLContextData{ .surface = surface, + .handle = createEmscriptenContext( + static_cast<QWasmOffscreenSurface *>(surface)->id(), + m_requestedFormat) }; + return m_ownedWebGLContext.handle; } } else { - m_webGLContext = createEmscriptenContext( - static_cast<QWasmWindow *>(surface)->canvasSelector(), m_requestedFormat); + destroyWebGLContext(m_ownedWebGLContext.handle); + + // Create a full on-screen context for the window canvas. + m_ownedWebGLContext = QOpenGLContextData{ + .surface = surface, + .handle = createEmscriptenContext(static_cast<QWasmWindow *>(surface)->canvasSelector(), + m_requestedFormat) + }; + + return m_ownedWebGLContext.handle; } - return m_webGLContext > 0; +} + +void QWasmOpenGLContext::destroyWebGLContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle) +{ + if (!contextHandle) + return; + emscripten::val jsEvents = emscripten::val::module_property("JSEvents"); + emscripten::val savedRemoveAllHandlersOnTargetFunction = jsEvents["removeAllHandlersOnTarget"]; + jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing")); + emscripten_webgl_destroy_context(contextHandle); + jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction); } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE @@ -124,10 +143,13 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface) { - if (!maybeCreateEmscriptenContext(surface)) + const auto context = obtainEmscriptenContext(surface); + if (!context) return false; - return emscripten_webgl_make_context_current(m_webGLContext) == EMSCRIPTEN_RESULT_SUCCESS; + m_usedWebGLContextHandle = context; + + return emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS; } void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface) @@ -153,7 +175,7 @@ bool QWasmOpenGLContext::isValid() const // Note: we get isValid() calls before we see the surface and can // create a native context, so no context is also a valid state. - return !m_webGLContext || !emscripten_is_webgl_context_lost(m_webGLContext); + return !m_usedWebGLContextHandle || !emscripten_is_webgl_context_lost(m_usedWebGLContextHandle); } QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName) diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index d51caf08b12..90863abdfe5 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -10,6 +10,7 @@ QT_BEGIN_NAMESPACE class QOpenGLContext; class QPlatformScreen; +class QPlatformSurface; class QWasmOpenGLContext : public QPlatformOpenGLContext { public: @@ -26,15 +27,23 @@ public: QFunctionPointer getProcAddress(const char *procName) override; private: + struct QOpenGLContextData + { + QPlatformSurface *surface = nullptr; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE handle = 0; + }; + static bool isOpenGLVersionSupported(QSurfaceFormat format); - bool maybeCreateEmscriptenContext(QPlatformSurface *surface); + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE obtainEmscriptenContext(QPlatformSurface *surface); static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const std::string &canvasSelector, QSurfaceFormat format); + static void destroyWebGLContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle); + QSurfaceFormat m_requestedFormat; - QPlatformSurface *m_surface = nullptr; QOpenGLContext *m_qGlContext; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_webGLContext = 0; + QOpenGLContextData m_ownedWebGLContext; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_usedWebGLContextHandle = 0; }; QT_END_NAMESPACE |