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