Solved About QSGGeometryNode and WebAssembly in QT
-
I'm trying to deploy my project on the Web with WebAssembly and encountered some problems.
The project made a custom rendering with QSGRenderNode and ran on the web.
And I'm getting error "INVALID OPERATION insufficient buffer size".
Here's my code bellow.
openglerenderer.h
#ifndef OPENGLRENDERER_H #define OPENGLRENDERER_H #include <qsgrendernode.h> #include <QQuickItem> #include "webaccess/render/baserender.h" #if QT_CONFIG(opengl) class OpenGLRenderNode : public QSGRenderNode { public: ~OpenGLRenderNode(); void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; RenderingFlags flags() const override; QRectF rect() const override; void sync(QQuickItem *item); private: void init(); int m_width = 0; int m_height = 0; bool beInit = false; BaseRender* m_render = nullptr; }; #endif #endif // OPENGLRENDERER_H
openglerenderer.cpp
#include "openglrenderer.h" #include <QQuickItem> #if QT_CONFIG(opengl) #include <QOpenGLFunctions> #include <QOpenGLExtraFunctions> #include <QQuickWindow> #include "webaccess/render/tutorial/les1.h" #include "webaccess/render/tutorial/les2.h" #include "webaccess/render/tutorial/les3.h" #include "webaccess/render/tutorial/les4.h" OpenGLRenderNode::~OpenGLRenderNode() { releaseResources(); } void OpenGLRenderNode::releaseResources() { if (m_render) { m_render->releaseResources(); } } void OpenGLRenderNode::init() { if (m_render) { m_render->init(); beInit = true; } } void OpenGLRenderNode::render(const RenderState *state) { if (!beInit) init(); if (m_render) { m_render->render(state); } } QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const { return BlendState | ScissorState | StencilState; } QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const { return BoundedRectRendering | DepthAwareRendering; } QRectF OpenGLRenderNode::rect() const { return QRect(0, 0, m_width, m_height); } void OpenGLRenderNode::sync(QQuickItem *item) { m_width = static_cast<int>(item->width()); m_height = static_cast<int>(item->height()); if (!m_render) { m_render = static_cast<BaseRender*>(new les4{}); if (m_render) { QObject::connect(item->window(), &QQuickWindow::beforeRendering, m_render, [&]() { m_render->beforeRender(); }, Qt::DirectConnection); } } if (m_render) { m_render->sync(item); m_render->setViewportSize(item->size().toSize() * item->window()->devicePixelRatio()); m_render->setPosition(item->position().toPoint()); m_render->setWindow(item->window()); } } #endif
les4.h
#ifndef LES4_H #define LES4_H #include "../baserender.h" class QOpenGLTexture; class QOpenGLShaderProgram; class QOpenGLBuffer; class QOpenGLVertexArrayObject; class les4 : public BaseRender { public: les4(); ~les4() override; virtual void init() override; virtual void render(const QSGRenderNode::RenderState *state) override; virtual void sync(QQuickItem *item) override; virtual void releaseResources() override; virtual void setViewportSize(QSize size) override { m_viewportSize = size; } virtual void setPosition(QPoint point) override { m_position = point; } virtual void setWindow(QQuickWindow* window) override { m_window = window; } virtual void beforeRender() override; private: QSize m_viewportSize{}; QPoint m_position{}; QQuickWindow* m_window{}; QOpenGLShaderProgram *m_program = nullptr; QOpenGLVertexArrayObject *m_vao = nullptr; QOpenGLBuffer *m_vbo = nullptr; QOpenGLBuffer *m_ibo = nullptr; int m_width = 0; int m_height = 0; }; #endif // LES4_H
les4.cpp
#include "les4.h" #include <QQuickWindow> #include <QOpenGLContext> #include <QOpenGLTexture> #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions> #include <QGuiApplication> static const char *vertexShaderSource = "#version 100\n" "attribute vec3 aPos;\n" "attribute vec3 aColor;\n" "varying vec3 ourColor;\n" "void main() {\n" " gl_Position = vec4(aPos.xyz, 1.0);\n" " ourColor = aColor;\n" "}"; static const char *fragmentShaderSource = "#version 100\n" #if defined(Q_OS_HTML5) or defined(Q_OS_WASM) or defined(__EMSCRIPTEN__) "precision mediump float;\n" #endif "varying vec3 ourColor;\n" "void main() {\n" " gl_FragColor = vec4(ourColor, 1.0);\n" "}"; static float vertices[] { // 位置 // 顏色 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 頂部 }; static unsigned int indices[] { 0, 1, 2 }; les4::les4() { } les4::~les4() { } void les4::init() { QSurfaceFormat fmt; fmt.setVersion(2, 0); fmt.setRenderableType(QSurfaceFormat::OpenGLES); fmt.setMajorVersion(2); fmt.setMinorVersion(0); fmt.setRedBufferSize(5); fmt.setGreenBufferSize(6); fmt.setBlueBufferSize(5); fmt.setAlphaBufferSize(0); fmt.setDepthBufferSize(0); QSurfaceFormat::setDefaultFormat(fmt); QOpenGLContext *ctx = QOpenGLContext::currentContext(); ctx->setFormat(fmt); } void les4::releaseResources() { delete m_program; m_program = nullptr; delete m_vbo; m_vbo = nullptr; delete m_ibo; m_ibo = nullptr; delete m_vao; m_vao = nullptr; } void les4::beforeRender() { } void les4::render(const QSGRenderNode::RenderState*) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); #if defined(Q_OS_HTML5) or defined(Q_OS_WASM) or defined(__EMSCRIPTEN__) const bool isCoreProfile = false; #else const bool isCoreProfile = ctx->format().profile() == QSurfaceFormat::CoreProfile; #endif QOpenGLFunctions *f = ctx->functions(); int y = (m_window->size()* m_window->devicePixelRatio()).height() - m_viewportSize.height() - m_position.y(); f->glViewport(m_position.x(), y, m_viewportSize.width(), m_viewportSize.height()); f->glClearColor(0.2f, 0.3f, 0.3f, 1.0f); f->glClear(GL_COLOR_BUFFER_BIT); auto setupVertAttrs = [this, f] { m_vbo->bind(); f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)0); f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float))); f->glEnableVertexAttribArray(0); f->glEnableVertexAttribArray(1); }; if (!m_program) { m_program = new QOpenGLShaderProgram; m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); m_program->bindAttributeLocation("aPos", 0); m_program->bindAttributeLocation("aColor", 1); m_program->link(); m_vao = new QOpenGLVertexArrayObject; m_vao->create(); m_vbo = new QOpenGLBuffer; m_vbo->create(); m_ibo = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); m_ibo->create(); } // non-premultiplied alpha f->glEnable(GL_BLEND); f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // no backface culling f->glDisable(GL_CULL_FACE); // still need depth test to test against the items rendered in the opaque pass f->glEnable(GL_DEPTH_TEST); // but no need to write out anything to the depth buffer f->glDepthMask(GL_FALSE); // do not write out alpha f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // will always scissor f->glEnable(GL_SCISSOR_TEST); if (m_vao->isCreated()) m_vao->bind(); m_program->bind(); m_vbo->bind(); m_vbo->allocate(vertices, sizeof(vertices)); m_ibo->bind(); m_ibo->allocate(indices, sizeof(indices)); setupVertAttrs(); //scissor... //texture... f->glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } void les4::sync(QQuickItem *item) { // qDebug() << "w:" << item->width() << " h:" << item->height(); m_width = static_cast<int>(item->width()); m_height = static_cast<int>(item->height()); }
Please help me. I'm stuck here for a long time.
-
I figure out the solution.
First there is supposed to be three indices in my elements. The second argument of the function glDrawElements should be 3.
Second there's a limitation in webgl that the type of element supposed to be GL_UNSIGNED_SHORT.