| 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 | |