1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qwasmopenglcontext.h"
31 #include "qwasmintegration.h"
32 #include <EGL/egl.h>
33 #include <emscripten/val.h>
34 
35 QT_BEGIN_NAMESPACE
36 
QWasmOpenGLContext(const QSurfaceFormat & format)37 QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format)
38     : m_requestedFormat(format)
39 {
40     m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES);
41 
42     // if we set one, we need to set the other as well since in webgl, these are tied together
43     if (format.depthBufferSize() < 0 && format.stencilBufferSize() > 0)
44        m_requestedFormat.setDepthBufferSize(16);
45 
46    if (format.stencilBufferSize() < 0 && format.depthBufferSize() > 0)
47        m_requestedFormat.setStencilBufferSize(8);
48 
49 }
50 
~QWasmOpenGLContext()51 QWasmOpenGLContext::~QWasmOpenGLContext()
52 {
53     if (m_context) {
54         // Destroy GL context. Work around bug in emscripten_webgl_destroy_context
55         // which removes all event handlers on the canvas by temporarily removing
56         // emscripten's JSEvents global object.
57         emscripten::val jsEvents = emscripten::val::global("window")["JSEvents"];
58         emscripten::val::global("window").set("JSEvents", emscripten::val::undefined());
59         emscripten_webgl_destroy_context(m_context);
60         emscripten::val::global("window").set("JSEvents", jsEvents);
61         m_context = 0;
62     }
63 }
64 
isOpenGLVersionSupported(QSurfaceFormat format)65 bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format)
66 {
67     // Version check: support WebGL 1 and 2:
68     // (ES) 2.0 -> WebGL 1.0
69     // (ES) 3.0 -> WebGL 2.0
70     // [we don't expect that new WebGL versions will be created]
71     return ((format.majorVersion() == 2 && format.minorVersion() == 0) ||
72            (format.majorVersion() == 3 && format.minorVersion() == 0));
73 }
74 
maybeCreateEmscriptenContext(QPlatformSurface * surface)75 bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface)
76 {
77     // Native emscripten/WebGL contexts are tied to a single screen/canvas. The first
78     // call to this function creates a native canvas for the given screen, subsequent
79     // calls verify that the surface is on/off the same screen.
80     QPlatformScreen *screen = surface->screen();
81     if (m_context && !screen)
82         return false; // Alternative: return true to support makeCurrent on QOffScreenSurface with
83                       // no screen. However, Qt likes to substitute QGuiApplication::primaryScreen()
84                       // for null screens, which foils this plan.
85     if (!screen)
86         return false;
87     if (m_context)
88         return m_screen == screen;
89 
90     QString canvasId = QWasmScreen::get(screen)->canvasId();
91     m_context = createEmscriptenContext(canvasId, m_requestedFormat);
92     m_screen = screen;
93     return true;
94 }
95 
createEmscriptenContext(const QString & canvasId,QSurfaceFormat format)96 EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format)
97 {
98     EmscriptenWebGLContextAttributes attributes;
99     emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
100 
101     attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE;
102     attributes.failIfMajorPerformanceCaveat = false;
103     attributes.antialias = true;
104     attributes.enableExtensionsByDefault = true;
105     attributes.majorVersion = format.majorVersion() - 1;
106     attributes.minorVersion = format.minorVersion();
107 
108     // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
109     // we need both or none
110     bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
111 
112     // WebGL offers enable/disable control but not size control for these
113     attributes.alpha = format.alphaBufferSize() > 0;
114     attributes.depth = useDepthStencil;
115     attributes.stencil = useDepthStencil;
116 
117     QByteArray convasSelector = "#" + canvasId.toUtf8();
118     EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(convasSelector.constData(), &attributes);
119 
120     return context;
121 }
122 
format() const123 QSurfaceFormat QWasmOpenGLContext::format() const
124 {
125     return m_requestedFormat;
126 }
127 
defaultFramebufferObject(QPlatformSurface * surface) const128 GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
129 {
130     return QPlatformOpenGLContext::defaultFramebufferObject(surface);
131 }
132 
makeCurrent(QPlatformSurface * surface)133 bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
134 {
135     bool ok = maybeCreateEmscriptenContext(surface);
136     if (!ok)
137         return false;
138 
139     return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS;
140 }
141 
swapBuffers(QPlatformSurface * surface)142 void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface)
143 {
144     Q_UNUSED(surface);
145     // No swapbuffers on WebGl
146 }
147 
doneCurrent()148 void QWasmOpenGLContext::doneCurrent()
149 {
150     // No doneCurrent on WebGl
151 }
152 
isSharing() const153 bool QWasmOpenGLContext::isSharing() const
154 {
155     return false;
156 }
157 
isValid() const158 bool QWasmOpenGLContext::isValid() const
159 {
160     if (!(isOpenGLVersionSupported(m_requestedFormat)))
161         return false;
162 
163     // Note: we get isValid() calls before we see the surface and can
164     // create a native context, so no context is also a valid state.
165     return !m_context || !emscripten_is_webgl_context_lost(m_context);
166 }
167 
getProcAddress(const char * procName)168 QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
169 {
170     return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
171 }
172 
173 QT_END_NAMESPACE
174