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
23QHash<QPlatformSurface *, EMSCRIPTEN_WEBGL_CONTEXT_HANDLE> QWasmOpenGLContext::s_contexts;
24
25QWasmOpenGLContext::QWasmOpenGLContext(QOpenGLContext *context)
27{
28 m_actualFormat.setRenderableType(QSurfaceFormat::OpenGLES);
29
30 // if we set one, we need to set the other as well since in webgl, these are tied together
31 if (m_actualFormat.depthBufferSize() < 0 && m_actualFormat.stencilBufferSize() > 0)
32 m_actualFormat.setDepthBufferSize(16);
33
34 if (m_actualFormat.stencilBufferSize() < 0 && m_actualFormat.depthBufferSize() > 0)
35 m_actualFormat.setStencilBufferSize(8);
36}
37
39{
40 destroyWebGLContext(m_contextOwningSurface);
41}
42
43bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format)
44{
45 // Version check: support WebGL 1 and 2:
46 // (ES) 2.0 -> WebGL 1.0
47 // (ES) 3.0 -> WebGL 2.0
48 // [we don't expect that new WebGL versions will be created]
49 return ((format.majorVersion() == 2 && format.minorVersion() == 0) ||
50 (format.majorVersion() == 3 && format.minorVersion() == 0));
51}
52
53void QWasmOpenGLContext::destroyWebGLContext(QPlatformSurface *surface)
54{
55 if (surface == nullptr)
56 return;
57 int context = s_contexts.take(surface);
58 if (context)
59 destroyWebGLContext(context);
60}
61
62void QWasmOpenGLContext::destroyWebGLContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE contextHandle)
63{
64 if (!contextHandle)
65 return;
66
67 // Destroy GL context. Work around bug in emscripten_webgl_destroy_context
68 // which removes all event handlers on the canvas by temporarily replacing the function
69 // that does the removal with a function that does nothing.
70 emscripten::val jsEvents = emscripten::val::module_property("JSEvents");
71 emscripten::val savedRemoveAllHandlersOnTargetFunction = jsEvents["removeAllHandlersOnTarget"];
72 jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing"));
73 emscripten_webgl_destroy_context(contextHandle);
74 jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction);
75}
76
77EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const std::string &canvasSelector,
78 QSurfaceFormat format)
79{
80 EmscriptenWebGLContextAttributes attributes;
81 emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
82
83 attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE;
84 attributes.failIfMajorPerformanceCaveat = false;
85 attributes.antialias = true;
86 attributes.enableExtensionsByDefault = true;
87 attributes.majorVersion = 2; // try highest supported version ES3.0 / WebGL 2.0
88 attributes.minorVersion = 0; // emscripten only supports minor version 0
89 // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
90 // we need both or none
91 const bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
92
93 // WebGL offers enable/disable control but not size control for these
94 attributes.alpha = format.alphaBufferSize() > 0;
95 attributes.depth = useDepthStencil;
96 attributes.stencil = useDepthStencil;
97 EMSCRIPTEN_RESULT contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes);
98
99 if (contextResult <= 0) {
100 // fallback to opengles2/webgl1
101 // for devices that do not support opengles3/webgl2
102 attributes.majorVersion = 1;
103 contextResult = emscripten_webgl_create_context(canvasSelector.c_str(), &attributes);
104 }
105
106 if (contextResult <= 0) {
107 qWarning() << "WebGL context creation failed";
108 return contextResult;
109 }
110
111 // Sync up actual format
112 EmscriptenWebGLContextAttributes actualAttributes;
113 EMSCRIPTEN_RESULT attributesResult = emscripten_webgl_get_context_attributes(contextResult, &actualAttributes);
114 if (attributesResult == EMSCRIPTEN_RESULT_SUCCESS) {
115 if (actualAttributes.majorVersion == 1) {
116 m_actualFormat.setMajorVersion(2);
117 } else if (actualAttributes.majorVersion == 2) {
118 m_actualFormat.setMajorVersion(3);
119 }
120 m_actualFormat.setMinorVersion(0);
121 }
122
123 return contextResult;
124}
125
127{
128 return m_actualFormat;
129}
130
131GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
132{
133 return QPlatformOpenGLContext::defaultFramebufferObject(surface);
134}
135
136bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
137{
138 // Record this makeCurrent() attempt, since isValid() must repeat the answer
139 // from this function in order to signal context loss to calling code.
140 m_madeCurrentSurface = surface;
141
142 // The native webgl context is tied to a single surface, and can't
143 // be made current for a different surface.
144 if (m_contextOwningSurface && m_contextOwningSurface != surface)
145 return false;
146
147 // Return existing context or crate a new one.
148 EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
149 if (m_contextOwningSurface && s_contexts.contains(m_contextOwningSurface)) {
150 context = s_contexts.value(surface);
151 } else {
152 m_contextOwningSurface = surface;
153 bool isOffscreen = surface->surface()->surfaceClass() == QSurface::Offscreen;
154 auto canvasId = isOffscreen ? static_cast<QWasmOffscreenSurface *>(surface)->id() :
155 static_cast<QWasmWindow *>(surface)->canvasSelector();
156
157 context = createEmscriptenContext(canvasId, m_actualFormat);
158 s_contexts.insert(surface, context);
159 }
160
161 if (context == 0)
162 return false;
163
164 return emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS;
165}
166
167void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface)
168{
169 Q_UNUSED(surface);
170 // No swapbuffers on WebGl
171}
172
174{
175 m_madeCurrentSurface = nullptr;
176}
177
179{
180 // Return false to signal that context sharing is not supported.
181 // This will in turn make QOpenGLContext::shareContext() return
182 // a null context to the application.
183 return false;
184}
185
187{
188 if (!isOpenGLVersionSupported(m_actualFormat))
189 return false;
190
191 // We get isValid() calls before we see the surface and are able to
192 // create a native context, which means that "no context" is a valid state.
193 if (!m_madeCurrentSurface && !m_contextOwningSurface)
194 return true;
195
196 // Can't use this context for a different surface, since the native
197 // webgl context is tied to a single canvas.
198 if (m_madeCurrentSurface != m_contextOwningSurface)
199 return false;
200
201 // If the owning surfce/canvas has been deleted then this context is invalid
202 if (!s_contexts.contains(m_contextOwningSurface))
203 return false;
204
205 return !emscripten_is_webgl_context_lost(s_contexts.value(m_contextOwningSurface));
206}
207
209{
210 return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
211}
212
213QT_END_NAMESPACE
static void destroyWebGLContext(QPlatformSurface *surface)
bool isSharing() const override
bool isValid() const override
void doneCurrent() override
QWasmOpenGLContext(QOpenGLContext *context)
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)