/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt WebGL module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwebglcontext.h" #include "qwebglfunctioncall.h" #include "qwebglintegration.h" #include "qwebglintegration_p.h" #include "qwebglwebsocketserver.h" #include "qwebglwindow.h" #include "qwebglwindow_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.context") class QWebGLContextPrivate { public: static QAtomicInt nextId; static QSet waitingIds; union { int id = -1; qintptr padded; }; QPlatformSurface *currentSurface = nullptr; QSurfaceFormat surfaceFormat; }; QAtomicInt QWebGLContextPrivate::nextId(1); QSet QWebGLContextPrivate::waitingIds; struct PixelStorageModes { PixelStorageModes() : unpackAlignment(4) { } int unpackAlignment; }; struct ContextData { GLuint currentProgram = 0; GLuint boundArrayBuffer = 0; GLuint boundElementArrayBuffer = 0; GLuint boundTexture2D = 0; GLenum activeTextureUnit = GL_TEXTURE0; GLuint boundDrawFramebuffer = 0; // GLuint boundReadFramebuffer = 0; GLuint unpackAlignment = 4; struct VertexAttrib { VertexAttrib() : arrayBufferBinding(0), pointer(nullptr), enabled(false) { } GLuint arrayBufferBinding; const void *pointer; bool enabled; GLint size; GLenum type; bool normalized; GLsizei stride; }; QHash vertexAttribPointers; QHash images; PixelStorageModes pixelStorage; QMap cachedParameters; QSet stringCache; }; static QHash s_contextData; QWebGLContext *currentContext() { auto context = QOpenGLContext::currentContext(); if (context) return static_cast(context->handle()); return nullptr; } ContextData *currentContextData() { auto context = currentContext(); if (context) return &s_contextData[context->id()]; return nullptr; } inline int imageSize(GLsizei width, GLsizei height, GLenum format, GLenum type, const PixelStorageModes &pixelStorage) { Q_UNUSED(pixelStorage); // TODO: Support different pixelStorage formats static struct BppTabEntry { GLenum format; GLenum type; int bytesPerPixel; } bppTab[] = { { GL_RGBA, GL_UNSIGNED_BYTE, 4 }, { GL_RGBA, GL_BYTE, 4 }, { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 2 }, { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 2 }, { GL_RGBA, GL_FLOAT, 16 }, { GL_RGB, GL_UNSIGNED_BYTE, 3 }, { GL_RGB, GL_BYTE, 3 }, { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2 }, { GL_RGB, GL_FLOAT, 12 }, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 2 }, { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 4 }, { GL_DEPTH_COMPONENT, GL_FLOAT, 4 }, { GL_RGBA, GL_UNSIGNED_BYTE, 4 }, { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 2 }, { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 2 }, { GL_RGB, GL_UNSIGNED_BYTE, 3 }, { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2 }, { GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 2 }, { GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 }, { GL_ALPHA, GL_UNSIGNED_BYTE, 1 }, { GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4 }, { GL_BGRA_EXT, GL_BYTE, 4 }, { GL_BGRA_EXT, GL_UNSIGNED_SHORT_4_4_4_4, 2 }, { GL_BGRA_EXT, GL_UNSIGNED_SHORT_5_5_5_1, 2 }, { GL_BGRA_EXT, GL_FLOAT, 16 } }; int bytesPerPixel = 0; for (size_t i = 0; i < sizeof(bppTab) / sizeof(BppTabEntry); ++i) { if (bppTab[i].format == format && bppTab[i].type == type) { bytesPerPixel = bppTab[i].bytesPerPixel; break; } } const int rowSize = width * bytesPerPixel; if (!bytesPerPixel) qCWarning(lc, "Unknown texture format %x - %x", format, type); return rowSize * height; } static void lockMutex() { QWebGLIntegrationPrivate::instance()->webSocketServer->mutex()->lock(); } static void waitCondition(unsigned long time = ULONG_MAX) { auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex(); auto waitCondition = QWebGLIntegrationPrivate::instance()->webSocketServer->waitCondition(); waitCondition->wait(mutex, time); } static void unlockMutex() { auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex(); mutex->unlock(); } static int elementSize(GLenum type) { switch (type) { case GL_SHORT: case GL_UNSIGNED_SHORT: return 2; case GL_FLOAT: case GL_FIXED: case GL_INT: case GL_UNSIGNED_INT: return 4; default: return 1; } } static int vertexSize(GLint elementsPerVertex, GLenum type) { return elementSize(type) * elementsPerVertex; } static int bufferSize(GLsizei count, GLint elemsPerVertex, GLenum type, GLsizei stride) { if (count == 0) return 0; int vsize = vertexSize(elemsPerVertex, type); if (stride == 0) stride = vsize; return vsize + (count - 1) * stride; } static void setVertexAttribs(QWebGLFunctionCall *event, GLsizei count) { event->addInt(currentContextData()->vertexAttribPointers.count()); const auto &vertexAttribPointers = currentContextData()->vertexAttribPointers; for (auto it = vertexAttribPointers.cbegin(), end = vertexAttribPointers.cend(); it != end; ++it) { const ContextData::VertexAttrib &va(it.value()); if (va.arrayBufferBinding == 0 && va.enabled) { int len = bufferSize(count, va.size, va.type, va.stride); event->addParameters(it.key(), va.size, int(va.type), va.normalized, va.stride); // found an enabled vertex attribute that was specified with a client-side pointer event->addData(QByteArray(reinterpret_cast(va.pointer), len)); } } } template inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const QPair &elements) { QVariantList list; for (auto i = 0; i < elements.second; ++i) list.append(QVariant::fromValue(elements.first[i])); event->addList(list); return event; } template inline void addHelper(QWebGLFunctionCall *event, const QPair &elements) { QVariantList list; for (auto i = 0; i < elements.second; ++i) list.append(QVariant::fromValue(elements.first[i])); event->addList(list); } template inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value) { if (event) event->add(value); return event; } template inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value, const Ts&... rest) { if (event) { event->add(value); addHelper(event, rest...); } return event; } template static T queryValue(int id, const T &defaultValue = T()) { const auto variant = currentContext()->queryValue(id); if (variant.isNull()) return defaultValue; if (!variant.canConvert()) { qCWarning(lc, "Cannot convert %s to " QT_STRINGIFY(T), variant.typeName()); return defaultValue; } return variant.value(); } template struct ParameterTypeTraits { static int typeId() { return qMetaTypeId(); } static bool isArray() { return std::is_pointer(); } }; template struct ParameterTypeTraits : ParameterTypeTraits { static int typeId() { return qMetaTypeId(); } }; template struct ParameterTypeTraits : ParameterTypeTraits { }; template struct ParameterTypeTraits : ParameterTypeTraits { }; struct GLFunction { struct Parameter { Parameter() {} Parameter(const QString &name, const QString &typeName, int typeId, bool isArray) : name(name), typeName(typeName), typeId(typeId), isArray(isArray) {} QString name; QString typeName; int typeId; bool isArray; }; static QHash byName; static QStringList remoteFunctionNames; using ParameterList = QList; GLFunction(const QString &remoteName, const QString &localName, QFunctionPointer functionPointer, ParameterList parameters = ParameterList()) : remoteName(remoteName), localName(localName), functionPointer(functionPointer), parameters(parameters) { Q_ASSERT(!byName.contains(localName)); byName.insert(localName, this); id = remoteFunctionNames.size(); Q_ASSERT(remoteFunctionNames.size() <= std::numeric_limits::max()); remoteFunctionNames.append(remoteName); Q_ASSERT(byName.size() == remoteFunctionNames.size()); } GLFunction(const QString &name) : GLFunction(name, name, nullptr) {} quint8 id; const QString remoteName; const QString localName; const QFunctionPointer functionPointer; const ParameterList parameters; }; QHash GLFunction::byName; QStringList GLFunction::remoteFunctionNames; template static QWebGLFunctionCall *createEventImpl(bool wait) { auto context = QOpenGLContext::currentContext(); Q_ASSERT(context); const auto handle = static_cast(context->handle()); auto integrationPrivate = QWebGLIntegrationPrivate::instance(); const auto clientData = integrationPrivate->findClientData(handle->currentSurface()); if (!clientData || !clientData->socket || clientData->socket->state() != QAbstractSocket::ConnectedState) return nullptr; return new QWebGLFunctionCall(Function->localName, handle->currentSurface(), wait); } static void postEventImpl(QWebGLFunctionCall *event) { if (event->isBlocking()) QWebGLContextPrivate::waitingIds.insert(event->id()); QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event); } template static int createEventAndPostImpl(bool wait, Ts&&... arguments) { auto event = createEventImpl(wait); auto id = -1; if (event) { id = event->id(); addHelper(event, arguments...); postEventImpl(event); } return id; } template static int createEventAndPostImpl(bool wait) { auto event = createEventImpl(wait); auto id = -1; if (event) { id = event->id(); postEventImpl(event); } return id; } template inline int postEventImpl(bool wait, Ts&&... arguments) { return createEventAndPostImpl(wait, arguments...); } template inline int postEvent(bool wait) { return createEventAndPostImpl(wait); } template inline int postEvent(Ts&&... arguments) { return postEventImpl(false, arguments...); } template static ReturnType postEventAndQuery(ReturnType defaultValue, Ts&&... arguments) { auto id = postEventImpl(true, arguments...); return id != -1 ? queryValue(id, defaultValue) : defaultValue; } namespace QWebGL { #define EXPAND(x) x #define USE_(...) __VA_ARGS__ #define IGNORE_(...) // Retrieve the type // TYPEOF( (type) name ) -> TYPEOF_( SEPARATE (type) name ) -> FIRST_ARG( type, name ) -> type #define FIRST_ARG(ONE, ...) ONE #define SEPARATE(ITEM) ITEM, #define TYPEOF_(...) EXPAND(FIRST_ARG(__VA_ARGS__)) #define TYPEOF(ITEM) TYPEOF_(SEPARATE ITEM) // Strip off the type, leaving just the parameter name: #define STRIP(ITEM) IGNORE_ ITEM // PAIR( (type) name ) -> type name #define PAIR(ITEM) USE_ ITEM // Make a FOREACH macro #define FOR_1(EACH, ITEM) EACH(ITEM) #define FOR_2(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_1(EACH, __VA_ARGS__)) #define FOR_3(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_2(EACH, __VA_ARGS__)) #define FOR_4(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_3(EACH, __VA_ARGS__)) #define FOR_5(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_4(EACH, __VA_ARGS__)) #define FOR_6(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_5(EACH, __VA_ARGS__)) #define FOR_7(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_6(EACH, __VA_ARGS__)) #define FOR_8(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_7(EACH, __VA_ARGS__)) #define FOR_9(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_8(EACH, __VA_ARGS__)) #define FOR_10(EACH, ITEM, ...) EACH(ITEM), EXPAND(FOR_9(EACH, __VA_ARGS__)) //... repeat as needed #define SELECT_BY_COUNT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME #define FOR_EACH(action, ...) \ EXPAND(SELECT_BY_COUNT(__VA_ARGS__, FOR_10, FOR_9, FOR_8, FOR_7, \ FOR_6, FOR_5, FOR_4, FOR_3, FOR_2, FOR_1)(action, __VA_ARGS__)) #define QWEBGL_FUNCTION_PARAMETER(ITEM) \ GLFunction::Parameter { \ QStringLiteral(QT_STRINGIFY(STRIP(ITEM))), \ QStringLiteral(QT_STRINGIFY(TYPEOF(ITEM))), \ ParameterTypeTraits::typeId(), \ ParameterTypeTraits::isArray() \ } #if defined(Q_CC_MSVC) && defined(Q_OS_WIN32) && !defined(Q_OS_WIN64) # define WEBGL_APIENTRY __stdcall #else # define WEBGL_APIENTRY #endif #define QWEBGL_FUNCTION(REMOTE_NAME, RET_TYPE, LOCAL_NAME, ...) \ RET_TYPE WEBGL_APIENTRY LOCAL_NAME(FOR_EACH(TYPEOF, __VA_ARGS__));\ extern const GLFunction REMOTE_NAME { \ #REMOTE_NAME, \ #LOCAL_NAME, \ reinterpret_cast(LOCAL_NAME), \ GLFunction::ParameterList({FOR_EACH(QWEBGL_FUNCTION_PARAMETER, __VA_ARGS__)}) \ }; \ RET_TYPE WEBGL_APIENTRY LOCAL_NAME(FOR_EACH(PAIR, __VA_ARGS__)) #define QWEBGL_FUNCTION_NO_PARAMS(REMOTE_NAME, RET_TYPE, LOCAL_NAME) \ RET_TYPE WEBGL_APIENTRY LOCAL_NAME();\ extern const GLFunction REMOTE_NAME { \ #REMOTE_NAME, \ #LOCAL_NAME, \ (QFunctionPointer) LOCAL_NAME \ }; \ RET_TYPE WEBGL_APIENTRY LOCAL_NAME() #define QWEBGL_FUNCTION_POSTEVENT(REMOTE_NAME, LOCAL_NAME, ...) \ QWEBGL_FUNCTION(REMOTE_NAME, void, LOCAL_NAME, __VA_ARGS__) { \ postEvent<&REMOTE_NAME>(FOR_EACH(STRIP, __VA_ARGS__)); \ } QWEBGL_FUNCTION(activeTexture, void, glActiveTexture, (GLenum) texture) { postEvent<&activeTexture>(texture); currentContextData()->activeTextureUnit = texture; } QWEBGL_FUNCTION_POSTEVENT(attachShader, glAttachShader, (GLuint) program, (GLuint) shader) QWEBGL_FUNCTION_POSTEVENT(bindAttribLocation, glBindAttribLocation, (GLuint) program, (GLuint) index, (const GLchar *) name) QWEBGL_FUNCTION(bindBuffer, void, glBindBuffer, (GLenum) target, (GLuint) buffer) { postEvent<&bindBuffer>(target, buffer); if (target == GL_ARRAY_BUFFER) currentContextData()->boundArrayBuffer = buffer; if (target == GL_ELEMENT_ARRAY_BUFFER) currentContextData()->boundElementArrayBuffer = buffer; } QWEBGL_FUNCTION(bindFramebuffer, void, glBindFramebuffer, (GLenum) target, (GLuint) framebuffer) { postEvent<&bindFramebuffer>(target, framebuffer); if (target == GL_FRAMEBUFFER) currentContextData()->boundDrawFramebuffer = framebuffer; } QWEBGL_FUNCTION_POSTEVENT(bindRenderbuffer, glBindRenderbuffer, (GLenum) target, (GLuint) renderbuffer) QWEBGL_FUNCTION(bindTexture, void, glBindTexture, (GLenum) target, (GLuint) texture) { postEvent<&bindTexture>(target, texture); if (target == GL_TEXTURE_2D) currentContextData()->boundTexture2D = texture; } QWEBGL_FUNCTION_POSTEVENT(blendColor, glBlendColor, (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha) QWEBGL_FUNCTION_POSTEVENT(blendEquation, glBlendEquation, (GLenum) mode) QWEBGL_FUNCTION_POSTEVENT(blendEquationSeparate, glBlendEquationSeparate, (GLenum) modeRGB, (GLenum) modeAlpha) QWEBGL_FUNCTION_POSTEVENT(blendFunc, glBlendFunc, (GLenum) sfactor, (GLenum) dfactor) QWEBGL_FUNCTION_POSTEVENT(blendFuncSeparate, glBlendFuncSeparate, (GLenum) sfactorRGB, (GLenum) dfactorRGB, (GLenum) sfactorAlpha, (GLenum) dfactorAlpha) QWEBGL_FUNCTION(bufferData, void, glBufferData, (GLenum) target, (GLsizeiptr) size, (const void *) data, (GLenum) usage) { postEvent<&bufferData>(target, usage, int(size), data ? QByteArray((const char *)data, size) : QByteArray()); } QWEBGL_FUNCTION(bufferSubData, void, glBufferSubData, (GLenum) target, (GLintptr) offset, (GLsizeiptr) size, (const void *) data) { postEvent<&bufferSubData>(target, int(offset), QByteArray((const char *)data, size)); } QWEBGL_FUNCTION(checkFramebufferStatus, GLenum, glCheckFramebufferStatus, (GLenum) target) { return postEventAndQuery<&checkFramebufferStatus>(0u, target); } QWEBGL_FUNCTION_POSTEVENT(clear, glClear, (GLbitfield) mask) QWEBGL_FUNCTION_POSTEVENT(clearColor, glClearColor, (GLfloat) red, (GLfloat) green, (GLfloat) blue, (GLfloat) alpha) QWEBGL_FUNCTION_POSTEVENT(clearDepthf, glClearDepthf, (GLfloat) d) QWEBGL_FUNCTION_POSTEVENT(clearStencil, glClearStencil, (GLint) s) QWEBGL_FUNCTION_POSTEVENT(colorMask, glColorMask, (GLboolean) red, (GLboolean) green, (GLboolean) blue, (GLboolean) alpha) QWEBGL_FUNCTION_POSTEVENT(compileShader, glCompileShader, (GLuint) shader) QWEBGL_FUNCTION(compressedTexImage2D, void, glCompressedTexImage2D, (GLenum) target, (GLint) level, (GLenum) internalformat, (GLsizei) width, (GLsizei) height, (GLint) border, (GLsizei) imageSize, (const void *) data) { postEvent<&compressedTexImage2D>(target, level, internalformat, width, height, border, imageSize, QByteArray(reinterpret_cast(data), imageSize)); } QWEBGL_FUNCTION(compressedTexSubImage2D, void, glCompressedTexSubImage2D, (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset, (GLsizei) width, (GLsizei) height, (GLenum) format, (GLsizei) imageSize, (const void *) data) { postEvent<&compressedTexSubImage2D>(target, level, xoffset, yoffset, width, height, format, imageSize, QByteArray(reinterpret_cast(data), imageSize)); } QWEBGL_FUNCTION_POSTEVENT(copyTexImage2D, glCopyTexImage2D, (GLenum) target, (GLint) level, (GLenum) internalformat, (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height, (GLint) border) QWEBGL_FUNCTION_POSTEVENT(copyTexSubImage2D, glCopyTexSubImage2D, (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset, (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height) QWEBGL_FUNCTION_NO_PARAMS(createProgram, GLuint, glCreateProgram) { return postEventAndQuery<&createProgram>(0u); } QWEBGL_FUNCTION(createShader, GLuint, glCreateShader, (GLenum) type) { return postEventAndQuery<&createShader>(0u, type); } QWEBGL_FUNCTION_POSTEVENT(cullFace, glCullFace, (GLenum) mode) QWEBGL_FUNCTION(deleteBuffers, void, glDeleteBuffers, (GLsizei) n, (const GLuint *) buffers) { postEvent<&deleteBuffers>(n, qMakePair(buffers, n)); for (int i = 0; i < n; ++i) { if (currentContextData()->boundArrayBuffer == buffers[i]) currentContextData()->boundArrayBuffer = 0; if (currentContextData()->boundElementArrayBuffer == buffers[i]) currentContextData()->boundElementArrayBuffer = 0; } } QWEBGL_FUNCTION(deleteFramebuffers, void, glDeleteFramebuffers, (GLsizei) n, (const GLuint *) framebuffers) { postEvent<&deleteFramebuffers>(qMakePair(framebuffers, n)); } QWEBGL_FUNCTION_POSTEVENT(deleteProgram, glDeleteProgram, (GLuint) program) QWEBGL_FUNCTION(deleteRenderbuffers, void, glDeleteRenderbuffers, (GLsizei) n, (const GLuint *) renderbuffers) { postEvent<&deleteRenderbuffers>(qMakePair(renderbuffers, n)); } QWEBGL_FUNCTION_POSTEVENT(deleteShader, glDeleteShader, (GLuint) shader) QWEBGL_FUNCTION(deleteTextures, void, glDeleteTextures, (GLsizei) n, (const GLuint *) textures) { postEvent<&deleteTextures>(qMakePair(textures, n)); } QWEBGL_FUNCTION_POSTEVENT(depthFunc, glDepthFunc, (GLenum) func) QWEBGL_FUNCTION_POSTEVENT(depthMask, glDepthMask, (GLboolean) flag) QWEBGL_FUNCTION_POSTEVENT(depthRangef, glDepthRangef, (GLfloat) n, (GLfloat) f) QWEBGL_FUNCTION_POSTEVENT(detachShader, glDetachShader, (GLuint) program, (GLuint) shader) QWEBGL_FUNCTION(disableVertexAttribArray, void, glDisableVertexAttribArray, (GLuint) index) { postEvent<&disableVertexAttribArray>(index); currentContextData()->vertexAttribPointers[index].enabled = false; } QWEBGL_FUNCTION(drawArrays, void, glDrawArrays, (GLenum) mode, (GLint) first, (GLsizei) count) { auto event = currentContext()->createEvent(QStringLiteral("glDrawArrays")); if (!event) return; event->addParameters(mode, first, count); // Some vertex attributes may be client-side, others may not. Therefore // client-side ones need to transfer the data starting from the base // pointer, not just from 'first'. setVertexAttribs(event, first + count); QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event); } QWEBGL_FUNCTION(drawElements, void, glDrawElements, (GLenum) mode, (GLsizei) count, (GLenum) type, (const void *) indices) { auto event = currentContext()->createEvent(QStringLiteral("glDrawElements")); if (!event) return; event->addParameters(mode, count, type); setVertexAttribs(event, count); ContextData *d = currentContextData(); if (d->boundElementArrayBuffer == 0) { event->addParameters(0, QByteArray(reinterpret_cast(indices), count * elementSize(type))); } else { event->addParameters(1, uint(quintptr(indices))); } QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event); } QWEBGL_FUNCTION(enableVertexAttribArray, void, glEnableVertexAttribArray, (GLuint) index) { postEvent<&enableVertexAttribArray>(index); currentContextData()->vertexAttribPointers[index].enabled = true; } QWEBGL_FUNCTION_NO_PARAMS(finish, void, glFinish) { postEvent<&finish>(); } QWEBGL_FUNCTION_NO_PARAMS(flush, void, glFlush) { postEvent<&flush>(); } QWEBGL_FUNCTION_POSTEVENT(framebufferRenderbuffer, glFramebufferRenderbuffer, (GLenum) target, (GLenum) attachment, (GLenum) renderbuffertarget, (GLuint) renderbuffer) QWEBGL_FUNCTION_POSTEVENT(framebufferTexture2D, glFramebufferTexture2D, (GLenum) target, (GLenum) attachment, (GLenum) textarget, (GLuint) texture, (GLint) level) QWEBGL_FUNCTION_POSTEVENT(frontFace, glFrontFace, (GLenum) mode) QWEBGL_FUNCTION(genBuffers, void, glGenBuffers, (GLsizei) n, (GLuint *) buffers) { const auto values = postEventAndQuery<&genBuffers>(QVariantList(), n); if (values.size() != n) qCWarning(lc, "Failed to create buffers"); for (int i = 0; i < qMin(n, values.size()); ++i) buffers[i] = values.at(i).toUInt(); } QWEBGL_FUNCTION(genFramebuffers, void, glGenFramebuffers, (GLsizei) n, (GLuint *) framebuffers) { const auto values = postEventAndQuery<&genFramebuffers>(QVariantList(), n); if (values.size() != n) qCWarning(lc, "Failed to create framebuffers"); for (int i = 0; i < qMin(n, values.size()); ++i) framebuffers[i] = values.at(i).toUInt(); } QWEBGL_FUNCTION(genRenderbuffers, void, glGenRenderbuffers, (GLsizei) n, (GLuint *) renderbuffers) { const auto values = postEventAndQuery<&genRenderbuffers>(QVariantList(), n); if (values.size() != n) qCWarning(lc, "Failed to create render buffers"); for (int i = 0; i < qMin(n, values.size()); ++i) renderbuffers[i] = values.at(i).toUInt(); } QWEBGL_FUNCTION(genTextures, void, glGenTextures, (GLsizei) n, (GLuint *) textures) { const auto values = postEventAndQuery<&genTextures>(QVariantList(), n); if (values.size() != n) qCWarning(lc, "Failed to create textures"); for (int i = 0; i < qMin(n, values.size()); ++i) textures[i] = values.at(i).toUInt(); } QWEBGL_FUNCTION_POSTEVENT(generateMipmap, glGenerateMipmap, (GLenum) target) QWEBGL_FUNCTION(getActiveAttrib, void, glGetActiveAttrib, (GLuint) program, (GLuint) index, (GLsizei) bufSize, (GLsizei *) length, (GLint *) size, (GLenum *) type, (GLchar *) name) { const auto values = postEventAndQuery<&getActiveAttrib>(QVariantMap(), program, index, bufSize); if (values.isEmpty()) return; const int rtype = values["rtype"].toInt(); const int rsize = values["rsize"].toInt(); const QByteArray rname = values["rname"].toByteArray(); if (type) *type = rtype; if (size) *size = rsize; int len = qMax(0, qMin(bufSize - 1, rname.size())); if (length) *length = len; if (name) { memcpy(name, rname.constData(), len); name[len] = '\0'; } } QWEBGL_FUNCTION(getActiveUniform, void, glGetActiveUniform, (GLuint) program, (GLuint) index, (GLsizei) bufSize, (GLsizei *) length, (GLint *) size, (GLenum *) type, (GLchar *) name) { const auto values = postEventAndQuery<&getActiveUniform>(QVariantMap(), program, index, bufSize); if (values.isEmpty()) return; const int rtype = values["rtype"].toInt(); const int rsize = values["rsize"].toInt(); const QByteArray rname = values["rname"].toByteArray(); if (type) *type = rtype; if (size) *size = rsize; int len = qMax(0, qMin(bufSize - 1, rname.size())); if (length) *length = len; if (name) { memcpy(name, rname.constData(), len); name[len] = '\0'; } } QWEBGL_FUNCTION(getAttachedShaders, void, glGetAttachedShaders, (GLuint) program, (GLsizei) maxCount, (GLsizei *) count, (GLuint *) shaders) { const auto values = postEventAndQuery<&getAttachedShaders>(QVariantList(), program, maxCount); *count = values.size(); for (int i = 0; i < values.size(); ++i) shaders[i] = values.at(i).toUInt(); } QWEBGL_FUNCTION(getAttribLocation, GLint, glGetAttribLocation, (GLuint) program, (const GLchar *) name) { return postEventAndQuery<&getAttribLocation>(-1, program, name); } QWEBGL_FUNCTION(getString, const GLubyte *, glGetString, (GLenum) name) { static QByteArrayList strings; const auto it = currentContextData()->cachedParameters.find(name); if (it != currentContextData()->cachedParameters.end()) { auto &stringCache = currentContextData()->stringCache; Q_ASSERT(it->type() == QVariant::String); const auto string = it->toString().toLatin1(); { auto it = stringCache.find(string), end = stringCache.end(); if (it == end) it = stringCache.insert(string); return reinterpret_cast(it->constData()); } } const auto value = postEventAndQuery<&getString>(QByteArray(), name); strings.append(value); return reinterpret_cast(strings.last().constData()); } QWEBGL_FUNCTION(getIntegerv, void, glGetIntegerv, (GLenum) pname, (GLint *) data) { if (pname == GL_MAX_TEXTURE_SIZE) { static bool ok; static auto value = qgetenv("QT_WEBGL_MAX_TEXTURE_SIZE").toUInt(&ok); if (ok) { *data = value; return; } } const auto it = currentContextData()->cachedParameters.find(pname); if (it != currentContextData()->cachedParameters.end()) { QList values; switch (it->type()) { case QVariant::Map: values = it->toMap().values(); break; case QVariant::List: values = it->toList(); break; default: values = QVariantList{ *it }; } for (const auto &integer : qAsConst(values)) { bool ok; *data = integer.toInt(&ok); if (!ok) qCWarning(lc, "Failed to cast value"); ++data; } return; } switch (pname) { case GL_CURRENT_PROGRAM: *data = currentContextData()->currentProgram; return; case GL_FRAMEBUFFER_BINDING: *data = currentContextData()->boundDrawFramebuffer; return; case GL_ARRAY_BUFFER_BINDING: *data = currentContextData()->boundArrayBuffer; return; case GL_ELEMENT_ARRAY_BUFFER_BINDING: *data = currentContextData()->boundElementArrayBuffer; return; case GL_ACTIVE_TEXTURE: *data = currentContextData()->activeTextureUnit; return; case GL_TEXTURE_BINDING_2D: *data = currentContextData()->boundTexture2D; return; default: *data = postEventAndQuery<&getIntegerv>(0, pname); } } QWEBGL_FUNCTION(getBooleanv, void, glGetBooleanv, (GLenum) pname, (GLboolean *) data) { const auto it = currentContextData()->cachedParameters.find(pname); if (it != currentContextData()->cachedParameters.end()) { Q_ASSERT(it->type() == QVariant::Bool); *data = it->toBool(); return; } *data = postEventAndQuery<&getBooleanv>(GL_FALSE, pname); } QWEBGL_FUNCTION(enable, void, glEnable, (GLenum) cap) { if (!postEvent<&enable>(cap)) return; auto it = currentContextData()->cachedParameters.find(cap); if (it != currentContextData()->cachedParameters.end()) { Q_ASSERT(it->type() == QVariant::Bool); it->setValue(true); } } QWEBGL_FUNCTION(disable, void, glDisable, (GLenum) cap) { if (!postEvent<&disable>(cap)) return; auto it = currentContextData()->cachedParameters.find(cap); if (it != currentContextData()->cachedParameters.end()) { Q_ASSERT(it->type() == QVariant::Bool); it->setValue(false); } } QWEBGL_FUNCTION(getBufferParameteriv, void, glGetBufferParameteriv, (GLenum) target, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getBufferParameteriv>(0, target, pname); } QWEBGL_FUNCTION_NO_PARAMS(getError, GLenum, glGetError) { return postEventAndQuery<&getError>(GL_NO_ERROR); } QWEBGL_FUNCTION(getParameter, void, glGetFloatv, (GLenum) pname, (GLfloat*) data) { *data = postEventAndQuery<&getParameter>(0.0, pname); } QWEBGL_FUNCTION(getFramebufferAttachmentParameteriv, void, glGetFramebufferAttachmentParameteriv, (GLenum) target, (GLenum) attachment, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getFramebufferAttachmentParameteriv>(0, target, attachment, pname); } QWEBGL_FUNCTION(getProgramInfoLog, void, glGetProgramInfoLog, (GLuint) program, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) infoLog) { auto value = postEventAndQuery<&getProgramInfoLog>(QString(), program); *length = value.length(); if (bufSize >= value.length()) std::memcpy(infoLog, value.constData(), value.length()); } QWEBGL_FUNCTION(getProgramiv, void, glGetProgramiv, (GLuint) program, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getProgramiv>(0, program, pname); } QWEBGL_FUNCTION(getRenderbufferParameteriv, void, glGetRenderbufferParameteriv, (GLenum) target, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getRenderbufferParameteriv>(0, target, pname); } QWEBGL_FUNCTION(getShaderInfoLog, void, glGetShaderInfoLog, (GLuint) shader, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) infoLog) { const auto value = postEventAndQuery<&getShaderInfoLog>(QString(), shader); *length = value.length(); if (bufSize >= value.length()) std::memcpy(infoLog, value.constData(), value.length()); } QWEBGL_FUNCTION(getShaderPrecisionFormat, void, glGetShaderPrecisionFormat, (GLenum) shadertype, (GLenum) precisiontype, (GLint *) range, (GLint *) precision) { const auto value = postEventAndQuery<&getShaderPrecisionFormat>(QVariantMap(), shadertype, precisiontype); bool ok; range[0] = value[QStringLiteral("rangeMin")].toInt(&ok); if (!ok) qCCritical(lc, "Invalid rangeMin value"); range[1] = value[QStringLiteral("rangeMax")].toInt(&ok); if (!ok) qCCritical(lc, "Invalid rangeMax value"); *precision = value[QStringLiteral("precision")].toInt(&ok); if (!ok) qCCritical(lc, "Invalid precision value"); } QWEBGL_FUNCTION(getShaderSource, void, glGetShaderSource, (GLuint) shader, (GLsizei) bufSize, (GLsizei *) length, (GLchar *) source) { const auto value = postEventAndQuery<&getShaderSource>(QString(), shader); *length = value.length(); if (bufSize >= value.length()) std::memcpy(source, value.constData(), value.length()); } QWEBGL_FUNCTION(getShaderiv, void, glGetShaderiv, (GLuint) shader, (GLenum) pname, (GLint *) params) { if (pname == GL_INFO_LOG_LENGTH) { GLsizei bufSize = 0; glGetShaderInfoLog(shader, bufSize, &bufSize, nullptr); *params = bufSize; return; } if (pname == GL_SHADER_SOURCE_LENGTH) { GLsizei bufSize = 0; glGetShaderSource(shader, bufSize, &bufSize, nullptr); *params = bufSize; return; } *params = postEventAndQuery<&getShaderiv>(0, shader, pname); } QWEBGL_FUNCTION(getTexParameterfv, void, glGetTexParameterfv, (GLenum) target, (GLenum) pname, (GLfloat *) params) { *params = postEventAndQuery<&getTexParameterfv>(0.f, target, pname); } QWEBGL_FUNCTION(getTexParameteriv, void, glGetTexParameteriv, (GLenum) target, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getTexParameteriv>(0, target, pname); } QWEBGL_FUNCTION(getUniformLocation, GLint, glGetUniformLocation, (GLuint) program, (const GLchar *) name) { return postEventAndQuery<&getUniformLocation>(-1, program, name); } QWEBGL_FUNCTION(getUniformfv, void, glGetUniformfv, (GLuint) program, (GLint) location, (GLfloat *) params) { *params = postEventAndQuery<&getUniformfv>(0.f, program, location); } QWEBGL_FUNCTION(getUniformiv, void, glGetUniformiv, (GLuint) program, (GLint) location, (GLint *) params) { *params = postEventAndQuery<&getUniformiv>(0, program, location); } QWEBGL_FUNCTION(getVertexAttribPointerv, void, glGetVertexAttribPointerv, (GLuint) index, (GLenum) pname, (void **) pointer) { Q_UNUSED(index); Q_UNUSED(pname); Q_UNUSED(pointer); qFatal("glGetVertexAttribPointerv not supported"); return; } QWEBGL_FUNCTION(getVertexAttribfv, void, glGetVertexAttribfv, (GLuint) index, (GLenum) pname, (GLfloat *) params) { *params = postEventAndQuery<&getVertexAttribfv>(0.f, index, pname); } QWEBGL_FUNCTION(getVertexAttribiv, void, glGetVertexAttribiv, (GLuint) index, (GLenum) pname, (GLint *) params) { *params = postEventAndQuery<&getVertexAttribiv>(0, index, pname); } QWEBGL_FUNCTION_POSTEVENT(hint, glHint, (GLenum) target, (GLenum) mode) QWEBGL_FUNCTION(isBuffer, GLboolean, glIsBuffer, (GLuint) buffer) { return postEventAndQuery<&isBuffer>(GL_FALSE, buffer); } QWEBGL_FUNCTION(isEnabled, GLboolean, glIsEnabled, (GLenum) cap) { return postEventAndQuery<&isEnabled>(GL_FALSE, cap); } QWEBGL_FUNCTION(isFramebuffer, GLboolean, glIsFramebuffer, (GLuint) framebuffer) { return postEventAndQuery<&isFramebuffer>(GL_FALSE, framebuffer); } QWEBGL_FUNCTION(isProgram, GLboolean, glIsProgram, (GLuint) program) { return postEventAndQuery<&isProgram>(GL_FALSE, program); } QWEBGL_FUNCTION(isRenderbuffer, GLboolean, glIsRenderbuffer, (GLuint) renderbuffer) { return postEventAndQuery<&isRenderbuffer>(GL_FALSE, renderbuffer); } QWEBGL_FUNCTION(isShader, GLboolean, glIsShader, (GLuint) shader) { return postEventAndQuery<&isShader>(GL_FALSE, shader); } QWEBGL_FUNCTION(isTexture, GLboolean, glIsTexture, (GLuint) texture) { return postEventAndQuery<&isTexture>(GL_FALSE, texture); } QWEBGL_FUNCTION_POSTEVENT(lineWidth, glLineWidth, (GLfloat) width) QWEBGL_FUNCTION_POSTEVENT(linkProgram, glLinkProgram, (GLuint) program) QWEBGL_FUNCTION(pixelStorei, void, glPixelStorei, (GLenum) pname, (GLint) param) { postEvent<&pixelStorei>(pname, param); switch (pname) { case GL_UNPACK_ALIGNMENT: currentContextData()->unpackAlignment = param; break; } } QWEBGL_FUNCTION_POSTEVENT(polygonOffset, glPolygonOffset, (GLfloat) factor, (GLfloat) units) QWEBGL_FUNCTION(readPixels, void, glReadPixels, (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height, (GLenum) format, (GLenum) type, (void *) pixels) { const auto value = postEventAndQuery<&readPixels>(QByteArray(), x, y, width, height, format, type); if (!value.isEmpty()) std::memcpy(pixels, value.constData(), value.size()); } QWEBGL_FUNCTION_NO_PARAMS(releaseShaderCompiler, void, glReleaseShaderCompiler) { postEvent<&releaseShaderCompiler>(); } QWEBGL_FUNCTION_POSTEVENT(renderbufferStorage, glRenderbufferStorage, (GLenum) target, (GLenum) internalformat, (GLsizei) width, (GLsizei) height) QWEBGL_FUNCTION_POSTEVENT(sampleCoverage, glSampleCoverage, (GLfloat) value, (GLboolean) invert) QWEBGL_FUNCTION_POSTEVENT(scissor, glScissor, (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height) QWEBGL_FUNCTION(shaderBinary, void, glShaderBinary, (GLsizei), (const GLuint *), (GLenum), (const void *), (GLsizei)) { qFatal("WebGL does not allow precompiled shaders"); } QWEBGL_FUNCTION(shaderSource, void, glShaderSource, (GLuint) shader, (GLsizei) count, (const GLchar *const *) string, (const GLint *) length) { QString fullString; std::function concat; if (length) concat = [&](int i) { fullString.append(QString::fromLatin1(string[i], length[i])); }; else concat = [&](int i) { fullString.append(QString::fromLatin1(string[i])); }; for (int i = 0; i < count; ++i) concat(i); postEvent<&shaderSource>(shader, fullString); } QWEBGL_FUNCTION_POSTEVENT(stencilFunc, glStencilFunc, (GLenum) func, (GLint) ref, (GLuint) mask) QWEBGL_FUNCTION_POSTEVENT(stencilFuncSeparate, glStencilFuncSeparate, (GLenum) face, (GLenum) func, (GLint) ref, (GLuint) mask) QWEBGL_FUNCTION_POSTEVENT(stencilMask, glStencilMask, (GLuint) mask) QWEBGL_FUNCTION_POSTEVENT(stencilMaskSeparate, glStencilMaskSeparate, (GLenum) face, (GLuint) mask) QWEBGL_FUNCTION_POSTEVENT(stencilOp, glStencilOp, (GLenum) fail, (GLenum) zfail, (GLenum) zpass) QWEBGL_FUNCTION_POSTEVENT(stencilOpSeparate, glStencilOpSeparate, (GLenum) face, (GLenum) sfail, (GLenum) dpfail, (GLenum) dppass) QWEBGL_FUNCTION(texImage2D, void, glTexImage2D, (GLenum) target, (GLint) level, (GLint) internalformat, (GLsizei) width, (GLsizei) height, (GLint) border, (GLenum) format, (GLenum) type, (const void *) pixels) { const auto data = reinterpret_cast(pixels); const auto dataSize = imageSize(width, height, format, type, currentContextData()->pixelStorage); const bool isNull = data == nullptr || [](const char *pointer, int size) { const char *const end = pointer + size; const unsigned int zero = 0u; const char *const late = end + 1 - sizeof(zero); while (pointer < late) { // we have at least sizeof(zero) more bytes to check: if (*reinterpret_cast(pointer) != zero) return false; pointer += sizeof(zero); } return pointer >= end || std::memcmp(pointer, &zero, end - pointer) == 0; }(data, dataSize); postEvent<&texImage2D>(target, level, internalformat, width, height, border, format, type, isNull ? nullptr : QByteArray(data, dataSize)); } QWEBGL_FUNCTION_POSTEVENT(texParameterf, glTexParameterf, (GLenum) target, (GLenum) pname, (GLfloat) param) QWEBGL_FUNCTION(texParameterfv, void, glTexParameterfv, (GLenum), (GLenum), (const GLfloat *)) { qFatal("glTexParameterfv not implemented"); } QWEBGL_FUNCTION_POSTEVENT(texParameteri, glTexParameteri, (GLenum) target, (GLenum) pname, (GLint) param) QWEBGL_FUNCTION(texParameteriv, void, glTexParameteriv, (GLenum), (GLenum), (const GLint *)) { qFatal("glTexParameteriv not implemented"); } QWEBGL_FUNCTION(texSubImage2D, void, glTexSubImage2D, (GLenum) target, (GLint) level, (GLint) xoffset, (GLint) yoffset, (GLsizei) width, (GLsizei) height, (GLenum) format, (GLenum) type, (const void *) pixels) { postEvent<&texSubImage2D>(target, level, xoffset, yoffset, width, height, format, type, pixels ? QByteArray(reinterpret_cast(pixels), imageSize(width, height, format, type, currentContextData()->pixelStorage)) : nullptr); } QWEBGL_FUNCTION_POSTEVENT(uniform1f, glUniform1f, (GLint) location, (GLfloat) v0) QWEBGL_FUNCTION(uniform1fv, void, glUniform1fv, (GLint) location, (GLsizei) count, (const GLfloat *) value) { postEvent<&uniform1fv>(location, qMakePair(value, count)); } QWEBGL_FUNCTION_POSTEVENT(uniform1i, glUniform1i, (GLint) location, (GLint) v0) QWEBGL_FUNCTION(uniform1iv, void, glUniform1iv, (GLint) location, (GLsizei) count, (const GLint *) value) { postEvent<&uniform1iv>(location, qMakePair(value, count)); } QWEBGL_FUNCTION_POSTEVENT(uniform2f, glUniform2f, (GLint) location, (GLfloat) v0, (GLfloat) v1) QWEBGL_FUNCTION(uniform2fv, void, glUniform2fv, (GLint) location, (GLsizei) count, (const GLfloat *) value) { postEvent<&uniform2fv>(location, qMakePair(value, count * 2)); } QWEBGL_FUNCTION_POSTEVENT(uniform2i, glUniform2i, (GLint) location, (GLint) v0, (GLint) v1) QWEBGL_FUNCTION(uniform2iv, void, glUniform2iv, (GLint) location, (GLsizei) count, (const GLint *) value) { postEvent<&uniform2iv>(location, qMakePair(value, count * 2)); } QWEBGL_FUNCTION_POSTEVENT(uniform3f, glUniform3f, (GLint) location, (GLfloat) v0, (GLfloat) v1, (GLfloat) v2) QWEBGL_FUNCTION(uniform3fv, void, glUniform3fv, (GLint) location, (GLsizei) count, (const GLfloat *) value) { postEvent<&uniform3fv>(location, qMakePair(value, count * 3)); } QWEBGL_FUNCTION_POSTEVENT(uniform3i, glUniform3i, (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2) QWEBGL_FUNCTION(uniform3iv, void, glUniform3iv, (GLint) location, (GLsizei) count, (const GLint *) value) { postEvent<&uniform3iv>(location, qMakePair(value, count * 3)); } QWEBGL_FUNCTION_POSTEVENT(uniform4f, glUniform4f, (GLint) location, (GLfloat) v0, (GLfloat) v1, (GLfloat) v2, (GLfloat) v3) QWEBGL_FUNCTION(uniform4fv, void, glUniform4fv, (GLint) location, (GLsizei) count, (const GLfloat *) value) { postEvent<&uniform4fv>(location, qMakePair(value, count * 4)); } QWEBGL_FUNCTION_POSTEVENT(uniform4i, glUniform4i, (GLint) location, (GLint) v0, (GLint) v1, (GLint) v2, (GLint) v3) QWEBGL_FUNCTION(uniform4iv, void, glUniform4iv, (GLint) location, (GLsizei) count, (const GLint *) value) { postEvent<&uniform4iv>(location, qMakePair(value, count * 4)); } QWEBGL_FUNCTION(uniformMatrix2fv, void, glUniformMatrix2fv, (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value) { postEvent<&uniformMatrix2fv>(location, transpose, qMakePair(value, count * 4)); } QWEBGL_FUNCTION(uniformMatrix3fv, void, glUniformMatrix3fv, (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value) { postEvent<&uniformMatrix3fv>(location, transpose, qMakePair(value, count * 9)); } QWEBGL_FUNCTION(uniformMatrix4fv, void, glUniformMatrix4fv, (GLint) location, (GLsizei) count, (GLboolean) transpose, (const GLfloat *) value) { postEvent<&uniformMatrix4fv>(location, transpose, qMakePair(value, count * 16)); } QWEBGL_FUNCTION_POSTEVENT(useProgram, glUseProgram, (GLuint) program) QWEBGL_FUNCTION_POSTEVENT(validateProgram, glValidateProgram, (GLuint) program) QWEBGL_FUNCTION_POSTEVENT(vertexAttrib1f, glVertexAttrib1f, (GLuint) index, (GLfloat) x) QWEBGL_FUNCTION(vertexAttrib1fv, void, glVertexAttrib1fv, (GLuint) index, (const GLfloat *) v) { postEvent<&vertexAttrib1fv>(index, v[0]); } QWEBGL_FUNCTION_POSTEVENT(vertexAttrib2f, glVertexAttrib2f, (GLuint) index, (GLfloat) x, (GLfloat) y) QWEBGL_FUNCTION(vertexAttrib2fv, void, glVertexAttrib2fv, (GLuint) index, (const GLfloat *) v) { postEvent<&vertexAttrib2fv>(index, v[0], v[1]); } QWEBGL_FUNCTION_POSTEVENT(vertexAttrib3f, glVertexAttrib3f, (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z) QWEBGL_FUNCTION(vertexAttrib3fv, void, glVertexAttrib3fv, (GLuint) index, (const GLfloat *) v) { postEvent<&vertexAttrib3fv>(index, v[0], v[1], v[2]); } QWEBGL_FUNCTION_POSTEVENT(vertexAttrib4f, glVertexAttrib4f, (GLuint) index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w) QWEBGL_FUNCTION(vertexAttrib4fv, void, glVertexAttrib4fv, (GLuint) index, (const GLfloat *) v) { postEvent<&vertexAttrib4fv>(index, v[0], v[1], v[2], v[3]); } QWEBGL_FUNCTION(vertexAttribPointer, void, glVertexAttribPointer, (GLuint) index, (GLint) size, (GLenum) type, (GLboolean) normalized, (GLsizei) stride, (const void *) pointer) { ContextData *d = currentContextData(); ContextData::VertexAttrib &va(d->vertexAttribPointers[index]); va.arrayBufferBinding = d->boundArrayBuffer; va.size = size; va.type = type; va.normalized = normalized; va.stride = stride; va.pointer = pointer; if (d->boundArrayBuffer) postEvent<&vertexAttribPointer>(index, size, type, normalized, stride, uint(quintptr(pointer))); } QWEBGL_FUNCTION(viewport, void, glViewport, (GLint) x, (GLint) y, (GLsizei) width, (GLsizei) height) { postEvent<&viewport>(x, y, width, height); auto it = currentContextData()->cachedParameters.find(GL_VIEWPORT); if (it != currentContextData()->cachedParameters.end()) it->setValue(QVariantList{ x, y, width, height }); } QWEBGL_FUNCTION_POSTEVENT(blitFramebufferEXT, glBlitFramebufferEXT, (GLint) srcX0, (GLint) srcY0, (GLint) srcX1, (GLint) srcY1, (GLint) dstX0, (GLint) dstY0, (GLint) dstX1, (GLint) dstY1, (GLbitfield) mask, (GLenum) filter) QWEBGL_FUNCTION_POSTEVENT(renderbufferStorageMultisampleEXT, glRenderbufferStorageMultisampleEXT, (GLenum) target, (GLsizei) samples, (GLenum) internalformat, (GLsizei) width, (GLsizei) height) QWEBGL_FUNCTION(getTexLevelParameteriv, void, glGetTexLevelParameteriv, (GLenum), (GLint), (GLenum), (GLint *)) { qFatal("glGetTexLevelParameteriv not supported"); } #undef QWEBGL_FUNCTION extern const GLFunction makeCurrent("makeCurrent"); extern const GLFunction swapBuffers("swapBuffers"); } QWebGLContext::QWebGLContext(const QSurfaceFormat &format) : d_ptr(new QWebGLContextPrivate) { Q_D(QWebGLContext); d->id = d->nextId.fetchAndAddOrdered(1); qCDebug(lc, "Creating context %d", d->id); d->surfaceFormat = format; d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); } QWebGLContext::~QWebGLContext() {} QSurfaceFormat QWebGLContext::format() const { Q_D(const QWebGLContext); return d->surfaceFormat; } void QWebGLContext::swapBuffers(QPlatformSurface *surface) { Q_UNUSED(surface); auto event = createEvent(QStringLiteral("swapBuffers"), true); if (!event) return; lockMutex(); QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event); waitCondition(1000); unlockMutex(); } bool QWebGLContext::makeCurrent(QPlatformSurface *surface) { Q_D(QWebGLContext); qCDebug(lc, "%p", surface); if (surface->surface()->surfaceClass() == QSurface::Window) { const auto window = static_cast(surface); if (window->winId() == WId(-1)) return false; } QOpenGLContextPrivate::setCurrentContext(context()); d->currentSurface = surface; auto event = createEvent(QStringLiteral("makeCurrent")); if (!event) return false; event->addInt(d->id); if (surface->surface()->surfaceClass() == QSurface::Window) { auto window = static_cast(surface); if (s_contextData[id()].cachedParameters.isEmpty()) { auto future = window->d_func()->defaults.get_future(); std::future_status status = std::future_status::timeout; while (status == std::future_status::timeout) { if (!QWebGLIntegrationPrivate::instance()->findClientData(surface)) return false; status = future.wait_for(std::chrono::milliseconds(100)); } s_contextData[id()].cachedParameters = future.get(); } event->addInt(window->window()->width()); event->addInt(window->window()->height()); event->addInt(window->winId()); } else if (surface->surface()->surfaceClass() == QSurface::Offscreen) { qCDebug(lc, "QWebGLContext::makeCurrent: QSurface::Offscreen not implemented"); } QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event); return true; } void QWebGLContext::doneCurrent() { postEvent<&QWebGL::makeCurrent>(0, 0, 0, 0); } bool QWebGLContext::isValid() const { Q_D(const QWebGLContext); return d->id != -1; } QFunctionPointer QWebGLContext::getProcAddress(const char *procName) { const auto it = GLFunction::byName.find(procName); return it != GLFunction::byName.end() ? (*it)->functionPointer : nullptr; } int QWebGLContext::id() const { Q_D(const QWebGLContext); return d->id; } QPlatformSurface *QWebGLContext::currentSurface() const { Q_D(const QWebGLContext); return d->currentSurface; } QWebGLFunctionCall *QWebGLContext::createEvent(const QString &functionName, bool wait) { auto context = QOpenGLContext::currentContext(); Q_ASSERT(context); const auto handle = static_cast(context->handle()); if (!handle) return nullptr; auto integrationPrivate = QWebGLIntegrationPrivate::instance(); const auto clientData = integrationPrivate->findClientData(handle->currentSurface()); if (!clientData || !clientData->socket || clientData->socket->state() != QAbstractSocket::ConnectedState) return nullptr; const auto pointer = new QWebGLFunctionCall(functionName, handle->currentSurface(), wait); if (wait) QWebGLContextPrivate::waitingIds.insert(pointer->id()); return pointer; } QVariant QWebGLContext::queryValue(int id) { if (!QWebGLContextPrivate::waitingIds.contains(id)) { qCWarning(lc, "Unexpected id (%d)", id); return QVariant(); } static auto queryValue = [](int id) { lockMutex(); waitCondition(10); unlockMutex(); return QWebGLIntegrationPrivate::instance()->webSocketServer->queryValue(id); }; const auto handle = static_cast(currentContext()->context()->handle()); QVariant variant = queryValue(id); while (variant.isNull()) { auto integrationPrivate = QWebGLIntegrationPrivate::instance(); const auto clientData = integrationPrivate->findClientData(handle->currentSurface()); if (!clientData || !clientData->socket || clientData->socket->state() != QAbstractSocket::ConnectedState) return QVariant(); variant = queryValue(id); } QWebGLContextPrivate::waitingIds.remove(id); return variant; } QStringList QWebGLContext::supportedFunctions() { return GLFunction::remoteFunctionNames; } quint8 QWebGLContext::functionIndex(const QString &functionName) { const auto it = GLFunction::byName.find(functionName); Q_ASSERT(it != GLFunction::byName.end()); return (*it)->id; } QT_END_NAMESPACE