blob: 5857883ab0c01663932d43509be6b6df42a5192f [file] [log] [blame]
/****************************************************************************
**
** 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