Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwasmopenglcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
7#include "qwasmintegration.h"
8#include <EGL/egl.h>
9#include <emscripten/bind.h>
10#include <emscripten/val.h>
11
12namespace {
13void qtDoNothing(emscripten::val) { }
14} // namespace
15
16EMSCRIPTEN_BINDINGS(qwasmopenglcontext)
17{
18 function("qtDoNothing", &qtDoNothing);
19}
20
22
24 : m_requestedFormat(context->format()), m_qGlContext(context)
25{
27
28 // if we set one, we need to set the other as well since in webgl, these are tied together
29 if (m_requestedFormat.depthBufferSize() < 0 && m_requestedFormat.stencilBufferSize() > 0)
30 m_requestedFormat.setDepthBufferSize(16);
31
32 if (m_requestedFormat.stencilBufferSize() < 0 && m_requestedFormat.depthBufferSize() > 0)
33 m_requestedFormat.setStencilBufferSize(8);
34}
35
37{
38 // Destroy GL context. Work around bug in emscripten_webgl_destroy_context
39 // which removes all event handlers on the canvas by temporarily replacing the function
40 // that does the removal with a function that does nothing.
41 destroyWebGLContext(m_ownedWebGLContext.handle);
42}
43
44bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format)
45{
46 // Version check: support WebGL 1 and 2:
47 // (ES) 2.0 -> WebGL 1.0
48 // (ES) 3.0 -> WebGL 2.0
49 // [we don't expect that new WebGL versions will be created]
50 return ((format.majorVersion() == 2 && format.minorVersion() == 0) ||
51 (format.majorVersion() == 3 && format.minorVersion() == 0));
52}
53
54EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
55QWasmOpenGLContext::obtainEmscriptenContext(QPlatformSurface *surface)
56{
57 if (m_ownedWebGLContext.surface == surface)
58 return m_ownedWebGLContext.handle;
59
60 if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
61 if (const auto *shareContext = m_qGlContext->shareContext()) {
62 // Since there are no resource sharing capabilities with WebGL whatsoever, we use the
63 // same actual underlaying WebGL context. This is not perfect, but it works in most
64 // cases.
65 return static_cast<QWasmOpenGLContext *>(shareContext->handle())
66 ->m_ownedWebGLContext.handle;
67 } else {
68 // Reuse the existing context for offscreen drawing, even if it happens to be a canvas
69 // context. This is because it is impossible to re-home an existing context to the
70 // new surface and works as an emulation measure.
71 if (m_ownedWebGLContext.handle)
72 return m_ownedWebGLContext.handle;
73
74 // The non-shared offscreen context is heavily limited on WASM, but we provide it
75 // anyway for potential pixel readbacks.
76 m_ownedWebGLContext =
77 QOpenGLContextData{ .surface = surface,
78 .handle = createEmscriptenContext(
79 static_cast<QWasmOffscreenSurface *>(surface)->id(),
80 m_requestedFormat) };
81 return m_ownedWebGLContext.handle;
82 }
83 } else {
84 destroyWebGLContext(m_ownedWebGLContext.handle);
85
86 // Create a full on-screen context for the window canvas.
87 m_ownedWebGLContext = QOpenGLContextData{
88 .surface = surface,
89 .handle = createEmscriptenContext(static_cast<QWasmWindow *>(surface)->canvasSelector(),
90 m_requestedFormat)
91 };
92
93 return m_ownedWebGLContext.handle;
94 }
95}
96
97void QWasmOpenGLContext::destroyWebGLContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle)
98{
99 if (!contextHandle)
100 return;
101 emscripten::val jsEvents = emscripten::val::module_property("JSEvents");
102 emscripten::val savedRemoveAllHandlersOnTargetFunction = jsEvents["removeAllHandlersOnTarget"];
103 jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing"));
104 emscripten_webgl_destroy_context(contextHandle);
105 jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction);
106}
107
108EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
109QWasmOpenGLContext::createEmscriptenContext(const std::string &canvasSelector,
111{
112 EmscriptenWebGLContextAttributes attributes;
113 emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
114
115 attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE;
116 attributes.failIfMajorPerformanceCaveat = false;
117 attributes.antialias = true;
118 attributes.enableExtensionsByDefault = true;
119 attributes.majorVersion = format.majorVersion() - 1;
120 attributes.minorVersion = format.minorVersion();
121
122 // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
123 // we need both or none
124 const bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
125
126 // WebGL offers enable/disable control but not size control for these
127 attributes.alpha = format.alphaBufferSize() > 0;
128 attributes.depth = useDepthStencil;
129 attributes.stencil = useDepthStencil;
130
131 return emscripten_webgl_create_context(canvasSelector.c_str(), &attributes);
132}
133
135{
136 return m_requestedFormat;
137}
138
140{
142}
143
145{
146 const auto context = obtainEmscriptenContext(surface);
147 if (!context)
148 return false;
149
150 m_usedWebGLContextHandle = context;
151
152 return emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS;
153}
154
156{
157 Q_UNUSED(surface);
158 // No swapbuffers on WebGl
159}
160
162{
163 // No doneCurrent on WebGl
164}
165
167{
168 return m_qGlContext->shareContext();
169}
170
172{
173 if (!isOpenGLVersionSupported(m_requestedFormat))
174 return false;
175
176 // Note: we get isValid() calls before we see the surface and can
177 // create a native context, so no context is also a valid state.
178 return !m_usedWebGLContextHandle || !emscripten_is_webgl_context_lost(m_usedWebGLContextHandle);
179}
180
181QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
182{
183 return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
184}
185
\inmodule QtGui
QOpenGLContext * shareContext() const
Returns the share context this context was created with.
QOpenGLContext * context() const
virtual GLuint defaultFramebufferObject(QPlatformSurface *surface) const
Reimplement in subclass if your platform uses framebuffer objects for surfaces.
The QPlatformSurface class provides an abstraction for a surface.
QSurface * surface() const
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
void setRenderableType(RenderableType type)
Sets the desired renderable type.
void setDepthBufferSize(int size)
Set the minimum depth buffer size to size.
int stencilBufferSize() const
Returns the stencil buffer size in bits.
void setStencilBufferSize(int size)
Set the preferred stencil buffer size to size bits.
int depthBufferSize() const
Returns the depth buffer size.
SurfaceClass surfaceClass() const
Returns the surface class of this surface.
Definition qsurface.cpp:121
@ Offscreen
Definition qsurface.h:26
bool isSharing() const override
bool isValid() const override
void doneCurrent() override
void swapBuffers(QPlatformSurface *surface) override
Reimplement in subclass to native swap buffers calls.
bool makeCurrent(QPlatformSurface *surface) override
QSurfaceFormat format() const override
QFunctionPointer getProcAddress(const char *procName) override
Reimplement in subclass to allow dynamic querying of OpenGL symbols.
QWasmOpenGLContext(QOpenGLContext *context)
GLuint defaultFramebufferObject(QPlatformSurface *surface) const override
Reimplement in subclass if your platform uses framebuffer objects for surfaces.
Combined button and popup list for selecting options.
static void * context
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
GLint GLsizei GLsizei GLenum format
#define GLuint
#define Q_UNUSED(x)
EMSCRIPTEN_BINDINGS(qwasmopenglcontext)