From 07f2ca775c70998c96d528c44911a5e008a2dae0 Mon Sep 17 00:00:00 2001 From: Mikolaj Boc Date: Mon, 28 Nov 2022 16:35:24 +0100 Subject: [PATCH] Quasi-support for offscreen surface on WASM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since context sharing is not currently supported with WebGL, offscreen contexts have a limited usability on Qt for WASM. If a context is shared, use the same underlaying WebGL context so that the two can actually share resources. This is not full-blown context sharing by any means but it makes e.g. the Open GL widget work as the readback texture for it is 'shared' between the virtual Qt contexts. If no sharing is desired, we use an OffscreenCanvas and actually create a separate WebGL context. Fixes: QTBUG-107558 Change-Id: If57e44739ddb57c167d5f8881a74d8dee52531f6 Reviewed-by: MikoĊ‚aj Boc (cherry picked from commit 0eea2238f37a5eaa83ada6cd262eceaed20e415c) Reviewed-by: Qt Cherry-pick Bot --- .../platforms/wasm/qwasmintegration.cpp | 2 +- .../platforms/wasm/qwasmoffscreensurface.cpp | 19 ++++++- .../platforms/wasm/qwasmoffscreensurface.h | 10 +++- .../platforms/wasm/qwasmopenglcontext.cpp | 55 +++++++++++-------- .../platforms/wasm/qwasmopenglcontext.h | 6 +- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 4c6f1b14c84..148810ba8bd 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -186,7 +186,7 @@ void QWasmIntegration::removeBackingStore(QWindow* window) #ifndef QT_NO_OPENGL QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - return new QWasmOpenGLContext(context->format()); + return new QWasmOpenGLContext(context); } #endif diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp index 31adc73cf5f..0191e0b2165 100644 --- a/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp +++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp @@ -6,10 +6,25 @@ QT_BEGIN_NAMESPACE QWasmOffscreenSurface::QWasmOffscreenSurface(QOffscreenSurface *offscreenSurface) - :QPlatformOffscreenSurface(offscreenSurface) + : QPlatformOffscreenSurface(offscreenSurface), m_offscreenCanvas(emscripten::val::undefined()) { + const auto offscreenCanvasClass = emscripten::val::global("OffscreenCanvas"); + // The OffscreenCanvas is not supported on some browsers, most notably on Safari. + if (!offscreenCanvasClass) + return; + + m_offscreenCanvas = offscreenCanvasClass.new_(offscreenSurface->size().width(), + offscreenSurface->size().height()); + + m_specialTargetId = std::string("!qtoffscreen_") + std::to_string(uintptr_t(this)); + + emscripten::val::module_property("specialHTMLTargets") + .set(m_specialTargetId, m_offscreenCanvas); } -QWasmOffscreenSurface::~QWasmOffscreenSurface() = default; +QWasmOffscreenSurface::~QWasmOffscreenSurface() +{ + emscripten::val::module_property("specialHTMLTargets").delete_(m_specialTargetId); +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.h b/src/plugins/platforms/wasm/qwasmoffscreensurface.h index 88a64b775a5..38a991f4cee 100644 --- a/src/plugins/platforms/wasm/qwasmoffscreensurface.h +++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.h @@ -6,6 +6,10 @@ #include +#include + +#include + QT_BEGIN_NAMESPACE class QOffscreenSurface; @@ -14,8 +18,12 @@ class QWasmOffscreenSurface final : public QPlatformOffscreenSurface public: explicit QWasmOffscreenSurface(QOffscreenSurface *offscreenSurface); ~QWasmOffscreenSurface() final; -private: + const std::string &id() const { return m_specialTargetId; } + +private: + std::string m_specialTargetId; + emscripten::val m_offscreenCanvas; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 3dfb201367a..80e842f83dc 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmopenglcontext.h" + +#include "qwasmoffscreensurface.h" #include "qwasmintegration.h" #include #include @@ -18,23 +20,22 @@ EMSCRIPTEN_BINDINGS(qwasmopenglcontext) QT_BEGIN_NAMESPACE -QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format) - : m_requestedFormat(format) +QWasmOpenGLContext::QWasmOpenGLContext(QOpenGLContext *context) + : m_requestedFormat(context->format()), m_qGlContext(context) { m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES); // if we set one, we need to set the other as well since in webgl, these are tied together - if (format.depthBufferSize() < 0 && format.stencilBufferSize() > 0) - m_requestedFormat.setDepthBufferSize(16); - - if (format.stencilBufferSize() < 0 && format.depthBufferSize() > 0) - m_requestedFormat.setStencilBufferSize(8); + if (m_requestedFormat.depthBufferSize() < 0 && m_requestedFormat.stencilBufferSize() > 0) + m_requestedFormat.setDepthBufferSize(16); + if (m_requestedFormat.stencilBufferSize() < 0 && m_requestedFormat.depthBufferSize() > 0) + m_requestedFormat.setStencilBufferSize(8); } QWasmOpenGLContext::~QWasmOpenGLContext() { - if (!m_context) + if (!m_webGLContext) return; // Destroy GL context. Work around bug in emscripten_webgl_destroy_context @@ -44,9 +45,9 @@ QWasmOpenGLContext::~QWasmOpenGLContext() emscripten::val savedRemoveAllHandlersOnTargetFunction = jsEvents["removeAllHandlersOnTarget"]; jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing")); - emscripten_webgl_destroy_context(m_context); + emscripten_webgl_destroy_context(m_webGLContext); jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction); - m_context = 0; + m_webGLContext = 0; } bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format) @@ -61,18 +62,28 @@ bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format) bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface) { - if (m_context && m_surface == surface) + if (m_webGLContext && m_surface == surface) return true; - // TODO(mikolajboc): Use OffscreenCanvas if available. - if (surface->surface()->surfaceClass() == QSurface::Offscreen) - return false; - m_surface = surface; - - auto *window = static_cast(surface); - m_context = createEmscriptenContext(window->canvasSelector(), m_requestedFormat); - return true; + 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(shareContext->handle())->m_webGLContext; + } 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(surface)->id(), m_requestedFormat); + } + } else { + m_webGLContext = createEmscriptenContext( + static_cast(surface)->canvasSelector(), m_requestedFormat); + } + return m_webGLContext > 0; } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE @@ -116,7 +127,7 @@ bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface) if (!maybeCreateEmscriptenContext(surface)) return false; - return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS; + return emscripten_webgl_make_context_current(m_webGLContext) == EMSCRIPTEN_RESULT_SUCCESS; } void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface) @@ -132,7 +143,7 @@ void QWasmOpenGLContext::doneCurrent() bool QWasmOpenGLContext::isSharing() const { - return false; + return m_qGlContext->shareContext(); } bool QWasmOpenGLContext::isValid() const @@ -142,7 +153,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_context || !emscripten_is_webgl_context_lost(m_context); + return !m_webGLContext || !emscripten_is_webgl_context_lost(m_webGLContext); } QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName) diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index ac456d90e4f..d51caf08b12 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -8,11 +8,12 @@ QT_BEGIN_NAMESPACE +class QOpenGLContext; class QPlatformScreen; class QWasmOpenGLContext : public QPlatformOpenGLContext { public: - QWasmOpenGLContext(const QSurfaceFormat &format); + explicit QWasmOpenGLContext(QOpenGLContext *context); ~QWasmOpenGLContext(); QSurfaceFormat format() const override; @@ -32,7 +33,8 @@ private: QSurfaceFormat m_requestedFormat; QPlatformSurface *m_surface = nullptr; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context = 0; + QOpenGLContext *m_qGlContext; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_webGLContext = 0; }; QT_END_NAMESPACE