Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
8#include <EGL/egl.h>
9#include <emscripten/bind.h>
10#include <emscripten/val.h>
11
12namespace {
13void qtDoNothing(emscripten::val) { }
14} // namespace
15
17{
18 function("qtDoNothing", &qtDoNothing);
19}
20
21QT_BEGIN_NAMESPACE
22
23QWasmOpenGLContext::QWasmOpenGLContext(QOpenGLContext *context)
24 : m_actualFormat(context->format()), m_qGlContext(context)
25{
26 m_actualFormat.setRenderableType(QSurfaceFormat::OpenGLES);
27
28 // if we set one, we need to set the other as well since in webgl, these are tied together
29 if (m_actualFormat.depthBufferSize() < 0 && m_actualFormat.stencilBufferSize() > 0)
30 m_actualFormat.setDepthBufferSize(16);
31
32 if (m_actualFormat.stencilBufferSize() < 0 && m_actualFormat.depthBufferSize() > 0)
33 m_actualFormat.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 // Reuse the existing context for offscreen drawing, even if it happens to be a canvas
62 // context. This is because it is impossible to re-home an existing context to the
63 // new surface and works as an emulation measure.
64 if (m_ownedWebGLContext.handle)
65 return m_ownedWebGLContext.handle;
66
67 // The non-shared offscreen context is heavily limited on WASM, but we provide it
68 // anyway for potential pixel readbacks.
69 m_ownedWebGLContext =
70 QOpenGLContextData{ .surface = surface,
71 .handle = createEmscriptenContext(
72 static_cast<QWasmOffscreenSurface *>(surface)->id(),
73 m_actualFormat) };
74 } else {
75 destroyWebGLContext(m_ownedWebGLContext.handle);
76
77 // Create a full on-screen context for the window canvas.
78 m_ownedWebGLContext = QOpenGLContextData{
79 .surface = surface,
80 .handle = createEmscriptenContext(static_cast<QWasmWindow *>(surface)->canvasSelector(),
81 m_actualFormat)
82 };
83 }
84
85 EmscriptenWebGLContextAttributes actualAttributes;
86
87 EMSCRIPTEN_RESULT attributesResult = emscripten_webgl_get_context_attributes(m_ownedWebGLContext.handle, &actualAttributes);
88 if (attributesResult == EMSCRIPTEN_RESULT_SUCCESS) {
89 if (actualAttributes.majorVersion == 1) {
90 m_actualFormat.setMajorVersion(2);
91 } else if (actualAttributes.majorVersion == 2) {
92 m_actualFormat.setMajorVersion(3);
93 }
94 m_actualFormat.setMinorVersion(0);
95 }
96
97 return m_ownedWebGLContext.handle;
98}
99
100void QWasmOpenGLContext::destroyWebGLContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle)
101{
102 if (!contextHandle)
103 return;
104 emscripten::val jsEvents = emscripten::val::module_property("JSEvents");
105 emscripten::val savedRemoveAllHandlersOnTargetFunction = jsEvents["removeAllHandlersOnTarget"];
106 jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing"));
107 emscripten_webgl_destroy_context(contextHandle);
108 jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction);
109}
110
111EMSCRIPTEN_WEBGL_CONTEXT_HANDLE
112QWasmOpenGLContext::createEmscriptenContext(const std::string &canvasSelector,
113 QSurfaceFormat format)
114{
115 EmscriptenWebGLContextAttributes attributes;
116 emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
117
118 attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE;
119 attributes.failIfMajorPerformanceCaveat = false;
120 attributes.antialias = true;
121 attributes.enableExtensionsByDefault = true;
122 attributes.majorVersion = 2; // try highest supported version ES3.0 / WebGL 2.0
123 attributes.minorVersion = 0; // emscripten only supports minor version 0
124 // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
125 // we need both or none
126 const bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
127
128 // WebGL offers enable/disable control but not size control for these
129 attributes.alpha = format.alphaBufferSize() > 0;
130 attributes.depth = useDepthStencil;
131 attributes.stencil = useDepthStencil;
132 EMSCRIPTEN_RESULT contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes);
133
134 if (contextResult <= 0) {
135 // fallback to opengles2/webgl1
136 // for devices that do not support opengles3/webgl2
137 attributes.majorVersion = 1;
138 contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes);
139 }
140 return contextResult;
141}
142
144{
145 return m_actualFormat;
146}
147
148GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
149{
150 return QPlatformOpenGLContext::defaultFramebufferObject(surface);
151}
152
153bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
154{
155 static bool sentSharingWarning = false;
156 if (!sentSharingWarning && isSharing()) {
157 qWarning() << "The functionality for sharing OpenGL contexts is limited, see documentation";
158 sentSharingWarning = true;
159 }
160
161 if (auto *shareContext = m_qGlContext->shareContext())
162 return shareContext->makeCurrent(surface->surface());
163
164 const auto context = obtainEmscriptenContext(surface);
165 if (!context)
166 return false;
167
168 m_usedWebGLContextHandle = context;
169
170 return emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS;
171}
172
173void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface)
174{
175 Q_UNUSED(surface);
176 // No swapbuffers on WebGl
177}
178
180{
181 // No doneCurrent on WebGl
182}
183
185{
186 return m_qGlContext->shareContext();
187}
188
190{
191 if (!isOpenGLVersionSupported(m_actualFormat))
192 return false;
193
194 // Note: we get isValid() calls before we see the surface and can
195 // create a native context, so no context is also a valid state.
196 return !m_usedWebGLContextHandle || !emscripten_is_webgl_context_lost(m_usedWebGLContextHandle);
197}
198
200{
201 return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
202}
203
204QT_END_NAMESPACE
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.
GLuint defaultFramebufferObject(QPlatformSurface *surface) const override
Reimplement in subclass if your platform uses framebuffer objects for surfaces.
EMSCRIPTEN_BINDINGS(qwasmopenglcontext)