1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt WebGL module 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 "qwebglcontext.h"
31
32#include "qwebglfunctioncall.h"
33#include "qwebglintegration.h"
34#include "qwebglintegration_p.h"
35#include "qwebglwebsocketserver.h"
36#include "qwebglwindow.h"
37#include "qwebglwindow_p.h"
38
39#include <QtCore/qhash.h>
40#include <QtCore/qpair.h>
41#include <QtCore/qrect.h>
42#include <QtCore/qset.h>
43#include <QtGui/private/qguiapplication_p.h>
44#include <QtGui/private/qopenglcontext_p.h>
45#include <QtGui/qguiapplication.h>
46#include <QtGui/qimage.h>
47#include <QtGui/qopenglcontext.h>
48#include <QtGui/qsurface.h>
49#include <QtWebSockets/qwebsocket.h>
50
51#include <cstring>
52#include <limits>
53
54QT_BEGIN_NAMESPACE
55
56static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.context")
57
58class QWebGLContextPrivate
59{
60public:
61 static QAtomicInt nextId;
62 static QSet<int> waitingIds;
63 union { int id = -1; qintptr padded; };
64 QPlatformSurface *currentSurface = nullptr;
65 QSurfaceFormat surfaceFormat;
66};
67
68QAtomicInt QWebGLContextPrivate::nextId(1);
69QSet<int> QWebGLContextPrivate::waitingIds;
70
71struct PixelStorageModes
72{
73 PixelStorageModes() : unpackAlignment(4) { }
74 int unpackAlignment;
75};
76
77struct ContextData {
78 GLuint currentProgram = 0;
79 GLuint boundArrayBuffer = 0;
80 GLuint boundElementArrayBuffer = 0;
81 GLuint boundTexture2D = 0;
82 GLenum activeTextureUnit = GL_TEXTURE0;
83 GLuint boundDrawFramebuffer = 0;
84// GLuint boundReadFramebuffer = 0;
85 GLuint unpackAlignment = 4;
86 struct VertexAttrib {
87 VertexAttrib() : arrayBufferBinding(0), pointer(nullptr), enabled(false) { }
88 GLuint arrayBufferBinding;
89 const void *pointer;
90 bool enabled;
91 GLint size;
92 GLenum type;
93 bool normalized;
94 GLsizei stride;
95 };
96 QHash<GLuint, VertexAttrib> vertexAttribPointers;
97 QHash<GLuint, QImage> images;
98 PixelStorageModes pixelStorage;
99 QMap<GLenum, QVariant> cachedParameters;
100 QSet<QByteArray> stringCache;
101};
102
103static QHash<int, ContextData> s_contextData;
104
105QWebGLContext *currentContext()
106{
107 auto context = QOpenGLContext::currentContext();
108 if (context)
109 return static_cast<QWebGLContext *>(context->handle());
110 return nullptr;
111}
112
113ContextData *currentContextData()
114{
115 auto context = currentContext();
116 if (context)
117 return &s_contextData[context->id()];
118 return nullptr;
119}
120
121inline int imageSize(GLsizei width, GLsizei height, GLenum format, GLenum type,
122 const PixelStorageModes &pixelStorage)
123{
124 Q_UNUSED(pixelStorage); // TODO: Support different pixelStorage formats
125
126 static struct BppTabEntry {
127 GLenum format;
128 GLenum type;
129 int bytesPerPixel;
130 } bppTab[] = {
131 { GL_RGBA, GL_UNSIGNED_BYTE, .bytesPerPixel: 4 },
132 { GL_RGBA, GL_BYTE, .bytesPerPixel: 4 },
133 { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, .bytesPerPixel: 2 },
134 { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, .bytesPerPixel: 2 },
135 { GL_RGBA, GL_FLOAT, .bytesPerPixel: 16 },
136 { GL_RGB, GL_UNSIGNED_BYTE, .bytesPerPixel: 3 },
137 { GL_RGB, GL_BYTE, .bytesPerPixel: 3 },
138 { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, .bytesPerPixel: 2 },
139 { GL_RGB, GL_FLOAT, .bytesPerPixel: 12 },
140
141 { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, .bytesPerPixel: 2 },
142 { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, .bytesPerPixel: 4 },
143 { GL_DEPTH_COMPONENT, GL_FLOAT, .bytesPerPixel: 4 },
144
145 { GL_RGBA, GL_UNSIGNED_BYTE, .bytesPerPixel: 4 },
146 { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, .bytesPerPixel: 2 },
147 { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, .bytesPerPixel: 2 },
148 { GL_RGB, GL_UNSIGNED_BYTE, .bytesPerPixel: 3 },
149 { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, .bytesPerPixel: 2 },
150 { GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, .bytesPerPixel: 2 },
151 { GL_LUMINANCE, GL_UNSIGNED_BYTE, .bytesPerPixel: 1 },
152 { GL_ALPHA, GL_UNSIGNED_BYTE, .bytesPerPixel: 1 },
153
154 { GL_BGRA_EXT, GL_UNSIGNED_BYTE, .bytesPerPixel: 4 },
155 { GL_BGRA_EXT, GL_BYTE, .bytesPerPixel: 4 },
156 { GL_BGRA_EXT, GL_UNSIGNED_SHORT_4_4_4_4, .bytesPerPixel: 2 },
157 { GL_BGRA_EXT, GL_UNSIGNED_SHORT_5_5_5_1, .bytesPerPixel: 2 },
158 { GL_BGRA_EXT, GL_FLOAT, .bytesPerPixel: 16 }
159 };
160
161 int bytesPerPixel = 0;
162 for (size_t i = 0; i < sizeof(bppTab) / sizeof(BppTabEntry); ++i) {
163 if (bppTab[i].format == format && bppTab[i].type == type) {
164 bytesPerPixel = bppTab[i].bytesPerPixel;
165 break;
166 }
167 }
168
169 const int rowSize = width * bytesPerPixel;
170 if (!bytesPerPixel)
171 qCWarning(lc, "Unknown texture format %x - %x", format, type);
172
173 return rowSize * height;
174}
175
176static void lockMutex()
177{
178 QWebGLIntegrationPrivate::instance()->webSocketServer->mutex()->lock();
179}
180
181static void waitCondition(unsigned long time = ULONG_MAX)
182{
183 auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex();
184 auto waitCondition = QWebGLIntegrationPrivate::instance()->webSocketServer->waitCondition();
185 waitCondition->wait(lockedMutex: mutex, time);
186}
187
188static void unlockMutex()
189{
190 auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex();
191 mutex->unlock();
192}
193
194static int elementSize(GLenum type)
195{
196 switch (type) {
197 case GL_SHORT:
198 case GL_UNSIGNED_SHORT:
199 return 2;
200 case GL_FLOAT:
201 case GL_FIXED:
202 case GL_INT:
203 case GL_UNSIGNED_INT:
204 return 4;
205 default:
206 return 1;
207 }
208}
209
210static int vertexSize(GLint elementsPerVertex, GLenum type)
211{
212 return elementSize(type) * elementsPerVertex;
213}
214
215static int bufferSize(GLsizei count, GLint elemsPerVertex, GLenum type, GLsizei stride)
216{
217 if (count == 0)
218 return 0;
219
220 int vsize = vertexSize(elementsPerVertex: elemsPerVertex, type);
221
222 if (stride == 0)
223 stride = vsize;
224
225 return vsize + (count - 1) * stride;
226}
227
228static void setVertexAttribs(QWebGLFunctionCall *event, GLsizei count)
229{
230 event->addInt(value: currentContextData()->vertexAttribPointers.count());
231 const auto &vertexAttribPointers = currentContextData()->vertexAttribPointers;
232 for (auto it = vertexAttribPointers.cbegin(), end = vertexAttribPointers.cend(); it != end; ++it) {
233 const ContextData::VertexAttrib &va(it.value());
234 if (va.arrayBufferBinding == 0 && va.enabled) {
235 int len = bufferSize(count, elemsPerVertex: va.size, type: va.type, stride: va.stride);
236 event->addParameters(arguments: it.key(), arguments: va.size, arguments: int(va.type), arguments: va.normalized, arguments: va.stride);
237 // found an enabled vertex attribute that was specified with a client-side pointer
238 event->addData(data: QByteArray(reinterpret_cast<const char *>(va.pointer), len));
239 }
240 }
241}
242
243template<class POINTER, class COUNT>
244inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event,
245 const QPair<POINTER, COUNT> &elements)
246{
247 QVariantList list;
248 for (auto i = 0; i < elements.second; ++i)
249 list.append(QVariant::fromValue(elements.first[i]));
250 event->addList(list);
251 return event;
252}
253
254template<class SIZE>
255inline void addHelper(QWebGLFunctionCall *event, const QPair<const float *, SIZE> &elements)
256{
257 QVariantList list;
258 for (auto i = 0; i < elements.second; ++i)
259 list.append(QVariant::fromValue<double>(elements.first[i]));
260 event->addList(list);
261}
262
263template<class T>
264inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value)
265{
266 if (event)
267 event->add(value);
268 return event;
269}
270
271template<class T, class... Ts>
272inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value, const Ts&... rest)
273{
274 if (event) {
275 event->add(value);
276 addHelper(event, rest...);
277 }
278 return event;
279}
280
281template<class T>
282static T queryValue(int id, const T &defaultValue = T())
283{
284 const auto variant = currentContext()->queryValue(id);
285 if (variant.isNull())
286 return defaultValue;
287 if (!variant.canConvert<T>()) {
288 qCWarning(lc, "Cannot convert %s to " QT_STRINGIFY(T), variant.typeName());
289 return defaultValue;
290 }
291 return variant.value<T>();
292}
293
294template<typename T>
295struct ParameterTypeTraits {
296 static int typeId() { return qMetaTypeId<T>(); }
297 static bool isArray() { return std::is_pointer<T>(); }
298};
299
300template<typename T>
301struct ParameterTypeTraits<T*> : ParameterTypeTraits<T> {
302 static int typeId() { return qMetaTypeId<T>(); }
303};
304
305template<typename T>
306struct ParameterTypeTraits<const T*> : ParameterTypeTraits<T*> {
307};
308
309template<typename T>
310struct ParameterTypeTraits<const T**> : ParameterTypeTraits<T*> {
311};
312
313struct GLFunction
314{
315 struct Parameter {
316 Parameter() {}
317 Parameter(const QString &name, const QString &typeName, int typeId, bool isArray) :
318 name(name), typeName(typeName), typeId(typeId), isArray(isArray) {}
319
320 QString name;
321 QString typeName;
322 int typeId;
323 bool isArray;
324 };
325
326 static QHash<QString, const GLFunction *> byName;
327 static QStringList remoteFunctionNames;
328 using ParameterList = QVector<Parameter>;
329
330 GLFunction(const QString &remoteName,
331 const QString &localName,
332 QFunctionPointer functionPointer,
333 ParameterList parameters = ParameterList())
334 : remoteName(remoteName), localName(localName),
335 functionPointer(functionPointer), parameters(parameters)
336 {
337 Q_ASSERT(!byName.contains(localName));
338 byName.insert(akey: localName, avalue: this);
339 id = remoteFunctionNames.size();
340 Q_ASSERT(remoteFunctionNames.size() <= std::numeric_limits<quint8>::max());
341 remoteFunctionNames.append(t: remoteName);
342 Q_ASSERT(byName.size() == remoteFunctionNames.size());
343 }
344
345 GLFunction(const QString &name) : GLFunction(name, name, nullptr)
346 {}
347 quint8 id;
348 const QString remoteName;
349 const QString localName;
350 const QFunctionPointer functionPointer;
351 const ParameterList parameters;
352};
353
354QHash<QString, const GLFunction *> GLFunction::byName;
355QStringList GLFunction::remoteFunctionNames;
356
357template<const GLFunction *Function>
358static QWebGLFunctionCall *createEventImpl(bool wait)
359{
360 auto context = QOpenGLContext::currentContext();
361 Q_ASSERT(context);
362 const auto handle = static_cast<QWebGLContext *>(context->handle());
363 auto integrationPrivate = QWebGLIntegrationPrivate::instance();
364 const auto clientData = integrationPrivate->findClientData(surface: handle->currentSurface());
365 if (!clientData || !clientData->socket
366 || clientData->socket->state() != QAbstractSocket::ConnectedState)
367 return nullptr;
368 return new QWebGLFunctionCall(Function->localName, handle->currentSurface(), wait);
369}
370
371static void postEventImpl(QWebGLFunctionCall *event)
372{
373 if (event->isBlocking())
374 QWebGLContextPrivate::waitingIds.insert(value: event->id());
375 QCoreApplication::postEvent(receiver: QWebGLIntegrationPrivate::instance()->webSocketServer, event);
376}
377
378template<const GLFunction *Function, class... Ts>
379static int createEventAndPostImpl(bool wait, Ts&&... arguments)
380{
381 auto event = createEventImpl<Function>(wait);
382 auto id = -1;
383 if (event) {
384 id = event->id();
385 addHelper(event, arguments...);
386 postEventImpl(event);
387 }
388 return id;
389}
390
391template<const GLFunction *Function>
392static int createEventAndPostImpl(bool wait)
393{
394 auto event = createEventImpl<Function>(wait);
395 auto id = -1;
396 if (event) {
397 id = event->id();
398 postEventImpl(event);
399 }
400 return id;
401}
402
403template<const GLFunction *Function, class... Ts>
404inline int postEventImpl(bool wait, Ts&&... arguments)
405{
406 return createEventAndPostImpl<Function>(wait, arguments...);
407}
408
409template<const GLFunction *Function>
410inline int postEvent(bool wait)
411{
412 return createEventAndPostImpl<Function>(wait);
413}
414
415template<const GLFunction *Function, class...Ts>
416inline int postEvent(Ts&&... arguments)
417{
418 return postEventImpl<Function>(false, arguments...);
419}
420
421template<const GLFunction *Function, class ReturnType, class...Ts>
422static ReturnType postEventAndQuery(ReturnType defaultValue,
423 Ts&&... arguments)
424{
425 auto id = postEventImpl<Function>(true, arguments...);
426 return id != -1 ? queryValue(id, defaultValue) : defaultValue;
427}
428
429namespace QWebGL {
430#define EXPAND(x) x
431
432#define USE_(...) __VA_ARGS__
433#define IGNORE_(...)
434
435
436// Retrieve the type
437// TYPEOF( (type) name ) -> TYPEOF_( SEPARATE (type) name ) -> FIRST_ARG( type, name ) -> type
438#define FIRST_ARG(ONE, ...) ONE
439#define SEPARATE(ITEM) ITEM,
440#define TYPEOF_(...) EXPAND(FIRST_ARG(__VA_ARGS__))
441#define TYPEOF(ITEM) TYPEOF_(SEPARATE ITEM)
442
443// Strip off the type, leaving just the parameter name:
444#define STRIP(ITEM) IGNORE_ ITEM
445// PAIR( (type) name ) -> type name
446#define PAIR(ITEM) USE_ ITEM
447
448// Make a FOREACH macro
449#define FOR_1(EACH, ITEM) EACH(ITEM)
450#define FOR_2(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_1(EACH, __VA_ARGS__))
451#define FOR_3(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_2(EACH, __VA_ARGS__))
452#define FOR_4(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_3(EACH, __VA_ARGS__))
453#define FOR_5(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_4(EACH, __VA_ARGS__))
454#define FOR_6(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_5(EACH, __VA_ARGS__))
455#define FOR_7(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_6(EACH, __VA_ARGS__))
456#define FOR_8(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_7(EACH, __VA_ARGS__))
457#define FOR_9(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_8(EACH, __VA_ARGS__))
458#define FOR_10(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_9(EACH, __VA_ARGS__))
459//... repeat as needed
460
461#define SELECT_BY_COUNT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME
462#define FOR_EACH(action, ...) \
463 EXPAND(SELECT_BY_COUNT(__VA_ARGS__, FOR_10, FOR_9, FOR_8, FOR_7, \
464 FOR_6, FOR_5, FOR_4, FOR_3, FOR_2, FOR_1)(action, __VA_ARGS__))
465
466#define QWEBGL_FUNCTION_PARAMETER(ITEM) \
467 GLFunction::Parameter { \
468 QStringLiteral(QT_STRINGIFY(STRIP(ITEM))), \
469 QStringLiteral(QT_STRINGIFY(TYPEOF(ITEM))), \
470 ParameterTypeTraits<TYPEOF(ITEM)>::typeId(), \
471 ParameterTypeTraits<TYPEOF(ITEM)>::isArray() \
472 }
473
474#if defined(Q_CC_MSVC) && defined(Q_OS_WIN32) && !defined(Q_OS_WIN64)
475# define WEBGL_APIENTRY __stdcall
476#else
477# define WEBGL_APIENTRY
478#endif
479
480#define QWEBGL_FUNCTION(REMOTE_NAME, RET_TYPE, LOCAL_NAME, ...) \
481 RET_TYPE WEBGL_APIENTRY LOCAL_NAME(FOR_EACH(TYPEOF, __VA_ARGS__));\
482 extern const GLFunction REMOTE_NAME { \
483 #REMOTE_NAME, \
484 #LOCAL_NAME, \
485 reinterpret_cast<QFunctionPointer>(LOCAL_NAME), \
486 GLFunction::ParameterList({FOR_EACH(QWEBGL_FUNCTION_PARAMETER, __VA_ARGS__)}) \
487 }; \
488 RET_TYPE WEBGL_APIENTRY LOCAL_NAME(FOR_EACH(PAIR, __VA_ARGS__))
489
490#define QWEBGL_FUNCTION_NO_PARAMS(REMOTE_NAME, RET_TYPE, LOCAL_NAME) \
491 RET_TYPE WEBGL_APIENTRY LOCAL_NAME();\
492 extern const GLFunction REMOTE_NAME { \
493 #REMOTE_NAME, \
494 #LOCAL_NAME, \
495 (QFunctionPointer) LOCAL_NAME \
496 }; \
497 RET_TYPE WEBGL_APIENTRY LOCAL_NAME()
498
499#define QWEBGL_FUNCTION_POSTEVENT(REMOTE_NAME, LOCAL_NAME, ...) \
500 QWEBGL_FUNCTION(REMOTE_NAME, void, LOCAL_NAME, __VA_ARGS__) { \
501 postEvent<&REMOTE_NAME>(FOR_EACH(STRIP, __VA_ARGS__)); \
502 }
503
504QWEBGL_FUNCTION(activeTexture, void, glActiveTexture,
505 (GLenum) texture)
506{
507 postEvent<&activeTexture>(arguments&: texture);
508 currentContextData()->activeTextureUnit = texture;
509}
510
511QWEBGL_FUNCTION_POSTEVENT(attachShader, glAttachShader,
512 (GLuint) program, (GLuint) shader)
513QWEBGL_FUNCTION_POSTEVENT(bindAttribLocation, glBindAttribLocation,
514 (GLuint) program, (GLuint) index, (const GLchar *) name)
515
516QWEBGL_FUNCTION(bindBuffer, void, glBindBuffer,
517 (GLenum) target, (GLuint) buffer)
518{
519 postEvent<&bindBuffer>(arguments&: target, arguments&: buffer);
520 if (target == GL_ARRAY_BUFFER)
521 currentContextData()->boundArrayBuffer = buffer;
522 if (target == GL_ELEMENT_ARRAY_BUFFER)
523 currentContextData()->boundElementArrayBuffer = buffer;
524}
525
526QWEBGL_FUNCTION(bindFramebuffer, void, glBindFramebuffer,
527 (GLenum) target, (GLuint) framebuffer)
528{
529 postEvent<&bindFramebuffer>(arguments&: target, arguments&: framebuffer);
530 if (target == GL_FRAMEBUFFER)
531 currentContextData()->boundDrawFramebuffer = framebuffer;
532}
533
534QWEBGL_FUNCTION_POSTEVENT(bindRenderbuffer, glBindRenderbuffer,
535 (GLenum) target, (GLuint) renderbuffer)
536
537QWEBGL_FUNCTION(bindTexture, void, glBindTexture,
538 (GLenum) target, (GLuint) texture)
539{
540 postEvent<&bindTexture>(arguments&: target, arguments&: texture);
541 if (target == GL_TEXTURE_2D)
542 currentContextData()->boundTexture2D = texture;
543}
544
545QWEBGL_FUNCTION_POSTEVENT(blendColor, glBlendColor,
546 (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha)
547
548QWEBGL_FUNCTION_POSTEVENT(blendEquation, glBlendEquation,
549 (GLenum) mode)
550
551QWEBGL_FUNCTION_POSTEVENT(blendEquationSeparate, glBlendEquationSeparate,
552 (GLenum) modeRGB, (GLenum) modeAlpha)
553
554QWEBGL_FUNCTION_POSTEVENT(blendFunc, glBlendFunc,
555 (GLenum) sfactor, (GLenum) dfactor)
556
557QWEBGL_FUNCTION_POSTEVENT(blendFuncSeparate, glBlendFuncSeparate,
558 (GLenum) sfactorRGB, (GLenum) dfactorRGB,
559 (GLenum) sfactorAlpha, (GLenum) dfactorAlpha)
560
561QWEBGL_FUNCTION(bufferData, void, glBufferData,
562 (GLenum) target, (GLsizeiptr) size, (const void *) data, (GLenum) usage)
563{
564 postEvent<&bufferData>(arguments&: target, arguments&: usage, arguments: int(size), arguments: data ? QByteArray((const char *)data, size)
565 : QByteArray());
566}
567
568QWEBGL_FUNCTION(bufferSubData, void, glBufferSubData,
569 (GLenum) target, (GLintptr) offset, (GLsizeiptr) size, (const void *) data)
570{
571 postEvent<&bufferSubData>(arguments&: target, arguments: int(offset), arguments: QByteArray((const char *)data, size));
572}
573
574QWEBGL_FUNCTION(checkFramebufferStatus, GLenum, glCheckFramebufferStatus,
575 (GLenum) target)
576{
577 return postEventAndQuery<&checkFramebufferStatus>(defaultValue: 0u, arguments&: target);
578}
579
580QWEBGL_FUNCTION_POSTEVENT(clear, glClear, (GLbitfield) mask)
581
582QWEBGL_FUNCTION_POSTEVENT(clearColor, glClearColor,
583 (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha)
584
585QWEBGL_FUNCTION_POSTEVENT(clearDepthf, glClearDepthf,
586 (GLfloat) d)
587
588QWEBGL_FUNCTION_POSTEVENT(clearStencil, glClearStencil,
589 (GLint) s)
590
591QWEBGL_FUNCTION_POSTEVENT(colorMask, glColorMask,
592 (GLboolean) red, (GLboolean) green, (GLboolean) blue, (GLboolean) alpha)
593
594QWEBGL_FUNCTION_POSTEVENT(compileShader, glCompileShader, (GLuint) shader)
595
596QWEBGL_FUNCTION(compressedTexImage2D, void, glCompressedTexImage2D,
597 (GLenum) target, (GLint) level, (GLenum) internalformat,
598 (GLsizei) width, (GLsizei) height, (GLint) border,
599 (GLsizei) imageSize, (const void *) data) {
600 postEvent<&compressedTexImage2D>(arguments&: target, arguments&: level, arguments&: internalformat, arguments&: width, arguments&: height, arguments&: border,
601 arguments&: imageSize, arguments: QByteArray(reinterpret_cast<const char *>(data),
602 imageSize));
603}
604
605QWEBGL_FUNCTION(compressedTexSubImage2D, void, glCompressedTexSubImage2D,
606 (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset,
607 (GLsizei) width, (GLsizei) height, (GLenum) format,
608 (GLsizei) imageSize, (const void *) data) {
609 postEvent<&compressedTexSubImage2D>(arguments&: target, arguments&: level, arguments&: xoffset, arguments&: yoffset, arguments&: width, arguments&: height, arguments&: format,
610 arguments&: imageSize, arguments: QByteArray(reinterpret_cast<const char *>(data),
611 imageSize));
612}
613
614QWEBGL_FUNCTION_POSTEVENT(copyTexImage2D, glCopyTexImage2D,
615 (GLenum) target, (GLint) level, (GLenum) internalformat,
616 (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height, (GLint) border)
617
618QWEBGL_FUNCTION_POSTEVENT(copyTexSubImage2D, glCopyTexSubImage2D,
619 (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset,
620 (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height)
621
622QWEBGL_FUNCTION_NO_PARAMS(createProgram, GLuint, glCreateProgram)
623{
624 return postEventAndQuery<&createProgram>(defaultValue: 0u);
625}
626
627QWEBGL_FUNCTION(createShader, GLuint, glCreateShader,
628 (GLenum) type)
629{
630 return postEventAndQuery<&createShader>(defaultValue: 0u, arguments&: type);
631}
632
633QWEBGL_FUNCTION_POSTEVENT(cullFace, glCullFace,
634 (GLenum) mode)
635
636QWEBGL_FUNCTION(deleteBuffers, void, glDeleteBuffers,
637 (GLsizei) n, (const GLuint *) buffers)
638{
639 postEvent<&deleteBuffers>(arguments&: n, arguments: qMakePair(x: buffers, y: n));
640 for (int i = 0; i < n; ++i) {
641 if (currentContextData()->boundArrayBuffer == buffers[i])
642 currentContextData()->boundArrayBuffer = 0;
643 if (currentContextData()->boundElementArrayBuffer == buffers[i])
644 currentContextData()->boundElementArrayBuffer = 0;
645 }
646}
647
648QWEBGL_FUNCTION(deleteFramebuffers, void, glDeleteFramebuffers,
649 (GLsizei) n, (const GLuint *) framebuffers)
650{
651 postEvent<&deleteFramebuffers>(arguments: qMakePair(x: framebuffers, y: n));
652}
653
654QWEBGL_FUNCTION_POSTEVENT(deleteProgram, glDeleteProgram,
655 (GLuint) program)
656
657QWEBGL_FUNCTION(deleteRenderbuffers, void, glDeleteRenderbuffers,
658 (GLsizei) n, (const GLuint *) renderbuffers)
659{
660 postEvent<&deleteRenderbuffers>(arguments: qMakePair(x: renderbuffers, y: n));
661}
662
663QWEBGL_FUNCTION_POSTEVENT(deleteShader, glDeleteShader,
664 (GLuint) shader)
665
666QWEBGL_FUNCTION(deleteTextures, void, glDeleteTextures,
667 (GLsizei) n, (const GLuint *) textures)
668{
669 postEvent<&deleteTextures>(arguments: qMakePair(x: textures, y: n));
670}
671
672QWEBGL_FUNCTION_POSTEVENT(depthFunc, glDepthFunc,
673 (GLenum) func)
674
675QWEBGL_FUNCTION_POSTEVENT(depthMask, glDepthMask,
676 (GLboolean) flag)
677
678QWEBGL_FUNCTION_POSTEVENT(depthRangef, glDepthRangef,
679 (GLfloat) n, (GLfloat) f)
680
681QWEBGL_FUNCTION_POSTEVENT(detachShader, glDetachShader,
682 (GLuint) program, (GLuint) shader)
683
684QWEBGL_FUNCTION(disableVertexAttribArray, void, glDisableVertexAttribArray,
685 (GLuint) index)
686{
687 postEvent<&disableVertexAttribArray>(arguments&: index);
688 currentContextData()->vertexAttribPointers[index].enabled = false;
689}
690
691QWEBGL_FUNCTION(drawArrays, void, glDrawArrays,
692 (GLenum) mode, (GLint) first, (GLsizei) count)
693{
694 auto event = currentContext()->createEvent(QStringLiteral("glDrawArrays"));
695 if (!event)
696 return;
697 event->addParameters(arguments&: mode, arguments&: first, arguments&: count);
698 // Some vertex attributes may be client-side, others may not. Therefore
699 // client-side ones need to transfer the data starting from the base
700 // pointer, not just from 'first'.
701 setVertexAttribs(event, count: first + count);
702 QCoreApplication::postEvent(receiver: QWebGLIntegrationPrivate::instance()->webSocketServer, event);
703}
704
705QWEBGL_FUNCTION(drawElements, void, glDrawElements,
706 (GLenum) mode, (GLsizei) count, (GLenum) type, (const void *) indices)
707{
708 auto event = currentContext()->createEvent(QStringLiteral("glDrawElements"));
709 if (!event)
710 return;
711 event->addParameters(arguments&: mode, arguments&: count, arguments&: type);
712 setVertexAttribs(event, count);
713 ContextData *d = currentContextData();
714 if (d->boundElementArrayBuffer == 0) {
715 event->addParameters(arguments: 0, arguments: QByteArray(reinterpret_cast<const char *>(indices),
716 count * elementSize(type)));
717 } else {
718 event->addParameters(arguments: 1, arguments: uint(quintptr(indices)));
719 }
720 QCoreApplication::postEvent(receiver: QWebGLIntegrationPrivate::instance()->webSocketServer, event);
721}
722
723QWEBGL_FUNCTION(enableVertexAttribArray, void, glEnableVertexAttribArray,
724 (GLuint) index)
725{
726 postEvent<&enableVertexAttribArray>(arguments&: index);
727 currentContextData()->vertexAttribPointers[index].enabled = true;
728}
729
730QWEBGL_FUNCTION_NO_PARAMS(finish, void, glFinish)
731{
732 postEvent<&finish>();
733}
734
735QWEBGL_FUNCTION_NO_PARAMS(flush, void, glFlush)
736{
737 postEvent<&flush>();
738}
739
740QWEBGL_FUNCTION_POSTEVENT(framebufferRenderbuffer, glFramebufferRenderbuffer,
741 (GLenum) target, (GLenum) attachment,
742 (GLenum) renderbuffertarget, (GLuint) renderbuffer)
743
744QWEBGL_FUNCTION_POSTEVENT(framebufferTexture2D, glFramebufferTexture2D,
745 (GLenum) target, (GLenum) attachment, (GLenum) textarget,
746 (GLuint) texture, (GLint) level)
747
748QWEBGL_FUNCTION_POSTEVENT(frontFace, glFrontFace,
749 (GLenum) mode)
750
751QWEBGL_FUNCTION(genBuffers, void, glGenBuffers,
752 (GLsizei) n, (GLuint *) buffers)
753{
754 const auto values = postEventAndQuery<&genBuffers>(defaultValue: QVariantList(), arguments&: n);
755 if (values.size() != n)
756 qCWarning(lc, "Failed to create buffers");
757 for (int i = 0; i < qMin(a: n, b: values.size()); ++i)
758 buffers[i] = values.at(i).toUInt();
759}
760
761QWEBGL_FUNCTION(genFramebuffers, void, glGenFramebuffers,
762 (GLsizei) n, (GLuint *) framebuffers)
763{
764 const auto values = postEventAndQuery<&genFramebuffers>(defaultValue: QVariantList(), arguments&: n);
765 if (values.size() != n)
766 qCWarning(lc, "Failed to create framebuffers");
767 for (int i = 0; i < qMin(a: n, b: values.size()); ++i)
768 framebuffers[i] = values.at(i).toUInt();
769}
770
771QWEBGL_FUNCTION(genRenderbuffers, void, glGenRenderbuffers,
772 (GLsizei) n, (GLuint *) renderbuffers)
773{
774 const auto values = postEventAndQuery<&genRenderbuffers>(defaultValue: QVariantList(), arguments&: n);
775 if (values.size() != n)
776 qCWarning(lc, "Failed to create render buffers");
777 for (int i = 0; i < qMin(a: n, b: values.size()); ++i)
778 renderbuffers[i] = values.at(i).toUInt();
779}
780
781QWEBGL_FUNCTION(genTextures, void, glGenTextures,
782 (GLsizei) n, (GLuint *) textures)
783{
784 const auto values = postEventAndQuery<&genTextures>(defaultValue: QVariantList(), arguments&: n);
785 if (values.size() != n)
786 qCWarning(lc, "Failed to create textures");
787 for (int i = 0; i < qMin(a: n, b: values.size()); ++i)
788 textures[i] = values.at(i).toUInt();
789}
790
791QWEBGL_FUNCTION_POSTEVENT(generateMipmap, glGenerateMipmap,
792 (GLenum) target)
793
794QWEBGL_FUNCTION(getActiveAttrib, void, glGetActiveAttrib,
795 (GLuint) program, (GLuint) index, (GLsizei) bufSize,
796 (GLsizei *) length, (GLint *) size, (GLenum *) type, (GLchar *) name)
797{
798 const auto values = postEventAndQuery<&getActiveAttrib>(defaultValue: QVariantMap(), arguments&: program, arguments&: index, arguments&: bufSize);
799 if (values.isEmpty())
800 return;
801 const int rtype = values["rtype"].toInt();
802 const int rsize = values["rsize"].toInt();
803 const QByteArray rname = values["rname"].toByteArray();
804 if (type)
805 *type = rtype;
806 if (size)
807 *size = rsize;
808 int len = qMax(a: 0, b: qMin(a: bufSize - 1, b: rname.size()));
809 if (length)
810 *length = len;
811 if (name) {
812 memcpy(dest: name, src: rname.constData(), n: len);
813 name[len] = '\0';
814 }
815}
816
817QWEBGL_FUNCTION(getActiveUniform, void, glGetActiveUniform,
818 (GLuint) program, (GLuint) index, (GLsizei) bufSize,
819 (GLsizei *) length, (GLint *) size, (GLenum *) type, (GLchar *) name)
820{
821 const auto values = postEventAndQuery<&getActiveUniform>(defaultValue: QVariantMap(), arguments&: program, arguments&: index, arguments&: bufSize);
822 if (values.isEmpty())
823 return;
824 const int rtype = values["rtype"].toInt();
825 const int rsize = values["rsize"].toInt();
826 const QByteArray rname = values["rname"].toByteArray();
827 if (type)
828 *type = rtype;
829 if (size)
830 *size = rsize;
831 int len = qMax(a: 0, b: qMin(a: bufSize - 1, b: rname.size()));
832 if (length)
833 *length = len;
834 if (name) {
835 memcpy(dest: name, src: rname.constData(), n: len);
836 name[len] = '\0';
837 }
838}
839
840QWEBGL_FUNCTION(getAttachedShaders, void, glGetAttachedShaders,
841 (GLuint) program, (GLsizei) maxCount, (GLsizei *) count, (GLuint *) shaders)
842{
843 const auto values = postEventAndQuery<&getAttachedShaders>(defaultValue: QVariantList(), arguments&: program, arguments&: maxCount);
844 *count = values.size();
845 for (int i = 0; i < values.size(); ++i)
846 shaders[i] = values.at(i).toUInt();
847}
848
849QWEBGL_FUNCTION(getAttribLocation, GLint, glGetAttribLocation,
850 (GLuint) program, (const GLchar *) name)
851{
852 return postEventAndQuery<&getAttribLocation>(defaultValue: -1, arguments&: program, arguments&: name);
853}
854
855QWEBGL_FUNCTION(getString, const GLubyte *, glGetString,
856 (GLenum) name)
857{
858 static QByteArrayList strings;
859 const auto it = currentContextData()->cachedParameters.find(akey: name);
860 if (it != currentContextData()->cachedParameters.end()) {
861 auto &stringCache = currentContextData()->stringCache;
862 Q_ASSERT(it->type() == QVariant::String);
863 const auto string = it->toString().toLatin1();
864 {
865 auto it = stringCache.find(value: string), end = stringCache.end();
866 if (it == end)
867 it = stringCache.insert(value: string);
868 return reinterpret_cast<const GLubyte *>(it->constData());
869 }
870 }
871 const auto value = postEventAndQuery<&getString>(defaultValue: QByteArray(), arguments&: name);
872 strings.append(t: value);
873 return reinterpret_cast<const GLubyte *>(strings.last().constData());
874}
875
876QWEBGL_FUNCTION(getIntegerv, void, glGetIntegerv,
877 (GLenum) pname, (GLint *) data)
878{
879 if (pname == GL_MAX_TEXTURE_SIZE) {
880 static bool ok;
881 static auto value = qgetenv(varName: "QT_WEBGL_MAX_TEXTURE_SIZE").toUInt(ok: &ok);
882 if (ok) {
883 *data = value;
884 return;
885 }
886 }
887 const auto it = currentContextData()->cachedParameters.find(akey: pname);
888 if (it != currentContextData()->cachedParameters.end()) {
889 QList<QVariant> values;
890 switch (it->type()) {
891 case QVariant::Map: values = it->toMap().values(); break;
892 case QVariant::List: values = it->toList(); break;
893 default: values = QVariantList{ *it };
894 }
895 for (const auto &integer : qAsConst(t&: values)) {
896 bool ok;
897 *data = integer.toInt(ok: &ok);
898 if (!ok)
899 qCWarning(lc, "Failed to cast value");
900 ++data;
901 }
902 return;
903 }
904 switch (pname) {
905 case GL_CURRENT_PROGRAM:
906 *data = currentContextData()->currentProgram;
907 return;
908 case GL_FRAMEBUFFER_BINDING:
909 *data = currentContextData()->boundDrawFramebuffer;
910 return;
911 case GL_ARRAY_BUFFER_BINDING:
912 *data = currentContextData()->boundArrayBuffer;
913 return;
914 case GL_ELEMENT_ARRAY_BUFFER_BINDING:
915 *data = currentContextData()->boundElementArrayBuffer;
916 return;
917 case GL_ACTIVE_TEXTURE:
918 *data = currentContextData()->activeTextureUnit;
919 return;
920 case GL_TEXTURE_BINDING_2D:
921 *data = currentContextData()->boundTexture2D;
922 return;
923 default:
924 *data = postEventAndQuery<&getIntegerv>(defaultValue: 0, arguments&: pname);
925 }
926}
927
928QWEBGL_FUNCTION(getBooleanv, void, glGetBooleanv,
929 (GLenum) pname, (GLboolean *) data)
930{
931 const auto it = currentContextData()->cachedParameters.find(akey: pname);
932 if (it != currentContextData()->cachedParameters.end()) {
933 Q_ASSERT(it->type() == QVariant::Bool);
934 *data = it->toBool();
935 return;
936 }
937 *data = postEventAndQuery<&getBooleanv>(GL_FALSE, arguments&: pname);
938}
939
940QWEBGL_FUNCTION(enable, void, glEnable,
941 (GLenum) cap)
942{
943 if (!postEvent<&enable>(arguments&: cap))
944 return;
945 auto it = currentContextData()->cachedParameters.find(akey: cap);
946 if (it != currentContextData()->cachedParameters.end()) {
947 Q_ASSERT(it->type() == QVariant::Bool);
948 it->setValue(true);
949 }
950}
951
952QWEBGL_FUNCTION(disable, void, glDisable,
953 (GLenum) cap)
954{
955 if (!postEvent<&disable>(arguments&: cap))
956 return;
957 auto it = currentContextData()->cachedParameters.find(akey: cap);
958 if (it != currentContextData()->cachedParameters.end()) {
959 Q_ASSERT(it->type() == QVariant::Bool);
960 it->setValue(false);
961 }
962}
963
964QWEBGL_FUNCTION(getBufferParameteriv, void, glGetBufferParameteriv,
965 (GLenum) target, (GLenum) pname, (GLint *) params)
966{
967 *params = postEventAndQuery<&getBufferParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname);
968}
969
970QWEBGL_FUNCTION_NO_PARAMS(getError, GLenum, glGetError)
971{
972 return postEventAndQuery<&getError>(GL_NO_ERROR);
973}
974
975QWEBGL_FUNCTION(getParameter, void, glGetFloatv,
976 (GLenum) pname, (GLfloat*) data)
977{
978 *data = postEventAndQuery<&getParameter>(defaultValue: 0.0, arguments&: pname);
979}
980
981QWEBGL_FUNCTION(getFramebufferAttachmentParameteriv, void, glGetFramebufferAttachmentParameteriv,
982 (GLenum) target, (GLenum) attachment, (GLenum) pname, (GLint *) params)
983{
984 *params = postEventAndQuery<&getFramebufferAttachmentParameteriv>(defaultValue: 0, arguments&: target, arguments&: attachment, arguments&: pname);
985}
986
987QWEBGL_FUNCTION(getProgramInfoLog, void, glGetProgramInfoLog,
988 (GLuint) program, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) infoLog)
989{
990 auto value = postEventAndQuery<&getProgramInfoLog>(defaultValue: QString(), arguments&: program);
991 *length = value.length();
992 if (bufSize >= value.length())
993 std::memcpy(dest: infoLog, src: value.constData(), n: value.length());
994}
995
996QWEBGL_FUNCTION(getProgramiv, void, glGetProgramiv,
997 (GLuint) program, (GLenum) pname, (GLint *) params)
998{
999 *params = postEventAndQuery<&getProgramiv>(defaultValue: 0, arguments&: program, arguments&: pname);
1000}
1001
1002QWEBGL_FUNCTION(getRenderbufferParameteriv, void, glGetRenderbufferParameteriv,
1003 (GLenum) target, (GLenum) pname, (GLint *) params)
1004{
1005 *params = postEventAndQuery<&getRenderbufferParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname);
1006}
1007
1008QWEBGL_FUNCTION(getShaderInfoLog, void, glGetShaderInfoLog,
1009 (GLuint) shader, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) infoLog)
1010{
1011 const auto value = postEventAndQuery<&getShaderInfoLog>(defaultValue: QString(), arguments&: shader);
1012 *length = value.length();
1013 if (bufSize >= value.length())
1014 std::memcpy(dest: infoLog, src: value.constData(), n: value.length());
1015}
1016
1017QWEBGL_FUNCTION(getShaderPrecisionFormat, void, glGetShaderPrecisionFormat,
1018 (GLenum) shadertype, (GLenum) precisiontype, (GLint *) range, (GLint *) precision)
1019{
1020 const auto value = postEventAndQuery<&getShaderPrecisionFormat>(defaultValue: QVariantMap(), arguments&: shadertype,
1021 arguments&: precisiontype);
1022 bool ok;
1023 range[0] = value[QStringLiteral("rangeMin")].toInt(ok: &ok);
1024 if (!ok)
1025 qCCritical(lc, "Invalid rangeMin value");
1026 range[1] = value[QStringLiteral("rangeMax")].toInt(ok: &ok);
1027 if (!ok)
1028 qCCritical(lc, "Invalid rangeMax value");
1029 *precision = value[QStringLiteral("precision")].toInt(ok: &ok);
1030 if (!ok)
1031 qCCritical(lc, "Invalid precision value");
1032}
1033
1034QWEBGL_FUNCTION(getShaderSource, void, glGetShaderSource,
1035 (GLuint) shader, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) source)
1036{
1037 const auto value = postEventAndQuery<&getShaderSource>(defaultValue: QString(), arguments&: shader);
1038 *length = value.length();
1039 if (bufSize >= value.length())
1040 std::memcpy(dest: source, src: value.constData(), n: value.length());
1041}
1042
1043QWEBGL_FUNCTION(getShaderiv, void, glGetShaderiv,
1044 (GLuint) shader, (GLenum) pname, (GLint *) params)
1045{
1046 if (pname == GL_INFO_LOG_LENGTH) {
1047 GLsizei bufSize = 0;
1048 glGetShaderInfoLog(shader, bufSize, length: &bufSize, infoLog: nullptr);
1049 *params = bufSize;
1050 return;
1051 }
1052 if (pname == GL_SHADER_SOURCE_LENGTH) {
1053 GLsizei bufSize = 0;
1054 glGetShaderSource(shader, bufSize, length: &bufSize, source: nullptr);
1055 *params = bufSize;
1056 return;
1057 }
1058 *params = postEventAndQuery<&getShaderiv>(defaultValue: 0, arguments&: shader, arguments&: pname);
1059}
1060
1061QWEBGL_FUNCTION(getTexParameterfv, void, glGetTexParameterfv,
1062 (GLenum) target, (GLenum) pname, (GLfloat *) params)
1063{
1064 *params = postEventAndQuery<&getTexParameterfv>(defaultValue: 0.f, arguments&: target, arguments&: pname);
1065}
1066
1067QWEBGL_FUNCTION(getTexParameteriv, void, glGetTexParameteriv,
1068 (GLenum) target, (GLenum) pname, (GLint *) params)
1069{
1070 *params = postEventAndQuery<&getTexParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname);
1071}
1072
1073QWEBGL_FUNCTION(getUniformLocation, GLint, glGetUniformLocation,
1074 (GLuint) program, (const GLchar *) name)
1075{
1076 return postEventAndQuery<&getUniformLocation>(defaultValue: -1, arguments&: program, arguments&: name);
1077}
1078
1079QWEBGL_FUNCTION(getUniformfv, void, glGetUniformfv,
1080 (GLuint) program, (GLint) location, (GLfloat *) params)
1081{
1082 *params = postEventAndQuery<&getUniformfv>(defaultValue: 0.f, arguments&: program, arguments&: location);
1083}
1084
1085QWEBGL_FUNCTION(getUniformiv, void, glGetUniformiv,
1086 (GLuint) program, (GLint) location, (GLint *) params)
1087{
1088 *params = postEventAndQuery<&getUniformiv>(defaultValue: 0, arguments&: program, arguments&: location);
1089}
1090
1091QWEBGL_FUNCTION(getVertexAttribPointerv, void, glGetVertexAttribPointerv,
1092 (GLuint) index, (GLenum) pname, (void **) pointer)
1093{
1094 Q_UNUSED(index);
1095 Q_UNUSED(pname);
1096 Q_UNUSED(pointer);
1097 qFatal(msg: "glGetVertexAttribPointerv not supported");
1098 return;
1099}
1100
1101QWEBGL_FUNCTION(getVertexAttribfv, void, glGetVertexAttribfv,
1102 (GLuint) index, (GLenum) pname, (GLfloat *) params)
1103{
1104 *params = postEventAndQuery<&getVertexAttribfv>(defaultValue: 0.f, arguments&: index, arguments&: pname);
1105}
1106
1107QWEBGL_FUNCTION(getVertexAttribiv, void, glGetVertexAttribiv,
1108 (GLuint) index, (GLenum) pname, (GLint *) params)
1109{
1110 *params = postEventAndQuery<&getVertexAttribiv>(defaultValue: 0, arguments&: index, arguments&: pname);
1111}
1112
1113QWEBGL_FUNCTION_POSTEVENT(hint, glHint,
1114 (GLenum) target, (GLenum) mode)
1115
1116QWEBGL_FUNCTION(isBuffer, GLboolean, glIsBuffer,
1117 (GLuint) buffer)
1118{
1119 return postEventAndQuery<&isBuffer>(GL_FALSE, arguments&: buffer);
1120}
1121
1122QWEBGL_FUNCTION(isEnabled, GLboolean, glIsEnabled,
1123 (GLenum) cap)
1124{
1125 return postEventAndQuery<&isEnabled>(GL_FALSE, arguments&: cap);
1126}
1127
1128QWEBGL_FUNCTION(isFramebuffer, GLboolean, glIsFramebuffer,
1129 (GLuint) framebuffer)
1130{
1131 return postEventAndQuery<&isFramebuffer>(GL_FALSE, arguments&: framebuffer);
1132}
1133
1134QWEBGL_FUNCTION(isProgram, GLboolean, glIsProgram,
1135 (GLuint) program)
1136{
1137 return postEventAndQuery<&isProgram>(GL_FALSE, arguments&: program);
1138}
1139
1140QWEBGL_FUNCTION(isRenderbuffer, GLboolean, glIsRenderbuffer,
1141 (GLuint) renderbuffer)
1142{
1143 return postEventAndQuery<&isRenderbuffer>(GL_FALSE, arguments&: renderbuffer);
1144}
1145
1146QWEBGL_FUNCTION(isShader, GLboolean, glIsShader,
1147 (GLuint) shader)
1148{
1149 return postEventAndQuery<&isShader>(GL_FALSE, arguments&: shader);
1150}
1151
1152QWEBGL_FUNCTION(isTexture, GLboolean, glIsTexture,
1153 (GLuint) texture)
1154{
1155 return postEventAndQuery<&isTexture>(GL_FALSE, arguments&: texture);
1156}
1157
1158QWEBGL_FUNCTION_POSTEVENT(lineWidth, glLineWidth,
1159 (GLfloat) width)
1160
1161QWEBGL_FUNCTION_POSTEVENT(linkProgram, glLinkProgram,
1162 (GLuint) program)
1163
1164QWEBGL_FUNCTION(pixelStorei, void, glPixelStorei,
1165 (GLenum) pname, (GLint) param)
1166{
1167 postEvent<&pixelStorei>(arguments&: pname, arguments&: param);
1168 switch (pname) {
1169 case GL_UNPACK_ALIGNMENT: currentContextData()->unpackAlignment = param; break;
1170 }
1171}
1172
1173QWEBGL_FUNCTION_POSTEVENT(polygonOffset, glPolygonOffset,
1174 (GLfloat) factor, (GLfloat) units)
1175
1176QWEBGL_FUNCTION(readPixels, void, glReadPixels,
1177 (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height,
1178 (GLenum) format, (GLenum) type, (void *) pixels)
1179{
1180 const auto value = postEventAndQuery<&readPixels>(defaultValue: QByteArray(), arguments&: x, arguments&: y, arguments&: width, arguments&: height, arguments&: format,
1181 arguments&: type);
1182 if (!value.isEmpty())
1183 std::memcpy(dest: pixels, src: value.constData(), n: value.size());
1184}
1185
1186QWEBGL_FUNCTION_NO_PARAMS(releaseShaderCompiler, void, glReleaseShaderCompiler)
1187{
1188 postEvent<&releaseShaderCompiler>();
1189}
1190
1191QWEBGL_FUNCTION_POSTEVENT(renderbufferStorage, glRenderbufferStorage,
1192 (GLenum) target, (GLenum) internalformat,
1193 (GLsizei) width, (GLsizei) height)
1194
1195QWEBGL_FUNCTION_POSTEVENT(sampleCoverage, glSampleCoverage,
1196 (GLfloat) value, (GLboolean) invert)
1197
1198QWEBGL_FUNCTION_POSTEVENT(scissor, glScissor,
1199 (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height)
1200
1201QWEBGL_FUNCTION(shaderBinary, void, glShaderBinary,
1202 (GLsizei), (const GLuint *), (GLenum), (const void *), (GLsizei))
1203{
1204 qFatal(msg: "WebGL does not allow precompiled shaders");
1205}
1206
1207QWEBGL_FUNCTION(shaderSource, void, glShaderSource,
1208 (GLuint) shader, (GLsizei) count,
1209 (const GLchar *const *) string, (const GLint *) length)
1210{
1211 QString fullString;
1212 std::function<void(int)> concat;
1213 if (length)
1214 concat = [&](int i) { fullString.append(s: QString::fromLatin1(str: string[i], size: length[i])); };
1215 else
1216 concat = [&](int i) { fullString.append(s: QString::fromLatin1(str: string[i])); };
1217 for (int i = 0; i < count; ++i)
1218 concat(i);
1219 postEvent<&shaderSource>(arguments&: shader, arguments&: fullString);
1220}
1221
1222QWEBGL_FUNCTION_POSTEVENT(stencilFunc, glStencilFunc,
1223 (GLenum) func, (GLint) ref, (GLuint) mask)
1224
1225QWEBGL_FUNCTION_POSTEVENT(stencilFuncSeparate, glStencilFuncSeparate,
1226 (GLenum) face, (GLenum) func, (GLint) ref, (GLuint) mask)
1227
1228QWEBGL_FUNCTION_POSTEVENT(stencilMask, glStencilMask,
1229 (GLuint) mask)
1230
1231QWEBGL_FUNCTION_POSTEVENT(stencilMaskSeparate, glStencilMaskSeparate,
1232 (GLenum) face, (GLuint) mask)
1233
1234QWEBGL_FUNCTION_POSTEVENT(stencilOp, glStencilOp,
1235 (GLenum) fail, (GLenum) zfail, (GLenum) zpass)
1236
1237QWEBGL_FUNCTION_POSTEVENT(stencilOpSeparate, glStencilOpSeparate,
1238 (GLenum) face, (GLenum) sfail, (GLenum) dpfail, (GLenum) dppass)
1239
1240QWEBGL_FUNCTION(texImage2D, void, glTexImage2D,
1241 (GLenum) target, (GLint) level, (GLint) internalformat,
1242 (GLsizei) width, (GLsizei) height, (GLint) border, (GLenum) format, (GLenum) type,
1243 (const void *) pixels)
1244{
1245 const auto data = reinterpret_cast<const char *>(pixels);
1246 const auto dataSize = imageSize(width, height, format, type,
1247 pixelStorage: currentContextData()->pixelStorage);
1248 const bool isNull = data == nullptr || [](const char *pointer, int size) {
1249 const char *const end = pointer + size;
1250 const unsigned int zero = 0u;
1251 const char *const late = end + 1 - sizeof(zero);
1252 while (pointer < late) { // we have at least sizeof(zero) more bytes to check:
1253 if (*reinterpret_cast<const unsigned int *>(pointer) != zero)
1254 return false;
1255 pointer += sizeof(zero);
1256 }
1257 return pointer >= end || std::memcmp(s1: pointer, s2: &zero, n: end - pointer) == 0;
1258 }(data, dataSize);
1259 postEvent<&texImage2D>(arguments&: target, arguments&: level, arguments&: internalformat, arguments&: width, arguments&: height, arguments&: border, arguments&: format, arguments&: type,
1260 arguments: isNull ? nullptr : QByteArray(data, dataSize));
1261}
1262
1263QWEBGL_FUNCTION_POSTEVENT(texParameterf, glTexParameterf,
1264 (GLenum) target, (GLenum) pname, (GLfloat) param)
1265
1266QWEBGL_FUNCTION(texParameterfv, void, glTexParameterfv,
1267 (GLenum), (GLenum), (const GLfloat *))
1268{
1269 qFatal(msg: "glTexParameterfv not implemented");
1270}
1271
1272QWEBGL_FUNCTION_POSTEVENT(texParameteri, glTexParameteri,
1273 (GLenum) target, (GLenum) pname, (GLint) param)
1274
1275QWEBGL_FUNCTION(texParameteriv, void, glTexParameteriv,
1276 (GLenum), (GLenum), (const GLint *))
1277{
1278 qFatal(msg: "glTexParameteriv not implemented");
1279}
1280
1281QWEBGL_FUNCTION(texSubImage2D, void, glTexSubImage2D,
1282 (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset,
1283 (GLsizei) width, (GLsizei) height, (GLenum) format, (GLenum) type,
1284 (const void *) pixels)
1285{
1286 postEvent<&texSubImage2D>(arguments&: target, arguments&: level, arguments&: xoffset, arguments&: yoffset, arguments&: width, arguments&: height, arguments&: format, arguments&: type,
1287 arguments: pixels ? QByteArray(reinterpret_cast<const char *>(pixels),
1288 imageSize(width, height, format, type,
1289 pixelStorage: currentContextData()->pixelStorage))
1290 : nullptr);
1291}
1292
1293QWEBGL_FUNCTION_POSTEVENT(uniform1f, glUniform1f, (GLint) location, (GLfloat) v0)
1294
1295QWEBGL_FUNCTION(uniform1fv, void, glUniform1fv,
1296 (GLint) location, (GLsizei) count, (const GLfloat *) value)
1297{
1298 postEvent<&uniform1fv>(arguments&: location, arguments: qMakePair(x: value, y: count));
1299}
1300
1301QWEBGL_FUNCTION_POSTEVENT(uniform1i, glUniform1i,
1302 (GLint) location, (GLint) v0)
1303
1304QWEBGL_FUNCTION(uniform1iv, void, glUniform1iv,
1305 (GLint) location, (GLsizei) count, (const GLint *) value)
1306{
1307 postEvent<&uniform1iv>(arguments&: location, arguments: qMakePair(x: value, y: count));
1308}
1309
1310QWEBGL_FUNCTION_POSTEVENT(uniform2f, glUniform2f,
1311 (GLint) location, (GLfloat) v0, (GLfloat) v1)
1312
1313QWEBGL_FUNCTION(uniform2fv, void, glUniform2fv,
1314 (GLint) location, (GLsizei) count, (const GLfloat *) value)
1315{
1316 postEvent<&uniform2fv>(arguments&: location, arguments: qMakePair(x: value, y: count * 2));
1317}
1318
1319QWEBGL_FUNCTION_POSTEVENT(uniform2i, glUniform2i,
1320 (GLint) location, (GLint) v0, (GLint) v1)
1321
1322QWEBGL_FUNCTION(uniform2iv, void, glUniform2iv,
1323 (GLint) location, (GLsizei) count, (const GLint *) value)
1324{
1325 postEvent<&uniform2iv>(arguments&: location, arguments: qMakePair(x: value, y: count * 2));
1326}
1327
1328QWEBGL_FUNCTION_POSTEVENT(uniform3f, glUniform3f,
1329 (GLint) location, (GLfloat) v0, (GLfloat) v1, (GLfloat) v2)
1330
1331QWEBGL_FUNCTION(uniform3fv, void, glUniform3fv,
1332 (GLint) location, (GLsizei) count, (const GLfloat *) value)
1333{
1334 postEvent<&uniform3fv>(arguments&: location, arguments: qMakePair(x: value, y: count * 3));
1335}
1336
1337QWEBGL_FUNCTION_POSTEVENT(uniform3i, glUniform3i,
1338 (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2)
1339
1340QWEBGL_FUNCTION(uniform3iv, void, glUniform3iv,
1341 (GLint) location, (GLsizei) count, (const GLint *) value)
1342{
1343 postEvent<&uniform3iv>(arguments&: location, arguments: qMakePair(x: value, y: count * 3));
1344}
1345
1346QWEBGL_FUNCTION_POSTEVENT(uniform4f, glUniform4f,
1347 (GLint) location, (GLfloat) v0, (GLfloat) v1,
1348 (GLfloat) v2, (GLfloat) v3)
1349
1350QWEBGL_FUNCTION(uniform4fv, void, glUniform4fv,
1351 (GLint) location, (GLsizei) count, (const GLfloat *) value)
1352{
1353 postEvent<&uniform4fv>(arguments&: location, arguments: qMakePair(x: value, y: count * 4));
1354}
1355
1356QWEBGL_FUNCTION_POSTEVENT(uniform4i, glUniform4i,
1357 (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2, (GLint) v3)
1358
1359QWEBGL_FUNCTION(uniform4iv, void, glUniform4iv,
1360 (GLint) location, (GLsizei) count, (const GLint *) value)
1361{
1362 postEvent<&uniform4iv>(arguments&: location, arguments: qMakePair(x: value, y: count * 4));
1363}
1364
1365QWEBGL_FUNCTION(uniformMatrix2fv, void, glUniformMatrix2fv,
1366 (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value)
1367{
1368 postEvent<&uniformMatrix2fv>(arguments&: location, arguments&: transpose, arguments: qMakePair(x: value, y: count * 4));
1369}
1370
1371QWEBGL_FUNCTION(uniformMatrix3fv, void, glUniformMatrix3fv,
1372 (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value)
1373{
1374 postEvent<&uniformMatrix3fv>(arguments&: location, arguments&: transpose, arguments: qMakePair(x: value, y: count * 9));
1375}
1376
1377QWEBGL_FUNCTION(uniformMatrix4fv, void, glUniformMatrix4fv,
1378 (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value)
1379{
1380 postEvent<&uniformMatrix4fv>(arguments&: location, arguments&: transpose, arguments: qMakePair(x: value, y: count * 16));
1381}
1382
1383QWEBGL_FUNCTION_POSTEVENT(useProgram, glUseProgram,
1384 (GLuint) program)
1385
1386QWEBGL_FUNCTION_POSTEVENT(validateProgram, glValidateProgram,
1387 (GLuint) program)
1388
1389QWEBGL_FUNCTION_POSTEVENT(vertexAttrib1f, glVertexAttrib1f,
1390 (GLuint) index, (GLfloat) x)
1391
1392QWEBGL_FUNCTION(vertexAttrib1fv, void, glVertexAttrib1fv,
1393 (GLuint) index, (const GLfloat *) v)
1394{
1395 postEvent<&vertexAttrib1fv>(arguments&: index, arguments: v[0]);
1396}
1397
1398QWEBGL_FUNCTION_POSTEVENT(vertexAttrib2f, glVertexAttrib2f,
1399 (GLuint) index, (GLfloat) x, (GLfloat) y)
1400
1401QWEBGL_FUNCTION(vertexAttrib2fv, void, glVertexAttrib2fv,
1402 (GLuint) index, (const GLfloat *) v)
1403{
1404 postEvent<&vertexAttrib2fv>(arguments&: index, arguments: v[0], arguments: v[1]);
1405}
1406
1407QWEBGL_FUNCTION_POSTEVENT(vertexAttrib3f, glVertexAttrib3f,
1408 (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z)
1409
1410QWEBGL_FUNCTION(vertexAttrib3fv, void, glVertexAttrib3fv,
1411 (GLuint) index, (const GLfloat *) v)
1412{
1413 postEvent<&vertexAttrib3fv>(arguments&: index, arguments: v[0], arguments: v[1], arguments: v[2]);
1414}
1415
1416QWEBGL_FUNCTION_POSTEVENT(vertexAttrib4f, glVertexAttrib4f,
1417 (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w)
1418
1419QWEBGL_FUNCTION(vertexAttrib4fv, void, glVertexAttrib4fv,
1420 (GLuint) index, (const GLfloat *) v)
1421{
1422 postEvent<&vertexAttrib4fv>(arguments&: index, arguments: v[0], arguments: v[1], arguments: v[2], arguments: v[3]);
1423}
1424
1425QWEBGL_FUNCTION(vertexAttribPointer, void, glVertexAttribPointer,
1426 (GLuint) index, (GLint) size, (GLenum) type,
1427 (GLboolean) normalized, (GLsizei) stride, (const void *) pointer)
1428{
1429 ContextData *d = currentContextData();
1430 ContextData::VertexAttrib &va(d->vertexAttribPointers[index]);
1431 va.arrayBufferBinding = d->boundArrayBuffer;
1432 va.size = size;
1433 va.type = type;
1434 va.normalized = normalized;
1435 va.stride = stride;
1436 va.pointer = pointer;
1437 if (d->boundArrayBuffer)
1438 postEvent<&vertexAttribPointer>(arguments&: index, arguments&: size, arguments&: type, arguments&: normalized, arguments&: stride,
1439 arguments: uint(quintptr(pointer)));
1440}
1441
1442QWEBGL_FUNCTION(viewport, void, glViewport,
1443 (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height)
1444{
1445 postEvent<&viewport>(arguments&: x, arguments&: y, arguments&: width, arguments&: height);
1446 auto it = currentContextData()->cachedParameters.find(GL_VIEWPORT);
1447 if (it != currentContextData()->cachedParameters.end())
1448 it->setValue(QVariantList{ x, y, width, height });
1449}
1450
1451QWEBGL_FUNCTION_POSTEVENT(blitFramebufferEXT, glBlitFramebufferEXT,
1452 (GLint) srcX0, (GLint) srcY0, (GLint) srcX1, (GLint) srcY1,
1453 (GLint) dstX0, (GLint) dstY0, (GLint) dstX1, (GLint) dstY1,
1454 (GLbitfield) mask, (GLenum) filter)
1455
1456QWEBGL_FUNCTION_POSTEVENT(renderbufferStorageMultisampleEXT, glRenderbufferStorageMultisampleEXT,
1457 (GLenum) target, (GLsizei) samples, (GLenum) internalformat, (GLsizei) width,
1458 (GLsizei) height)
1459
1460QWEBGL_FUNCTION(getTexLevelParameteriv, void, glGetTexLevelParameteriv,
1461 (GLenum), (GLint), (GLenum), (GLint *))
1462{
1463 qFatal(msg: "glGetTexLevelParameteriv not supported");
1464}
1465
1466#undef QWEBGL_FUNCTION
1467
1468extern const GLFunction makeCurrent("makeCurrent");
1469extern const GLFunction swapBuffers("swapBuffers");
1470
1471}
1472
1473QWebGLContext::QWebGLContext(const QSurfaceFormat &format) :
1474 d_ptr(new QWebGLContextPrivate)
1475{
1476 Q_D(QWebGLContext);
1477 d->id = d->nextId.fetchAndAddOrdered(valueToAdd: 1);
1478 qCDebug(lc, "Creating context %d", d->id);
1479 d->surfaceFormat = format;
1480 d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
1481}
1482
1483QWebGLContext::~QWebGLContext()
1484{}
1485
1486QSurfaceFormat QWebGLContext::format() const
1487{
1488 Q_D(const QWebGLContext);
1489 return d->surfaceFormat;
1490}
1491
1492void QWebGLContext::swapBuffers(QPlatformSurface *surface)
1493{
1494 Q_UNUSED(surface);
1495 auto event = createEvent(QStringLiteral("swapBuffers"), wait: true);
1496 if (!event)
1497 return;
1498 lockMutex();
1499 QCoreApplication::postEvent(receiver: QWebGLIntegrationPrivate::instance()->webSocketServer, event);
1500 waitCondition(time: 1000);
1501 unlockMutex();
1502}
1503
1504bool QWebGLContext::makeCurrent(QPlatformSurface *surface)
1505{
1506 Q_D(QWebGLContext);
1507
1508 qCDebug(lc, "%p", surface);
1509 if (surface->surface()->surfaceClass() == QSurface::Window) {
1510 const auto window = static_cast<QWebGLWindow *>(surface);
1511 if (window->winId() == WId(-1))
1512 return false;
1513 }
1514
1515 QOpenGLContextPrivate::setCurrentContext(context());
1516 d->currentSurface = surface;
1517
1518 auto event = createEvent(QStringLiteral("makeCurrent"));
1519 if (!event)
1520 return false;
1521 event->addInt(value: d->id);
1522 if (surface->surface()->surfaceClass() == QSurface::Window) {
1523 auto window = static_cast<QWebGLWindow *>(surface);
1524 if (s_contextData[id()].cachedParameters.isEmpty()) {
1525 auto future = window->d_func()->defaults.get_future();
1526 std::future_status status = std::future_status::timeout;
1527 while (status == std::future_status::timeout) {
1528 if (!QWebGLIntegrationPrivate::instance()->findClientData(surface))
1529 return false;
1530 status = future.wait_for(rel: std::chrono::milliseconds(100));
1531 }
1532 s_contextData[id()].cachedParameters = future.get();
1533 }
1534 event->addInt(value: window->window()->width());
1535 event->addInt(value: window->window()->height());
1536 event->addInt(value: window->winId());
1537 } else if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
1538 qCDebug(lc, "QWebGLContext::makeCurrent: QSurface::Offscreen not implemented");
1539 }
1540 QCoreApplication::postEvent(receiver: QWebGLIntegrationPrivate::instance()->webSocketServer, event);
1541 return true;
1542}
1543
1544void QWebGLContext::doneCurrent()
1545{
1546 postEvent<&QWebGL::makeCurrent>(arguments: 0, arguments: 0, arguments: 0, arguments: 0);
1547}
1548
1549bool QWebGLContext::isValid() const
1550{
1551 Q_D(const QWebGLContext);
1552 return d->id != -1;
1553}
1554
1555QFunctionPointer QWebGLContext::getProcAddress(const char *procName)
1556{
1557 const auto it = GLFunction::byName.find(akey: procName);
1558 return it != GLFunction::byName.end() ? (*it)->functionPointer : nullptr;
1559}
1560
1561int QWebGLContext::id() const
1562{
1563 Q_D(const QWebGLContext);
1564 return d->id;
1565}
1566
1567QPlatformSurface *QWebGLContext::currentSurface() const
1568{
1569 Q_D(const QWebGLContext);
1570 return d->currentSurface;
1571}
1572
1573QWebGLFunctionCall *QWebGLContext::createEvent(const QString &functionName, bool wait)
1574{
1575 auto context = QOpenGLContext::currentContext();
1576 Q_ASSERT(context);
1577 const auto handle = static_cast<QWebGLContext *>(context->handle());
1578 if (!handle)
1579 return nullptr;
1580 auto integrationPrivate = QWebGLIntegrationPrivate::instance();
1581 const auto clientData = integrationPrivate->findClientData(surface: handle->currentSurface());
1582 if (!clientData || !clientData->socket
1583 || clientData->socket->state() != QAbstractSocket::ConnectedState)
1584 return nullptr;
1585 const auto pointer = new QWebGLFunctionCall(functionName, handle->currentSurface(), wait);
1586 if (wait)
1587 QWebGLContextPrivate::waitingIds.insert(value: pointer->id());
1588
1589 return pointer;
1590}
1591
1592QVariant QWebGLContext::queryValue(int id)
1593{
1594 if (!QWebGLContextPrivate::waitingIds.contains(value: id)) {
1595 qCWarning(lc, "Unexpected id (%d)", id);
1596 return QVariant();
1597 }
1598
1599 static auto queryValue = [](int id)
1600 {
1601 lockMutex();
1602 waitCondition(time: 10);
1603 unlockMutex();
1604 return QWebGLIntegrationPrivate::instance()->webSocketServer->queryValue(id);
1605 };
1606
1607 const auto handle = static_cast<QWebGLContext *>(currentContext()->context()->handle());
1608 QVariant variant = queryValue(id);
1609 while (variant.isNull()) {
1610 auto integrationPrivate = QWebGLIntegrationPrivate::instance();
1611 const auto clientData = integrationPrivate->findClientData(surface: handle->currentSurface());
1612 if (!clientData || !clientData->socket
1613 || clientData->socket->state() != QAbstractSocket::ConnectedState)
1614 return QVariant();
1615 variant = queryValue(id);
1616 }
1617 QWebGLContextPrivate::waitingIds.remove(value: id);
1618 return variant;
1619}
1620
1621QStringList QWebGLContext::supportedFunctions()
1622{
1623 return GLFunction::remoteFunctionNames;
1624}
1625
1626quint8 QWebGLContext::functionIndex(const QString &functionName)
1627{
1628 const auto it = GLFunction::byName.find(akey: functionName);
1629 Q_ASSERT(it != GLFunction::byName.end());
1630 return (*it)->id;
1631}
1632
1633QT_END_NAMESPACE
1634

source code of qtwebglplugin/src/plugins/platforms/webgl/qwebglcontext.cpp