Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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_actualFormat(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_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,
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
152
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
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
199QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
200{
201 return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
202}
203
\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 setMinorVersion(int minorVersion)
Sets the desired minor OpenGL version.
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.
void setMajorVersion(int majorVersion)
Sets the desired major OpenGL version.
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
#define qWarning
Definition qlogging.h:166
GLint GLsizei GLsizei GLenum format
#define GLuint
#define Q_UNUSED(x)
EMSCRIPTEN_BINDINGS(qwasmopenglcontext)