| 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 |  | 
| 54 | QT_BEGIN_NAMESPACE | 
| 55 |  | 
| 56 | static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.context" ) | 
| 57 |  | 
| 58 | class QWebGLContextPrivate | 
| 59 | { | 
| 60 | public: | 
| 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 |  | 
| 68 | QAtomicInt QWebGLContextPrivate::nextId(1); | 
| 69 | QSet<int> QWebGLContextPrivate::waitingIds; | 
| 70 |  | 
| 71 | struct PixelStorageModes | 
| 72 | { | 
| 73 |     PixelStorageModes() : unpackAlignment(4) { } | 
| 74 |     int unpackAlignment; | 
| 75 | }; | 
| 76 |  | 
| 77 | struct 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 |  | 
| 103 | static QHash<int, ContextData> s_contextData; | 
| 104 |  | 
| 105 | QWebGLContext *currentContext() | 
| 106 | { | 
| 107 |     auto context = QOpenGLContext::currentContext(); | 
| 108 |     if (context) | 
| 109 |         return static_cast<QWebGLContext *>(context->handle()); | 
| 110 |     return nullptr; | 
| 111 | } | 
| 112 |  | 
| 113 | ContextData *currentContextData() | 
| 114 | { | 
| 115 |     auto context = currentContext(); | 
| 116 |     if (context) | 
| 117 |         return &s_contextData[context->id()]; | 
| 118 |     return nullptr; | 
| 119 | } | 
| 120 |  | 
| 121 | inline 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 |  | 
| 176 | static void lockMutex() | 
| 177 | { | 
| 178 |     QWebGLIntegrationPrivate::instance()->webSocketServer->mutex()->lock(); | 
| 179 | } | 
| 180 |  | 
| 181 | static 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 |  | 
| 188 | static void unlockMutex() | 
| 189 | { | 
| 190 |     auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex(); | 
| 191 |     mutex->unlock(); | 
| 192 | } | 
| 193 |  | 
| 194 | static 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 |  | 
| 210 | static int vertexSize(GLint elementsPerVertex, GLenum type) | 
| 211 | { | 
| 212 |     return elementSize(type) * elementsPerVertex; | 
| 213 | } | 
| 214 |  | 
| 215 | static 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 |  | 
| 228 | static 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 |  | 
| 243 | template<class POINTER, class COUNT> | 
| 244 | inline 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 |  | 
| 254 | template<class SIZE> | 
| 255 | inline 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 |  | 
| 263 | template<class T> | 
| 264 | inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value) | 
| 265 | { | 
| 266 |     if (event) | 
| 267 |         event->add(value); | 
| 268 |     return event; | 
| 269 | } | 
| 270 |  | 
| 271 | template<class T, class... Ts> | 
| 272 | inline 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 |  | 
| 281 | template<class T> | 
| 282 | static 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 |  | 
| 294 | template<typename T> | 
| 295 | struct ParameterTypeTraits { | 
| 296 |     static int typeId() { return qMetaTypeId<T>(); } | 
| 297 |     static bool isArray() { return std::is_pointer<T>(); } | 
| 298 | }; | 
| 299 |  | 
| 300 | template<typename T> | 
| 301 | struct ParameterTypeTraits<T*> : ParameterTypeTraits<T> { | 
| 302 |     static int typeId() { return qMetaTypeId<T>(); } | 
| 303 | }; | 
| 304 |  | 
| 305 | template<typename T> | 
| 306 | struct ParameterTypeTraits<const T*> : ParameterTypeTraits<T*> { | 
| 307 | }; | 
| 308 |  | 
| 309 | template<typename T> | 
| 310 | struct ParameterTypeTraits<const T**> : ParameterTypeTraits<T*> { | 
| 311 | }; | 
| 312 |  | 
| 313 | struct 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 |  | 
| 354 | QHash<QString, const GLFunction *> GLFunction::byName; | 
| 355 | QStringList GLFunction::remoteFunctionNames; | 
| 356 |  | 
| 357 | template<const GLFunction *Function> | 
| 358 | static 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 |  | 
| 371 | static 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 |  | 
| 378 | template<const GLFunction *Function, class... Ts> | 
| 379 | static 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 |  | 
| 391 | template<const GLFunction *Function> | 
| 392 | static 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 |  | 
| 403 | template<const GLFunction *Function, class... Ts> | 
| 404 | inline int postEventImpl(bool wait, Ts&&... arguments) | 
| 405 | { | 
| 406 |     return createEventAndPostImpl<Function>(wait, arguments...); | 
| 407 | } | 
| 408 |  | 
| 409 | template<const GLFunction *Function> | 
| 410 | inline int postEvent(bool wait) | 
| 411 | { | 
| 412 |     return createEventAndPostImpl<Function>(wait); | 
| 413 | } | 
| 414 |  | 
| 415 | template<const GLFunction *Function, class...Ts> | 
| 416 | inline int postEvent(Ts&&... arguments) | 
| 417 | { | 
| 418 |     return postEventImpl<Function>(false, arguments...); | 
| 419 | } | 
| 420 |  | 
| 421 | template<const GLFunction *Function, class ReturnType, class...Ts> | 
| 422 | static 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 |  | 
| 429 | namespace 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 |  | 
| 504 | QWEBGL_FUNCTION(activeTexture, void, glActiveTexture, | 
| 505 |                 (GLenum) texture) | 
| 506 | { | 
| 507 |     postEvent<&activeTexture>(arguments&: texture); | 
| 508 |     currentContextData()->activeTextureUnit = texture; | 
| 509 | } | 
| 510 |  | 
| 511 | QWEBGL_FUNCTION_POSTEVENT(attachShader, glAttachShader, | 
| 512 |                           (GLuint) program, (GLuint) shader) | 
| 513 | QWEBGL_FUNCTION_POSTEVENT(bindAttribLocation, glBindAttribLocation, | 
| 514 |                           (GLuint) program, (GLuint) index, (const GLchar *) name) | 
| 515 |  | 
| 516 | QWEBGL_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 |  | 
| 526 | QWEBGL_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 |  | 
| 534 | QWEBGL_FUNCTION_POSTEVENT(bindRenderbuffer, glBindRenderbuffer, | 
| 535 |                           (GLenum) target, (GLuint) renderbuffer) | 
| 536 |  | 
| 537 | QWEBGL_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 |  | 
| 545 | QWEBGL_FUNCTION_POSTEVENT(blendColor, glBlendColor, | 
| 546 |                           (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha) | 
| 547 |  | 
| 548 | QWEBGL_FUNCTION_POSTEVENT(blendEquation, glBlendEquation, | 
| 549 |                           (GLenum) mode) | 
| 550 |  | 
| 551 | QWEBGL_FUNCTION_POSTEVENT(blendEquationSeparate, glBlendEquationSeparate, | 
| 552 |                           (GLenum) modeRGB, (GLenum) modeAlpha) | 
| 553 |  | 
| 554 | QWEBGL_FUNCTION_POSTEVENT(blendFunc, glBlendFunc, | 
| 555 |                           (GLenum) sfactor, (GLenum) dfactor) | 
| 556 |  | 
| 557 | QWEBGL_FUNCTION_POSTEVENT(blendFuncSeparate, glBlendFuncSeparate, | 
| 558 |                           (GLenum) sfactorRGB, (GLenum) dfactorRGB, | 
| 559 |                           (GLenum) sfactorAlpha, (GLenum) dfactorAlpha) | 
| 560 |  | 
| 561 | QWEBGL_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 |  | 
| 568 | QWEBGL_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 |  | 
| 574 | QWEBGL_FUNCTION(checkFramebufferStatus, GLenum, glCheckFramebufferStatus, | 
| 575 |                 (GLenum) target) | 
| 576 | { | 
| 577 |     return postEventAndQuery<&checkFramebufferStatus>(defaultValue: 0u, arguments&: target); | 
| 578 | } | 
| 579 |  | 
| 580 | QWEBGL_FUNCTION_POSTEVENT(clear, glClear, (GLbitfield) mask) | 
| 581 |  | 
| 582 | QWEBGL_FUNCTION_POSTEVENT(clearColor, glClearColor, | 
| 583 |                           (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha) | 
| 584 |  | 
| 585 | QWEBGL_FUNCTION_POSTEVENT(clearDepthf, glClearDepthf, | 
| 586 |                           (GLfloat) d) | 
| 587 |  | 
| 588 | QWEBGL_FUNCTION_POSTEVENT(clearStencil, glClearStencil, | 
| 589 |                           (GLint) s) | 
| 590 |  | 
| 591 | QWEBGL_FUNCTION_POSTEVENT(colorMask, glColorMask, | 
| 592 |                           (GLboolean) red, (GLboolean) green, (GLboolean) blue, (GLboolean) alpha) | 
| 593 |  | 
| 594 | QWEBGL_FUNCTION_POSTEVENT(compileShader, glCompileShader, (GLuint) shader) | 
| 595 |  | 
| 596 | QWEBGL_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 |  | 
| 605 | QWEBGL_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 |  | 
| 614 | QWEBGL_FUNCTION_POSTEVENT(copyTexImage2D, glCopyTexImage2D, | 
| 615 |                           (GLenum) target, (GLint) level, (GLenum) internalformat, | 
| 616 |                           (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height, (GLint) border) | 
| 617 |  | 
| 618 | QWEBGL_FUNCTION_POSTEVENT(copyTexSubImage2D, glCopyTexSubImage2D, | 
| 619 |                           (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset, | 
| 620 |                           (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height) | 
| 621 |  | 
| 622 | QWEBGL_FUNCTION_NO_PARAMS(createProgram, GLuint, glCreateProgram) | 
| 623 | { | 
| 624 |     return postEventAndQuery<&createProgram>(defaultValue: 0u); | 
| 625 | } | 
| 626 |  | 
| 627 | QWEBGL_FUNCTION(createShader, GLuint, glCreateShader, | 
| 628 |                 (GLenum) type) | 
| 629 | { | 
| 630 |     return postEventAndQuery<&createShader>(defaultValue: 0u, arguments&: type); | 
| 631 | } | 
| 632 |  | 
| 633 | QWEBGL_FUNCTION_POSTEVENT(cullFace, glCullFace, | 
| 634 |                           (GLenum) mode) | 
| 635 |  | 
| 636 | QWEBGL_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 |  | 
| 648 | QWEBGL_FUNCTION(deleteFramebuffers, void, glDeleteFramebuffers, | 
| 649 |                 (GLsizei) n, (const GLuint *) framebuffers) | 
| 650 | { | 
| 651 |     postEvent<&deleteFramebuffers>(arguments: qMakePair(x: framebuffers, y: n)); | 
| 652 | } | 
| 653 |  | 
| 654 | QWEBGL_FUNCTION_POSTEVENT(deleteProgram, glDeleteProgram, | 
| 655 |                           (GLuint) program) | 
| 656 |  | 
| 657 | QWEBGL_FUNCTION(deleteRenderbuffers, void, glDeleteRenderbuffers, | 
| 658 |                 (GLsizei) n, (const GLuint *) renderbuffers) | 
| 659 | { | 
| 660 |     postEvent<&deleteRenderbuffers>(arguments: qMakePair(x: renderbuffers, y: n)); | 
| 661 | } | 
| 662 |  | 
| 663 | QWEBGL_FUNCTION_POSTEVENT(deleteShader, glDeleteShader, | 
| 664 |                           (GLuint) shader) | 
| 665 |  | 
| 666 | QWEBGL_FUNCTION(deleteTextures, void, glDeleteTextures, | 
| 667 |                 (GLsizei) n, (const GLuint *) textures) | 
| 668 | { | 
| 669 |     postEvent<&deleteTextures>(arguments: qMakePair(x: textures, y: n)); | 
| 670 | } | 
| 671 |  | 
| 672 | QWEBGL_FUNCTION_POSTEVENT(depthFunc, glDepthFunc, | 
| 673 |                           (GLenum) func) | 
| 674 |  | 
| 675 | QWEBGL_FUNCTION_POSTEVENT(depthMask, glDepthMask, | 
| 676 |                           (GLboolean) flag) | 
| 677 |  | 
| 678 | QWEBGL_FUNCTION_POSTEVENT(depthRangef, glDepthRangef, | 
| 679 |                           (GLfloat) n, (GLfloat) f) | 
| 680 |  | 
| 681 | QWEBGL_FUNCTION_POSTEVENT(detachShader, glDetachShader, | 
| 682 |                           (GLuint) program, (GLuint) shader) | 
| 683 |  | 
| 684 | QWEBGL_FUNCTION(disableVertexAttribArray, void, glDisableVertexAttribArray, | 
| 685 |                 (GLuint) index) | 
| 686 | { | 
| 687 |     postEvent<&disableVertexAttribArray>(arguments&: index); | 
| 688 |     currentContextData()->vertexAttribPointers[index].enabled = false; | 
| 689 | } | 
| 690 |  | 
| 691 | QWEBGL_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 |  | 
| 705 | QWEBGL_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 |  | 
| 723 | QWEBGL_FUNCTION(enableVertexAttribArray, void, glEnableVertexAttribArray, | 
| 724 |                 (GLuint) index) | 
| 725 | { | 
| 726 |     postEvent<&enableVertexAttribArray>(arguments&: index); | 
| 727 |     currentContextData()->vertexAttribPointers[index].enabled = true; | 
| 728 | } | 
| 729 |  | 
| 730 | QWEBGL_FUNCTION_NO_PARAMS(finish, void, glFinish) | 
| 731 | { | 
| 732 |     postEvent<&finish>(); | 
| 733 | } | 
| 734 |  | 
| 735 | QWEBGL_FUNCTION_NO_PARAMS(flush, void, glFlush) | 
| 736 | { | 
| 737 |     postEvent<&flush>(); | 
| 738 | } | 
| 739 |  | 
| 740 | QWEBGL_FUNCTION_POSTEVENT(framebufferRenderbuffer, glFramebufferRenderbuffer, | 
| 741 |                           (GLenum) target, (GLenum) attachment, | 
| 742 |                           (GLenum) renderbuffertarget, (GLuint) renderbuffer) | 
| 743 |  | 
| 744 | QWEBGL_FUNCTION_POSTEVENT(framebufferTexture2D, glFramebufferTexture2D, | 
| 745 |                           (GLenum) target, (GLenum) attachment, (GLenum) textarget, | 
| 746 |                           (GLuint) texture, (GLint) level) | 
| 747 |  | 
| 748 | QWEBGL_FUNCTION_POSTEVENT(frontFace, glFrontFace, | 
| 749 |                           (GLenum) mode) | 
| 750 |  | 
| 751 | QWEBGL_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 |  | 
| 761 | QWEBGL_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 |  | 
| 771 | QWEBGL_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 |  | 
| 781 | QWEBGL_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 |  | 
| 791 | QWEBGL_FUNCTION_POSTEVENT(generateMipmap, glGenerateMipmap, | 
| 792 |                           (GLenum) target) | 
| 793 |  | 
| 794 | QWEBGL_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 |  | 
| 817 | QWEBGL_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 |  | 
| 840 | QWEBGL_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 |  | 
| 849 | QWEBGL_FUNCTION(getAttribLocation, GLint, glGetAttribLocation, | 
| 850 |                 (GLuint) program, (const GLchar *) name) | 
| 851 | { | 
| 852 |     return postEventAndQuery<&getAttribLocation>(defaultValue: -1, arguments&: program, arguments&: name); | 
| 853 | } | 
| 854 |  | 
| 855 | QWEBGL_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 |  | 
| 876 | QWEBGL_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 |  | 
| 928 | QWEBGL_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 |  | 
| 940 | QWEBGL_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 |  | 
| 952 | QWEBGL_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 |  | 
| 964 | QWEBGL_FUNCTION(getBufferParameteriv, void, glGetBufferParameteriv, | 
| 965 |                 (GLenum) target, (GLenum) pname, (GLint *) params) | 
| 966 | { | 
| 967 |     *params = postEventAndQuery<&getBufferParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname); | 
| 968 | } | 
| 969 |  | 
| 970 | QWEBGL_FUNCTION_NO_PARAMS(getError, GLenum, glGetError) | 
| 971 | { | 
| 972 |     return postEventAndQuery<&getError>(GL_NO_ERROR); | 
| 973 | } | 
| 974 |  | 
| 975 | QWEBGL_FUNCTION(getParameter, void, glGetFloatv, | 
| 976 |                 (GLenum) pname, (GLfloat*) data) | 
| 977 | { | 
| 978 |     *data = postEventAndQuery<&getParameter>(defaultValue: 0.0, arguments&: pname); | 
| 979 | } | 
| 980 |  | 
| 981 | QWEBGL_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 |  | 
| 987 | QWEBGL_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 |  | 
| 996 | QWEBGL_FUNCTION(getProgramiv, void, glGetProgramiv, | 
| 997 |                 (GLuint) program, (GLenum) pname, (GLint *) params) | 
| 998 | { | 
| 999 |     *params = postEventAndQuery<&getProgramiv>(defaultValue: 0, arguments&: program, arguments&: pname); | 
| 1000 | } | 
| 1001 |  | 
| 1002 | QWEBGL_FUNCTION(getRenderbufferParameteriv, void, glGetRenderbufferParameteriv, | 
| 1003 |                 (GLenum) target, (GLenum) pname, (GLint *) params) | 
| 1004 | { | 
| 1005 |     *params = postEventAndQuery<&getRenderbufferParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname); | 
| 1006 | } | 
| 1007 |  | 
| 1008 | QWEBGL_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 |  | 
| 1017 | QWEBGL_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 |  | 
| 1034 | QWEBGL_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 |  | 
| 1043 | QWEBGL_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 |  | 
| 1061 | QWEBGL_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 |  | 
| 1067 | QWEBGL_FUNCTION(getTexParameteriv, void, glGetTexParameteriv, | 
| 1068 |                 (GLenum) target, (GLenum) pname, (GLint *) params) | 
| 1069 | { | 
| 1070 |     *params = postEventAndQuery<&getTexParameteriv>(defaultValue: 0, arguments&: target, arguments&: pname); | 
| 1071 | } | 
| 1072 |  | 
| 1073 | QWEBGL_FUNCTION(getUniformLocation, GLint, glGetUniformLocation, | 
| 1074 |                 (GLuint) program, (const GLchar *) name) | 
| 1075 | { | 
| 1076 |     return postEventAndQuery<&getUniformLocation>(defaultValue: -1, arguments&: program, arguments&: name); | 
| 1077 | } | 
| 1078 |  | 
| 1079 | QWEBGL_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 |  | 
| 1085 | QWEBGL_FUNCTION(getUniformiv, void, glGetUniformiv, | 
| 1086 |                 (GLuint) program, (GLint) location, (GLint *) params) | 
| 1087 | { | 
| 1088 |     *params = postEventAndQuery<&getUniformiv>(defaultValue: 0, arguments&: program, arguments&: location); | 
| 1089 | } | 
| 1090 |  | 
| 1091 | QWEBGL_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 |  | 
| 1101 | QWEBGL_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 |  | 
| 1107 | QWEBGL_FUNCTION(getVertexAttribiv, void, glGetVertexAttribiv, | 
| 1108 |                 (GLuint) index, (GLenum) pname, (GLint *) params) | 
| 1109 | { | 
| 1110 |     *params = postEventAndQuery<&getVertexAttribiv>(defaultValue: 0, arguments&: index, arguments&: pname); | 
| 1111 | } | 
| 1112 |  | 
| 1113 | QWEBGL_FUNCTION_POSTEVENT(hint, glHint, | 
| 1114 |                           (GLenum) target, (GLenum) mode) | 
| 1115 |  | 
| 1116 | QWEBGL_FUNCTION(isBuffer, GLboolean, glIsBuffer, | 
| 1117 |                 (GLuint) buffer) | 
| 1118 | { | 
| 1119 |     return postEventAndQuery<&isBuffer>(GL_FALSE, arguments&: buffer); | 
| 1120 | } | 
| 1121 |  | 
| 1122 | QWEBGL_FUNCTION(isEnabled, GLboolean, glIsEnabled, | 
| 1123 |                 (GLenum) cap) | 
| 1124 | { | 
| 1125 |     return postEventAndQuery<&isEnabled>(GL_FALSE, arguments&: cap); | 
| 1126 | } | 
| 1127 |  | 
| 1128 | QWEBGL_FUNCTION(isFramebuffer, GLboolean, glIsFramebuffer, | 
| 1129 |                 (GLuint) framebuffer) | 
| 1130 | { | 
| 1131 |     return postEventAndQuery<&isFramebuffer>(GL_FALSE, arguments&: framebuffer); | 
| 1132 | } | 
| 1133 |  | 
| 1134 | QWEBGL_FUNCTION(isProgram, GLboolean, glIsProgram, | 
| 1135 |                 (GLuint) program) | 
| 1136 | { | 
| 1137 |     return postEventAndQuery<&isProgram>(GL_FALSE, arguments&: program); | 
| 1138 | } | 
| 1139 |  | 
| 1140 | QWEBGL_FUNCTION(isRenderbuffer, GLboolean, glIsRenderbuffer, | 
| 1141 |                 (GLuint) renderbuffer) | 
| 1142 | { | 
| 1143 |     return postEventAndQuery<&isRenderbuffer>(GL_FALSE, arguments&: renderbuffer); | 
| 1144 | } | 
| 1145 |  | 
| 1146 | QWEBGL_FUNCTION(isShader, GLboolean, glIsShader, | 
| 1147 |                 (GLuint) shader) | 
| 1148 | { | 
| 1149 |     return postEventAndQuery<&isShader>(GL_FALSE, arguments&: shader); | 
| 1150 | } | 
| 1151 |  | 
| 1152 | QWEBGL_FUNCTION(isTexture, GLboolean, glIsTexture, | 
| 1153 |                 (GLuint) texture) | 
| 1154 | { | 
| 1155 |     return postEventAndQuery<&isTexture>(GL_FALSE, arguments&: texture); | 
| 1156 | } | 
| 1157 |  | 
| 1158 | QWEBGL_FUNCTION_POSTEVENT(lineWidth, glLineWidth, | 
| 1159 |                           (GLfloat) width) | 
| 1160 |  | 
| 1161 | QWEBGL_FUNCTION_POSTEVENT(linkProgram, glLinkProgram, | 
| 1162 |                           (GLuint) program) | 
| 1163 |  | 
| 1164 | QWEBGL_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 |  | 
| 1173 | QWEBGL_FUNCTION_POSTEVENT(polygonOffset, glPolygonOffset, | 
| 1174 |                           (GLfloat) factor, (GLfloat) units) | 
| 1175 |  | 
| 1176 | QWEBGL_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 |  | 
| 1186 | QWEBGL_FUNCTION_NO_PARAMS(releaseShaderCompiler, void, glReleaseShaderCompiler) | 
| 1187 | { | 
| 1188 |     postEvent<&releaseShaderCompiler>(); | 
| 1189 | } | 
| 1190 |  | 
| 1191 | QWEBGL_FUNCTION_POSTEVENT(renderbufferStorage, glRenderbufferStorage, | 
| 1192 |                           (GLenum) target, (GLenum) internalformat, | 
| 1193 |                           (GLsizei) width, (GLsizei) height) | 
| 1194 |  | 
| 1195 | QWEBGL_FUNCTION_POSTEVENT(sampleCoverage, glSampleCoverage, | 
| 1196 |                           (GLfloat) value, (GLboolean) invert) | 
| 1197 |  | 
| 1198 | QWEBGL_FUNCTION_POSTEVENT(scissor, glScissor, | 
| 1199 |                           (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height) | 
| 1200 |  | 
| 1201 | QWEBGL_FUNCTION(shaderBinary, void, glShaderBinary, | 
| 1202 |                 (GLsizei), (const GLuint *), (GLenum), (const void *), (GLsizei)) | 
| 1203 | { | 
| 1204 |     qFatal(msg: "WebGL does not allow precompiled shaders" ); | 
| 1205 | } | 
| 1206 |  | 
| 1207 | QWEBGL_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 |  | 
| 1222 | QWEBGL_FUNCTION_POSTEVENT(stencilFunc, glStencilFunc, | 
| 1223 |                           (GLenum) func, (GLint) ref, (GLuint) mask) | 
| 1224 |  | 
| 1225 | QWEBGL_FUNCTION_POSTEVENT(stencilFuncSeparate, glStencilFuncSeparate, | 
| 1226 |                           (GLenum) face, (GLenum) func, (GLint) ref, (GLuint) mask) | 
| 1227 |  | 
| 1228 | QWEBGL_FUNCTION_POSTEVENT(stencilMask, glStencilMask, | 
| 1229 |                           (GLuint) mask) | 
| 1230 |  | 
| 1231 | QWEBGL_FUNCTION_POSTEVENT(stencilMaskSeparate, glStencilMaskSeparate, | 
| 1232 |                           (GLenum) face, (GLuint) mask) | 
| 1233 |  | 
| 1234 | QWEBGL_FUNCTION_POSTEVENT(stencilOp, glStencilOp, | 
| 1235 |                           (GLenum) fail, (GLenum) zfail, (GLenum) zpass) | 
| 1236 |  | 
| 1237 | QWEBGL_FUNCTION_POSTEVENT(stencilOpSeparate, glStencilOpSeparate, | 
| 1238 |                           (GLenum) face, (GLenum) sfail, (GLenum) dpfail, (GLenum) dppass) | 
| 1239 |  | 
| 1240 | QWEBGL_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 |  | 
| 1263 | QWEBGL_FUNCTION_POSTEVENT(texParameterf, glTexParameterf, | 
| 1264 |                           (GLenum) target, (GLenum) pname, (GLfloat) param) | 
| 1265 |  | 
| 1266 | QWEBGL_FUNCTION(texParameterfv, void, glTexParameterfv, | 
| 1267 |                 (GLenum), (GLenum), (const GLfloat *)) | 
| 1268 | { | 
| 1269 |     qFatal(msg: "glTexParameterfv not implemented" ); | 
| 1270 | } | 
| 1271 |  | 
| 1272 | QWEBGL_FUNCTION_POSTEVENT(texParameteri, glTexParameteri, | 
| 1273 |                           (GLenum) target, (GLenum) pname, (GLint) param) | 
| 1274 |  | 
| 1275 | QWEBGL_FUNCTION(texParameteriv, void, glTexParameteriv, | 
| 1276 |                 (GLenum), (GLenum), (const GLint *)) | 
| 1277 | { | 
| 1278 |     qFatal(msg: "glTexParameteriv not implemented" ); | 
| 1279 | } | 
| 1280 |  | 
| 1281 | QWEBGL_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 |  | 
| 1293 | QWEBGL_FUNCTION_POSTEVENT(uniform1f, glUniform1f, (GLint) location, (GLfloat) v0) | 
| 1294 |  | 
| 1295 | QWEBGL_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 |  | 
| 1301 | QWEBGL_FUNCTION_POSTEVENT(uniform1i, glUniform1i, | 
| 1302 |                           (GLint) location, (GLint) v0) | 
| 1303 |  | 
| 1304 | QWEBGL_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 |  | 
| 1310 | QWEBGL_FUNCTION_POSTEVENT(uniform2f, glUniform2f, | 
| 1311 |                           (GLint) location, (GLfloat) v0, (GLfloat) v1) | 
| 1312 |  | 
| 1313 | QWEBGL_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 |  | 
| 1319 | QWEBGL_FUNCTION_POSTEVENT(uniform2i, glUniform2i, | 
| 1320 |                           (GLint) location, (GLint) v0, (GLint) v1) | 
| 1321 |  | 
| 1322 | QWEBGL_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 |  | 
| 1328 | QWEBGL_FUNCTION_POSTEVENT(uniform3f, glUniform3f, | 
| 1329 |                           (GLint) location, (GLfloat) v0, (GLfloat) v1, (GLfloat) v2) | 
| 1330 |  | 
| 1331 | QWEBGL_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 |  | 
| 1337 | QWEBGL_FUNCTION_POSTEVENT(uniform3i, glUniform3i, | 
| 1338 |                           (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2) | 
| 1339 |  | 
| 1340 | QWEBGL_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 |  | 
| 1346 | QWEBGL_FUNCTION_POSTEVENT(uniform4f, glUniform4f, | 
| 1347 |                           (GLint) location, (GLfloat) v0, (GLfloat) v1, | 
| 1348 |                           (GLfloat) v2, (GLfloat) v3) | 
| 1349 |  | 
| 1350 | QWEBGL_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 |  | 
| 1356 | QWEBGL_FUNCTION_POSTEVENT(uniform4i, glUniform4i, | 
| 1357 |                           (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2, (GLint) v3) | 
| 1358 |  | 
| 1359 | QWEBGL_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 |  | 
| 1365 | QWEBGL_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 |  | 
| 1371 | QWEBGL_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 |  | 
| 1377 | QWEBGL_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 |  | 
| 1383 | QWEBGL_FUNCTION_POSTEVENT(useProgram, glUseProgram, | 
| 1384 |                           (GLuint) program) | 
| 1385 |  | 
| 1386 | QWEBGL_FUNCTION_POSTEVENT(validateProgram, glValidateProgram, | 
| 1387 |                           (GLuint) program) | 
| 1388 |  | 
| 1389 | QWEBGL_FUNCTION_POSTEVENT(vertexAttrib1f, glVertexAttrib1f, | 
| 1390 |                           (GLuint) index, (GLfloat) x) | 
| 1391 |  | 
| 1392 | QWEBGL_FUNCTION(vertexAttrib1fv, void, glVertexAttrib1fv, | 
| 1393 |                 (GLuint) index, (const GLfloat *) v) | 
| 1394 | { | 
| 1395 |     postEvent<&vertexAttrib1fv>(arguments&: index, arguments: v[0]); | 
| 1396 | } | 
| 1397 |  | 
| 1398 | QWEBGL_FUNCTION_POSTEVENT(vertexAttrib2f, glVertexAttrib2f, | 
| 1399 |                           (GLuint) index, (GLfloat) x, (GLfloat) y) | 
| 1400 |  | 
| 1401 | QWEBGL_FUNCTION(vertexAttrib2fv, void, glVertexAttrib2fv, | 
| 1402 |                 (GLuint) index, (const GLfloat *) v) | 
| 1403 | { | 
| 1404 |     postEvent<&vertexAttrib2fv>(arguments&: index, arguments: v[0], arguments: v[1]); | 
| 1405 | } | 
| 1406 |  | 
| 1407 | QWEBGL_FUNCTION_POSTEVENT(vertexAttrib3f, glVertexAttrib3f, | 
| 1408 |                           (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z) | 
| 1409 |  | 
| 1410 | QWEBGL_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 |  | 
| 1416 | QWEBGL_FUNCTION_POSTEVENT(vertexAttrib4f, glVertexAttrib4f, | 
| 1417 |                           (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w) | 
| 1418 |  | 
| 1419 | QWEBGL_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 |  | 
| 1425 | QWEBGL_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 |  | 
| 1442 | QWEBGL_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 |  | 
| 1451 | QWEBGL_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 |  | 
| 1456 | QWEBGL_FUNCTION_POSTEVENT(renderbufferStorageMultisampleEXT, glRenderbufferStorageMultisampleEXT, | 
| 1457 |                 (GLenum) target, (GLsizei) samples, (GLenum) internalformat, (GLsizei) width, | 
| 1458 |                 (GLsizei) height) | 
| 1459 |  | 
| 1460 | QWEBGL_FUNCTION(getTexLevelParameteriv, void, glGetTexLevelParameteriv, | 
| 1461 |                 (GLenum), (GLint), (GLenum), (GLint *)) | 
| 1462 | { | 
| 1463 |     qFatal(msg: "glGetTexLevelParameteriv not supported" ); | 
| 1464 | } | 
| 1465 |  | 
| 1466 | #undef QWEBGL_FUNCTION | 
| 1467 |  | 
| 1468 | extern const GLFunction makeCurrent("makeCurrent" ); | 
| 1469 | extern const GLFunction swapBuffers("swapBuffers" ); | 
| 1470 |  | 
| 1471 | } | 
| 1472 |  | 
| 1473 | QWebGLContext::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 |  | 
| 1483 | QWebGLContext::~QWebGLContext() | 
| 1484 | {} | 
| 1485 |  | 
| 1486 | QSurfaceFormat QWebGLContext::format() const | 
| 1487 | { | 
| 1488 |     Q_D(const QWebGLContext); | 
| 1489 |     return d->surfaceFormat; | 
| 1490 | } | 
| 1491 |  | 
| 1492 | void 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 |  | 
| 1504 | bool 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 |  | 
| 1544 | void QWebGLContext::doneCurrent() | 
| 1545 | { | 
| 1546 |     postEvent<&QWebGL::makeCurrent>(arguments: 0, arguments: 0, arguments: 0, arguments: 0); | 
| 1547 | } | 
| 1548 |  | 
| 1549 | bool QWebGLContext::isValid() const | 
| 1550 | { | 
| 1551 |     Q_D(const QWebGLContext); | 
| 1552 |     return d->id != -1; | 
| 1553 | } | 
| 1554 |  | 
| 1555 | QFunctionPointer 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 |  | 
| 1561 | int QWebGLContext::id() const | 
| 1562 | { | 
| 1563 |     Q_D(const QWebGLContext); | 
| 1564 |     return d->id; | 
| 1565 | } | 
| 1566 |  | 
| 1567 | QPlatformSurface *QWebGLContext::currentSurface() const | 
| 1568 | { | 
| 1569 |     Q_D(const QWebGLContext); | 
| 1570 |     return d->currentSurface; | 
| 1571 | } | 
| 1572 |  | 
| 1573 | QWebGLFunctionCall *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 |  | 
| 1592 | QVariant 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 |  | 
| 1621 | QStringList QWebGLContext::supportedFunctions() | 
| 1622 | { | 
| 1623 |     return GLFunction::remoteFunctionNames; | 
| 1624 | } | 
| 1625 |  | 
| 1626 | quint8 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 |  | 
| 1633 | QT_END_NAMESPACE | 
| 1634 |  |