什么可能导致glBufferSubData中的opengl段错误?

时间:2014-08-15 15:14:07

标签: linux opengl segmentation-fault 64-bit intel

我已经在这段时间内缩减了这段时间,这是我机器上的一个非常简单的可重复示例(下图)。我有一种下沉的感觉,它是一个驱动程序错误,但我对OpenGL非常不熟悉,所以我更有可能只是做错了。

这是正确的OpenGL 3.3代码吗?不论平台和编译器都应该没问题吗?

这是使用gcc -ggdb -lGL -lSDL2

编译的代码
#include <stdio.h>
#include "GL/gl.h"
#include "GL/glext.h"
#include "SDL2/SDL.h"

// this section is for loading OpenGL things from later versions.

typedef void (APIENTRY *GLGenVertexArrays) (GLsizei n, GLuint *arrays);
typedef void (APIENTRY *GLGenBuffers) (GLsizei n, GLuint *buffers);
typedef void (APIENTRY *GLBindVertexArray) (GLuint array);
typedef void (APIENTRY *GLBindBuffer) (GLenum target, GLuint buffer);
typedef void (APIENTRY *GLBufferData) (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
typedef void (APIENTRY *GLBufferSubData) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
typedef void (APIENTRY *GLGetBufferSubData) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
typedef void (APIENTRY *GLFlush) (void);
typedef void (APIENTRY *GLFinish) (void);

GLGenVertexArrays glGenVertexArrays = NULL;
GLGenBuffers glGenBuffers = NULL;
GLBindVertexArray glBindVertexArray = NULL;
GLBindBuffer glBindBuffer = NULL;
GLBufferData glBufferData = NULL;
GLBufferSubData glBufferSubData = NULL;
GLGetBufferSubData glGetBufferSubData = NULL;

void load_gl_pointers() {
  glGenVertexArrays = (GLGenVertexArrays)SDL_GL_GetProcAddress("glGenVertexArrays");
  glGenBuffers = (GLGenBuffers)SDL_GL_GetProcAddress("glGenBuffers");
  glBindVertexArray = (GLBindVertexArray)SDL_GL_GetProcAddress("glBindVertexArray");
  glBindBuffer = (GLBindBuffer)SDL_GL_GetProcAddress("glBindBuffer");
  glBufferData = (GLBufferData)SDL_GL_GetProcAddress("glBufferData");
  glBufferSubData = (GLBufferSubData)SDL_GL_GetProcAddress("glBufferSubData");
  glGetBufferSubData = (GLGetBufferSubData)SDL_GL_GetProcAddress("glGetBufferSubData");
}

// end OpenGL loading stuff


#define CAPACITY (1 << 8)

// return nonzero if an OpenGL error has occurred.
int opengl_checkerr(const char* const label) {
  GLenum err;
  switch(err = glGetError()) {
    case GL_INVALID_ENUM:
      printf("GL_INVALID_ENUM");
      break;
    case GL_INVALID_VALUE:
      printf("GL_INVALID_VALUE");
      break;
    case GL_INVALID_OPERATION:
      printf("GL_INVALID_OPERATION");
      break;
    case GL_INVALID_FRAMEBUFFER_OPERATION:
      printf("GL_INVALID_FRAMEBUFFER_OPERATION");
      break;
    case GL_OUT_OF_MEMORY:
      printf("GL_OUT_OF_MEMORY");
      break;
    case GL_STACK_UNDERFLOW:
      printf("GL_STACK_UNDERFLOW");
      break;
    case GL_STACK_OVERFLOW:
      printf("GL_STACK_OVERFLOW");
      break;
    default: return 0;
  }

  printf(" %s\n", label);
  return 1;
}

int main(int nargs, const char* args[]) {
  printf("initializing..\n");
  SDL_Init(SDL_INIT_EVERYTHING);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

  SDL_Window* const w =
    SDL_CreateWindow(
      "broken",
      SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
      1, 1,
      SDL_WINDOW_OPENGL
    );

  if(w == NULL) {
    printf("window was null\n");
    return 0;
  }

  SDL_GLContext context = SDL_GL_CreateContext(w);

  if(context == NULL) {
    printf("context was null\n");
    return 0;
  }

  load_gl_pointers();

  if(opengl_checkerr("init")) {
    return 1;
  }

  printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR));
  printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));

  float* const vs = malloc(CAPACITY * sizeof(float));
  memset(vs, 0, CAPACITY * sizeof(float));

  unsigned int i = 0;
  while(i < 128000) {
    GLuint vertex_array;
    GLuint vertex_buffer;

    glGenVertexArrays(1, &vertex_array);
    glBindVertexArray(vertex_array);

    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);

    if(opengl_checkerr("gen/binding")) {
      return 1;
    }

    glBufferData(
      GL_ARRAY_BUFFER,
      CAPACITY * sizeof(float),
      vs, // initialize with `vs` just to make sure it's allocated.
      GL_DYNAMIC_DRAW
    );

    // verify that the memory is allocated by reading it back into `vs`.
    glGetBufferSubData(
      GL_ARRAY_BUFFER,
      0,
      CAPACITY * sizeof(float),
      vs
    );

    if(opengl_checkerr("creating buffer")) {
      return 1;
    }

    glFlush();
    glFinish();

    // segfault occurs here..
    glBufferSubData(
      GL_ARRAY_BUFFER,
      0,
      CAPACITY * sizeof(float),
      vs
    );

    glFlush();
    glFinish();

    ++i;
  }

  return 0;
}

当我将迭代次数从64k提高到128k时,我开始得到:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff754c859 in __memcpy_sse2_unaligned () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff754c859 in __memcpy_sse2_unaligned () from /usr/lib/libc.so.6
#1  0x00007ffff2ea154d in ?? () from /usr/lib/xorg/modules/dri/i965_dri.so
#2  0x0000000000400e5c in main (nargs=1, args=0x7fffffffe8d8) at opengl-segfault.c:145

但是,如果没有segfaulting,我可以将容量增加一倍以上(将迭代次数保持在64k)。

GL_VENDOR: Intel Open Source Technology Center
GL_RENDERER: Mesa DRI Intel(R) Haswell Mobile

2 个答案:

答案 0 :(得分:1)

调用glGenTextures和glBindTexture时遇到了类似的问题。我试过调试,当我尝试逐步完成这些行时,我会得到类似的东西:

Program received signal SIGSEGV, Segmentation fault. 0x00007ffff26eaaa8 in ?? () from /usr/lib/x86_64-linux-gnu/dri/i965_dri.so

请注意,在添加纹理之前,我可以成功运行带有vbos和vaos的程序并生成网格。在查看建议从xf86-video-intel driver切换到xf86-video-fbdev驱动程序的答案后,我会建议反对它(这个问题或面临的用户确实没有太多信息使用集成的英特尔显卡在Linux上进行段错误。也许这是一个很好的问题,可以在Intel OpenSource询问人们。

我找到的解决方案是停止使用freeglut 。切换到 glfw 。除了问题之外,intel linux图形堆栈是否存在实际问题,似乎可解决的问题是freeglut。如果你想在你的机器上使用glfw最新的opengl核心配置文件,你只需要以下内容:

glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

设置转发compat(虽然我看到许多帖子争论你不应该这样做)意味着mesa可以自由选择允许将最小上下文设置为3.0或更高的核心上下文。我想freeglut在与台面交互的某个地方肯定会出错,如果有人能分享一些关于这个的话会很棒!

答案 1 :(得分:0)

这是Linux的intel图形驱动程序中的一个错误。从xf86-video-intel驱动程序切换到xf86-video-fbdev驱动程序可以解决问题。

编辑:我不建议切换到fbdev,只是将其用作实验,以查看段错误是否消失。