| /**************************************************************************** |
| ** |
| ** 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 <QtCore/qhash.h> |
| #include <QtCore/qpair.h> |
| #include <QtCore/qrect.h> |
| #include <QtCore/qset.h> |
| #include <QtGui/private/qguiapplication_p.h> |
| #include <QtGui/private/qopenglcontext_p.h> |
| #include <QtGui/qguiapplication.h> |
| #include <QtGui/qimage.h> |
| #include <QtGui/qopenglcontext.h> |
| #include <QtGui/qsurface.h> |
| #include <QtWebSockets/qwebsocket.h> |
| |
| #include <cstring> |
| #include <limits> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.context") |
| |
| class QWebGLContextPrivate |
| { |
| public: |
| static QAtomicInt nextId; |
| static QSet<int> waitingIds; |
| union { int id = -1; qintptr padded; }; |
| QPlatformSurface *currentSurface = nullptr; |
| QSurfaceFormat surfaceFormat; |
| }; |
| |
| QAtomicInt QWebGLContextPrivate::nextId(1); |
| QSet<int> 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<GLuint, VertexAttrib> vertexAttribPointers; |
| QHash<GLuint, QImage> images; |
| PixelStorageModes pixelStorage; |
| QMap<GLenum, QVariant> cachedParameters; |
| QSet<QByteArray> stringCache; |
| }; |
| |
| static QHash<int, ContextData> s_contextData; |
| |
| QWebGLContext *currentContext() |
| { |
| auto context = QOpenGLContext::currentContext(); |
| if (context) |
| return static_cast<QWebGLContext *>(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<const char *>(va.pointer), len)); |
| } |
| } |
| } |
| |
| template<class POINTER, class COUNT> |
| inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, |
| const QPair<POINTER, COUNT> &elements) |
| { |
| QVariantList list; |
| for (auto i = 0; i < elements.second; ++i) |
| list.append(QVariant::fromValue(elements.first[i])); |
| event->addList(list); |
| return event; |
| } |
| |
| template<class SIZE> |
| inline void addHelper(QWebGLFunctionCall *event, const QPair<const float *, SIZE> &elements) |
| { |
| QVariantList list; |
| for (auto i = 0; i < elements.second; ++i) |
| list.append(QVariant::fromValue<double>(elements.first[i])); |
| event->addList(list); |
| } |
| |
| template<class T> |
| inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value) |
| { |
| if (event) |
| event->add(value); |
| return event; |
| } |
| |
| template<class T, class... Ts> |
| inline QWebGLFunctionCall *addHelper(QWebGLFunctionCall *event, const T &value, const Ts&... rest) |
| { |
| if (event) { |
| event->add(value); |
| addHelper(event, rest...); |
| } |
| return event; |
| } |
| |
| template<class T> |
| static T queryValue(int id, const T &defaultValue = T()) |
| { |
| const auto variant = currentContext()->queryValue(id); |
| if (variant.isNull()) |
| return defaultValue; |
| if (!variant.canConvert<T>()) { |
| qCWarning(lc, "Cannot convert %s to " QT_STRINGIFY(T), variant.typeName()); |
| return defaultValue; |
| } |
| return variant.value<T>(); |
| } |
| |
| template<typename T> |
| struct ParameterTypeTraits { |
| static int typeId() { return qMetaTypeId<T>(); } |
| static bool isArray() { return std::is_pointer<T>(); } |
| }; |
| |
| template<typename T> |
| struct ParameterTypeTraits<T*> : ParameterTypeTraits<T> { |
| static int typeId() { return qMetaTypeId<T>(); } |
| }; |
| |
| template<typename T> |
| struct ParameterTypeTraits<const T*> : ParameterTypeTraits<T*> { |
| }; |
| |
| template<typename T> |
| struct ParameterTypeTraits<const T**> : ParameterTypeTraits<T*> { |
| }; |
| |
| 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<QString, const GLFunction *> byName; |
| static QStringList remoteFunctionNames; |
| using ParameterList = QVector<Parameter>; |
| |
| 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<quint8>::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<QString, const GLFunction *> GLFunction::byName; |
| QStringList GLFunction::remoteFunctionNames; |
| |
| template<const GLFunction *Function> |
| static QWebGLFunctionCall *createEventImpl(bool wait) |
| { |
| auto context = QOpenGLContext::currentContext(); |
| Q_ASSERT(context); |
| const auto handle = static_cast<QWebGLContext *>(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<const GLFunction *Function, class... Ts> |
| static int createEventAndPostImpl(bool wait, Ts&&... arguments) |
| { |
| auto event = createEventImpl<Function>(wait); |
| auto id = -1; |
| if (event) { |
| id = event->id(); |
| addHelper(event, arguments...); |
| postEventImpl(event); |
| } |
| return id; |
| } |
| |
| template<const GLFunction *Function> |
| static int createEventAndPostImpl(bool wait) |
| { |
| auto event = createEventImpl<Function>(wait); |
| auto id = -1; |
| if (event) { |
| id = event->id(); |
| postEventImpl(event); |
| } |
| return id; |
| } |
| |
| template<const GLFunction *Function, class... Ts> |
| inline int postEventImpl(bool wait, Ts&&... arguments) |
| { |
| return createEventAndPostImpl<Function>(wait, arguments...); |
| } |
| |
| template<const GLFunction *Function> |
| inline int postEvent(bool wait) |
| { |
| return createEventAndPostImpl<Function>(wait); |
| } |
| |
| template<const GLFunction *Function, class...Ts> |
| inline int postEvent(Ts&&... arguments) |
| { |
| return postEventImpl<Function>(false, arguments...); |
| } |
| |
| template<const GLFunction *Function, class ReturnType, class...Ts> |
| static ReturnType postEventAndQuery(ReturnType defaultValue, |
| Ts&&... arguments) |
| { |
| auto id = postEventImpl<Function>(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<TYPEOF(ITEM)>::typeId(), \ |
| ParameterTypeTraits<TYPEOF(ITEM)>::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<QFunctionPointer>(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<const char *>(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<const char *>(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<const char *>(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<const GLubyte *>(it->constData()); |
| } |
| } |
| const auto value = postEventAndQuery<&getString>(QByteArray(), name); |
| strings.append(value); |
| return reinterpret_cast<const GLubyte *>(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<QVariant> 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<void(int)> 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<const char *>(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<const unsigned int *>(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<const char *>(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<QWebGLWindow *>(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<QWebGLWindow *>(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<QWebGLContext *>(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<QWebGLContext *>(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 |