diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmopenglcontext.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmopenglcontext.cpp | 128 |
1 files changed, 84 insertions, 44 deletions
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 80e842f83d..8a4664ec8c 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -21,33 +21,24 @@ EMSCRIPTEN_BINDINGS(qwasmopenglcontext) QT_BEGIN_NAMESPACE QWasmOpenGLContext::QWasmOpenGLContext(QOpenGLContext *context) - : m_requestedFormat(context->format()), m_qGlContext(context) + : m_actualFormat(context->format()), m_qGlContext(context) { - m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES); + m_actualFormat.setRenderableType(QSurfaceFormat::OpenGLES); // if we set one, we need to set the other as well since in webgl, these are tied together - if (m_requestedFormat.depthBufferSize() < 0 && m_requestedFormat.stencilBufferSize() > 0) - m_requestedFormat.setDepthBufferSize(16); + if (m_actualFormat.depthBufferSize() < 0 && m_actualFormat.stencilBufferSize() > 0) + m_actualFormat.setDepthBufferSize(16); - if (m_requestedFormat.stencilBufferSize() < 0 && m_requestedFormat.depthBufferSize() > 0) - m_requestedFormat.setStencilBufferSize(8); + if (m_actualFormat.stencilBufferSize() < 0 && m_actualFormat.depthBufferSize() > 0) + m_actualFormat.setStencilBufferSize(8); } 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,61 @@ 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; - } 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_actualFormat) }; } 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_actualFormat) + }; } - return m_webGLContext > 0; + + EmscriptenWebGLContextAttributes actualAttributes; + + EMSCRIPTEN_RESULT attributesResult = emscripten_webgl_get_context_attributes(m_ownedWebGLContext.handle, &actualAttributes); + if (attributesResult == EMSCRIPTEN_RESULT_SUCCESS) { + if (actualAttributes.majorVersion == 1) { + m_actualFormat.setMajorVersion(2); + } else if (actualAttributes.majorVersion == 2) { + m_actualFormat.setMajorVersion(3); + } + m_actualFormat.setMinorVersion(0); + } + + return m_ownedWebGLContext.handle; +} + +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 @@ -97,9 +119,8 @@ QWasmOpenGLContext::createEmscriptenContext(const std::string &canvasSelector, attributes.failIfMajorPerformanceCaveat = false; attributes.antialias = true; attributes.enableExtensionsByDefault = true; - attributes.majorVersion = format.majorVersion() - 1; - attributes.minorVersion = format.minorVersion(); - + attributes.majorVersion = 2; // try highest supported version ES3.0 / WebGL 2.0 + attributes.minorVersion = 0; // emscripten only supports minor version 0 // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT // we need both or none const bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0); @@ -108,13 +129,20 @@ QWasmOpenGLContext::createEmscriptenContext(const std::string &canvasSelector, attributes.alpha = format.alphaBufferSize() > 0; attributes.depth = useDepthStencil; attributes.stencil = useDepthStencil; + EMSCRIPTEN_RESULT contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes); - return emscripten_webgl_create_context(canvasSelector.c_str(), &attributes); + if (contextResult <= 0) { + // fallback to opengles2/webgl1 + // for devices that do not support opengles3/webgl2 + attributes.majorVersion = 1; + contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes); + } + return contextResult; } QSurfaceFormat QWasmOpenGLContext::format() const { - return m_requestedFormat; + return m_actualFormat; } GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const @@ -124,10 +152,22 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface) { - if (!maybeCreateEmscriptenContext(surface)) + static bool sentSharingWarning = false; + if (!sentSharingWarning && isSharing()) { + qWarning() << "The functionality for sharing OpenGL contexts is limited, see documentation"; + sentSharingWarning = true; + } + + if (auto *shareContext = m_qGlContext->shareContext()) + return shareContext->makeCurrent(surface->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) @@ -148,12 +188,12 @@ bool QWasmOpenGLContext::isSharing() const bool QWasmOpenGLContext::isValid() const { - if (!isOpenGLVersionSupported(m_requestedFormat)) + if (!isOpenGLVersionSupported(m_actualFormat)) return false; // 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) |