I'm trying to use instanced rendering in Qt 5, and I'm having some trouble. I think I've traced my problems back to my use of VAOs. I'm an experienced enough programmer to know that these problems are almost always my fault, but I've run into the occasional Qt bug, so I thought I could use some help before filing a bug report.
I've put together a simple example, which doesn't use instancing (for some reason I can't request a recent version of OpenGL, even though I don't have this problem in the "real" software I'm developing).
At initialization time, I create a VAO, and two VBOs. One VBO is filled with some data, and remains bound; The other is allocated, but released (I never actually use it: I was planning to eventually write matrices to it for instancing, but didn't get that far).
I've added some preprocessor directives to simplify turning usage of the VAO or the second VBO on or off. If I use both the VAO and both VBOs, I get a segmentation fault. If I get rid of either the second VBO or the VAO, there's no seg fault and the drawing looks right (it should be a white triangle drawn behind a coloured triangle).
Here's the code:
glbuffertest.cpp:
######################################################################
# Automatically generated by qmake (3.0) Fri May 16 09:49:41 2014
######################################################################
TEMPLATE = app
TARGET = glbuffertest
INCLUDEPATH += .
CONFIG += qt debug
DEFINES += USE_VAO
DEFINES += USE_MATBO
# Input
SOURCES += glbuffertest.cpp glwindow.cpp
HEADERS += glwindow.h
glbuffertest.cpp:
#include <QGuiApplication>
#include <QSurfaceFormat>
#include "glwindow.h"
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
GLWindow window;
window.resize(400, 400);
window.show();
return app.exec();
}
glwindow.cpp:
#include "glwindow.h"
#include <QColor>
#include <QMatrix4x4>
#include <QVector>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>
GLWindow::GLWindow(QWindow *parent)
: QWindow(parent)
, _vbo(QOpenGLBuffer::VertexBuffer)
, _matbo(QOpenGLBuffer::VertexBuffer)
, _context(0)
{
setSurfaceType(QWindow::OpenGLSurface);
create();
}
GLWindow::~GLWindow()
{}
void GLWindow::initGL()
{
if (_context) // already initialized
return;
_context = new QOpenGLContext(this);
QSurfaceFormat format(requestedFormat());
format.setDepthBufferSize(24);
_context->setFormat(format);
_context->create();
_context->makeCurrent(this);
setupShaders();
_program->bind();
_positionAttr = _program->attributeLocation("position");
_colourAttr = _program->attributeLocation("colour");
_matrixUnif = _program->uniformLocation("matrix");
_program->release();
initializeOpenGLFunctions();
QVector<QVector3D> triangles;
triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
QVector<QVector3D> colours;
colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
#ifdef USE_VAO
_vao.create();
_vao.bind();
#endif
_vbo.create();
_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_vbo.bind();
size_t positionSize = triangles.size() * sizeof(QVector3D);
size_t colourSize = colours.size() * sizeof(QVector3D);
_vbo.allocate(positionSize + colourSize);
_vbo.write(0, triangles.constData(), positionSize);
_vbo.write(positionSize, colours.constData(), colourSize);
_colourOffset = positionSize;
#ifdef USE_MATBO
_matbo.create();
_matbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_matbo.bind();
_matbo.allocate(4 * sizeof(QVector4D));
qDebug() << "matrix buffer size:" << _matbo.size() << "requested:" << 4 * sizeof(QVector4D);
_matbo.release();
#endif
#ifdef USE_VAO
glVertexAttribPointer(_positionAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(_colourAttr, 3, GL_FLOAT, GL_FALSE, 0, (void*)(_colourOffset));
glEnableVertexAttribArray(_positionAttr);
glEnableVertexAttribArray(_colourAttr);
_vao.release();
#else
_vbo.release();
#endif
resizeGL(width(), height());
}
void GLWindow::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void GLWindow::paintGL()
{
if (! _context) // not yet initialized
return;
_context->makeCurrent(this);
QColor background(Qt::black);
glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
matrix.translate(0, 0, -2);
_program->bind();
_program->setUniformValue(_matrixUnif, matrix);
#ifdef USE_VAO
_vao.bind();
#else
_vbo.bind();
glVertexAttribPointer(_positionAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(_colourAttr, 3, GL_FLOAT, GL_FALSE, 0, (void*)(_colourOffset));
glEnableVertexAttribArray(_positionAttr);
glEnableVertexAttribArray(_colourAttr);
#endif
glEnable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, 6);
#ifdef USE_VAO
_vao.release();
#else
glDisableVertexAttribArray(_positionAttr);
glDisableVertexAttribArray(_colourAttr);
_vbo.release();
#endif
_program->release();
_context->swapBuffers(this);
_context->doneCurrent();
}
static const char *vertexShaderSource =
"attribute highp vec4 position;\n"
"attribute lowp vec4 colour;\n"
"uniform highp mat4 matrix;\n"
"varying lowp vec4 col;\n"
"void main() {\n"
" col = colour;\n"
" gl_Position = matrix * position;\n"
"}\n";
static const char *fragmentShaderSource =
"varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n";
void GLWindow::setupShaders()
{
_program = new QOpenGLShaderProgram(this);
_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
_program->link();
}
void GLWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);
if (isExposed())
{
if (! _context)
initGL();
paintGL();
}
}
glwindow.h:
#ifndef GL_WINDOW_H
#define GL_WINDOW_H
#include <QExposeEvent>
#include <QSurfaceFormat>
#include <QWindow>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
class GLWindow : public QWindow, protected QOpenGLFunctions
{
Q_OBJECT
public:
GLWindow(QWindow * = 0);
virtual ~GLWindow();
void initGL();
void paintGL();
void resizeGL(int, int);
protected:
virtual void exposeEvent(QExposeEvent *);
private:
void setupShaders();
QOpenGLBuffer _vbo;
QOpenGLBuffer _matbo;
QOpenGLContext *_context;
QOpenGLShaderProgram *_program;
QOpenGLVertexArrayObject _vao;
GLuint _positionAttr;
GLuint _colourAttr;
GLuint _matrixUnif;
size_t _colourOffset;
} ;
#endif
Can anyone spot something stupid I'm doing?