QOpenGLVertexArrayObject 导致多个 VBO 的段错误?

Posted

技术标签:

【中文标题】QOpenGLVertexArrayObject 导致多个 VBO 的段错误?【英文标题】:QOpenGLVertexArrayObject causes segfault with multiple VBOs? 【发布时间】:2014-05-18 07:18:05 【问题描述】:

我正在尝试在 Qt 5 中使用实例化渲染,但遇到了一些麻烦。我想我的问题可以追溯到我对 VAO 的使用。我是一位经验丰富的程序员,知道这些问题几乎都是我的错,但我偶尔会遇到 Qt 错误,所以我想在提交错误报告之前我可以寻求帮助。

我已经整理了一个简单的示例,它不使用实例化(出于某种原因,我无法请求最新版本的 OpenGL,即使我在“真实”软件中没有这个问题我'正在开发中)。

在初始化时,我创建了一个 VAO 和两个 VBO。一个 VBO 填充了一些数据,并保持绑定;另一个已分配,但已释放(我从未真正使用过它:我计划最终将矩阵写入它以进行实例化,但没有走得那么远)。

我添加了一些预处理器指令来简化打开或关闭 VAO 或第二个 VBO 的使用。如果我同时使用 VAO 和 VBO,则会出现分段错误。如果我摆脱了第二个 VBO 或 VAO,则没有 seg 错误并且绘图看起来正确(它应该是在彩色三角形后面绘制的白色三角形)。

代码如下:

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

谁能看出我在做什么蠢事?

【问题讨论】:

关于无法使用最新版本的 openGL。一两个月前我遇到了同样的问题。看到这个帖子,它可能对你有帮助,它对我有用:qt-project.org/forums/viewthread/20424 @agrum 谢谢。我希望我知道为什么在我的实际软件中没有遇到这个问题。该线程让我意识到我可能也应该使用 QOpenGLShaderProgram 的一些功能。 【参考方案1】:

明显的 GL 问题出现在您的 glVertexAttribPointer 调用中。

glVertexAttribPointer 设置通用顶点属性数据。 它的行为会根据缓冲区当前是否绑定在GL_ARRAY_BUFFER 绑定上而改变

是的,这太可怕了。

由于这是绑定到该绑定点的缓冲区的主要用途,因此它们被称为 顶点 缓冲区对象。

如果存在缓冲区限制,则它会配置该属性以从该缓冲区读取数据,并且最后一个参数被解释为距缓冲区开头的字节偏移量。将要读取的实际数据量取决于其他参数(计数、数据类型的大小、步幅)。

如果在该绑定点没有缓冲区绑定,那么它会从最后一个参数指向的内存位置(在程序的地址空间中)读取数据。同样,从主内存读取并传输到 GL 的数据量取决于其他参数。

在您的情况下,没有任何约束:您刚刚释放了绑定到 GL_ARRAY_BUFFER 的任何内容。是的,这些缓冲点不会“堆积”或任何东西。在该绑定点最多可以绑定 一个 缓冲区。

当你打电话时

_matbo.release();

您是在告诉 GL 放弃对 GL_ARRAY_BUFFER 绑定点的任何绑定。因此,这些调用:

glVertexAttribPointer(_positionAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(_colourAttr, 3, GL_FLOAT, GL_FALSE, 0, (void*)(_colourOffset));

告诉 GL 从主内存中读取顶点属性数据,我很确定内存地址 0_colourOffset 没有任何用处。预计很快就会崩溃......

解决方案?只需在调用glVertexAttribPointer 之前将正确的缓冲区对象重新绑定到绑定点即可:

_vbo.bind();
glVertexAttribPointer(_positionAttr, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(_colourAttr, 3, GL_FLOAT, GL_FALSE, 0, (void*)(_colourOffset));

这正是您在非 VAO 工作路径中所做的。


其他建议:

    既然您已经在使用QOpenGLShaderProgram,您也可以只使用它的功能:

    program->setAttributeBuffer(...);
    

    在将 Qt 类型的表示(例如 QVector3D 或 QMatrix4x4)上传到 OpenGL 时,您应该非常小心。您无法控制它们的内存表示。您应该使用一些 Qt 包装器,或者上传原始数据(100% 确定)。

    您应该在使用之前询问特定的(最低)GL 版本,或者至少测试 VAO 创建是否成功(create 返回 true)。 VAO 在例如 GL 2 或 ES 2 上不存在,但需要扩展。

【讨论】:

在同一个注释中,从您的上下文中提取您的一组函数,以获得您的版本中支持的函数的确切范围。 QOpenGLContext::versionFunctions&lt;QOpenGLFunction*&gt;();。这样你就可以确定你没有调用最近的函数。 谢谢,您的解释很有道理。是否可以将 glVertexAttribDivisor(用于实例化)与 QOpenGLShaderProgram 函数结合使用,或者这是灾难的根源? 抱歉,今天早些时候忘记接受您的答复了。到目前为止,我在 Qt 中实现实例化的尝试对我来说还没有成功(尽管我现在至少可以使用版本设置)。 完全可以拨打glVertexAttribDivisor。其实真的应该被QOpenGLShaderProgram曝光...

以上是关于QOpenGLVertexArrayObject 导致多个 VBO 的段错误?的主要内容,如果未能解决你的问题,请参考以下文章