【问题标题】:iOS OpenGL ES Crash loading same object multiple timesiOS OpenGL ES崩溃多次加载相同的对象
【发布时间】:2013-09-10 15:31:34
【问题描述】:

基本上,在我的 iOS 应用程序中,我有一个模态屏幕,我在其中显示一些 OpenGL ES 图形。在应用程序崩溃之前,我可以进入和退出此模式屏幕 6 次。我假设这是一个内存问题,但我不知道什么和/或在哪里。

非常感谢您对此事的任何帮助。

在我的 OGLViewController 中:

#define OPENGL_ERROR_CHECK {GLuint error = glGetError(); ((error == GL_NO_ERROR) ? :  NSLog(@"GL Error: %d", (error)));}

@interface OGLItemViewController : GLKViewController

@property (nonatomic) GLuint program;

// item ogl arrays + buffers
@property (nonatomic) NSInteger numOGLBuffers;
@property (nonatomic) GLuint* vertexArrays;
@property (nonatomic) GLuint* vertexBuffers;

// index buffer for ogl item vertices
@property (nonatomic) GLuint* indexBuffers;

@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKBaseEffect *effect;

@end

@implementation

- (void)viewDidLoad
{
  [super viewDidLoad];

  self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

  OPENGL_ERROR_CHECK
  glFinish(); // put this in here to make sure all previous calls have been finished
  OPENGL_ERROR_CHECK

  if (!self.context)
  {
    NSLog(@"Failed to create ES context");
  }

  GLKView *view = (GLKView *) self.view;
  view.context = self.context;
  view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
  view.opaque = NO;

  self.numOGLBuffers = 0;

  self.vertexArrays = nil;
  self.vertexBuffers = nil;
  self.indexBuffers = nil;
}

- (void)setupGL
{
  OPENGL_ERROR_CHECK
  [EAGLContext setCurrentContext:self.context];
  OPENGL_ERROR_CHECK

  [self loadShaders];    
  OPENGL_ERROR_CHECK

  self.effect = [[GLKBaseEffect alloc] init];
  OPENGL_ERROR_CHECK
  self.effect.light0.enabled = GL_TRUE;
  OPENGL_ERROR_CHECK

  self.effect.colorMaterialEnabled = GL_TRUE;
  OPENGL_ERROR_CHECK

  self.effect.lightModelTwoSided = GL_FALSE;
  OPENGL_ERROR_CHECK

  self.effect.light0.diffuseColor = GLKVector4Make(0.69f, 0.69f, 0.69f, 0.5f);
  OPENGL_ERROR_CHECK

  glEnable(GL_DEPTH_TEST);
  OPENGL_ERROR_CHECK

  Item *item = [GlobalStore sharedInstance].item

  NSMutableArray *shells = item.geometry;

  if (shells.count > 0)
  {    
    _vertexArrays = malloc(shells.count * sizeof(GLuint));
    _vertexBuffers = malloc(shells.count * sizeof(GLuint));
    _indexBuffers = malloc(shells.count * sizeof(GLuint));
    self.numOGLBuffers = shells.count;

    for (int i = 0; i < shells.count; i++)
    {
        Geometry *geom = [shells objectAtIndex:i];
        if (geom.vertexCount > 0)
        {
            GLuint vao = 0;
            OPENGL_ERROR_CHECK
            glGenVertexArraysOES(1, &vao);

            OPENGL_ERROR_CHECK
            glBindVertexArrayOES(vao);
            OPENGL_ERROR_CHECK

            _vertexArrays[i] = vao;

            if (!geom.vertices)
            {
                [self displayError:-998];  // generic error codes that i've just canned in to see if any problems with these pointers
            }
            if (!geom.indices)
            {
                [self displayError:-997];
            }

            // create vertice buffer
            GLuint vbo = 0;
            glGenBuffers(1, &vbo);
            OPENGL_ERROR_CHECK
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            OPENGL_ERROR_CHECK
            glBufferData(GL_ARRAY_BUFFER, geom.vertexCount * sizeof(OGLVertices), geom.vertices, GL_STATIC_DRAW);
            OPENGL_ERROR_CHECK

            _vertexBuffers[i] = vbo;

            // create index by buffer
            GLuint ibo = 0;
            glGenBuffers(1, &ibo);
            OPENGL_ERROR_CHECK
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
            OPENGL_ERROR_CHECK
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, geom.indexCount * sizeof(GLuint), geom.indices, GL_STATIC_DRAW);
            OPENGL_ERROR_CHECK

            _indexBuffers[i] = ibo;

            // enable position, normal and colour attributes
            glEnableVertexAttribArray(GLKVertexAttribPosition);
            OPENGL_ERROR_CHECK
            glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Position));
            OPENGL_ERROR_CHECK
            glEnableVertexAttribArray(GLKVertexAttribNormal);
            OPENGL_ERROR_CHECK
            glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Normal));
            OPENGL_ERROR_CHECK
            glEnableVertexAttribArray(GLKVertexAttribColor);
            OPENGL_ERROR_CHECK
            glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Colour));
            OPENGL_ERROR_CHECK
        }
      }

      glBindVertexArrayOES(0);
      OPENGL_ERROR_CHECK
  }
}

- (void)tearDownGL
{
  [EAGLContext setCurrentContext:self.context];
  self.effect = nil;

  [self deleteOGLData];
}

- (void)dealloc
{
  [self tearDownGL];

  if ([EAGLContext currentContext] == self.context)
  {
    [EAGLContext setCurrentContext:nil];
  }
}

- (void) viewDidDisappear:(BOOL)animated
{
  [self tearDownGL];
}

- (void)deleteOGLData
{
    // delete ogl buffers and arrays
    if (self.numOGLBuffers > 0)
  {
    if (_vertexBuffers)
    {
        OPENGL_ERROR_CHECK
        glDeleteBuffers(self.numOGLBuffers, _vertexBuffers);

        OPENGL_ERROR_CHECK
        free(_vertexBuffers);
        _vertexBuffers = nil;
    }
    if (_vertexArrays)
    {
        glDeleteVertexArraysOES(self.numOGLBuffers, _vertexArrays);
        OPENGL_ERROR_CHECK
        free(_vertexArrays);
        _vertexArrays = nil;
    }
    if (_indexBuffers)
    {
        glDeleteBuffers(self.numOGLBuffers, _indexBuffers);
        OPENGL_ERROR_CHECK
        free(_indexBuffers);
        _indexBuffers = nil;
    }
  }


  self.numOGLBuffers = 0;

  glDeleteProgram(self.program);
  OPENGL_ERROR_CHECK

  self.program = 0;

  glFinish(); // again, just put this in here to check everything has finished
  OPENGL_ERROR_CHECK
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{ 
    OPENGL_ERROR_CHECK
    glClearColor(1.0, 1.0, 1.0, 0.0);
    OPENGL_ERROR_CHECK
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    OPENGL_ERROR_CHECK

    // Render the object with GLKit
    [self.effect prepareToDraw];
    OPENGL_ERROR_CHECK


    NSMutableArray *shells = [GlobalStore sharedInstance].item.geometry;
    if (shells && _vertexArrays && _vertexBuffers)
    {          
        for (int i = 0; i < shells.count; i++)
        {
            Geometry *geom = [shells objectAtIndex:i];

            if (geom.vertexCount > 0)
            {
                GLuint vao = self.vertexArrays[i];
                glBindVertexArrayOES(vao);
                OPENGL_ERROR_CHECK

                if(geom.indexCount == 0)
                    [self displayError:-996];

                glDrawElements(GL_TRIANGLES, geom.indexCount, GL_UNSIGNED_INT, 0);
                OPENGL_ERROR_CHECK
                self.initialised = YES;
            }
        }        
    }
}

- (BOOL)loadShaders
{
  GLuint vertShader, fragShader;
  NSString *vertShaderPathname, *fragShaderPathname;

  // Create shader program.
  self.program = glCreateProgram();
  OPENGL_ERROR_CHECK

  // Create and compile vertex shader.
  vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
  if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname])
  {
    NSLog(@"Failed to compile vertex shader");
    return NO;
  }

  // Create and compile fragment shader.
  fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
  if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname])
  {
    NSLog(@"Failed to compile fragment shader");
    return NO;
  }

  // Attach vertex shader to program.
  glAttachShader(_program, vertShader);
  OPENGL_ERROR_CHECK

  // Attach fragment shader to program.
  glAttachShader(_program, fragShader);
  OPENGL_ERROR_CHECK

  // Bind attribute locations.
  // This needs to be done prior to linking.
  glBindAttribLocation(_program, GLKVertexAttribPosition, "position");
  OPENGL_ERROR_CHECK

  glBindAttribLocation(_program, GLKVertexAttribNormal, "normal");
  OPENGL_ERROR_CHECK

  // Link program.
  if (![self linkProgram:_program])
  {
    NSLog(@"Failed to link program: %d", _program);

    if (vertShader)
    {
        glDeleteShader(vertShader);
        OPENGL_ERROR_CHECK
        vertShader = 0;
    }
    if (fragShader)
    {
        glDeleteShader(fragShader);
        OPENGL_ERROR_CHECK
        fragShader = 0;
    }
    if (_program)
    {
        glDeleteProgram(_program);
        OPENGL_ERROR_CHECK
        _program = 0;
    }

    return NO;
  }

  // Get uniform locations.
  uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
  uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(_program, "normalMatrix");

  // Release vertex and fragment shaders.
  if (vertShader)
  {
    glDetachShader(_program, vertShader);
    OPENGL_ERROR_CHECK
    glDeleteShader(vertShader);
    OPENGL_ERROR_CHECK
  }
  if (fragShader)
  {
    glDetachShader(_program, fragShader);
    OPENGL_ERROR_CHECK
    glDeleteShader(fragShader);
    OPENGL_ERROR_CHECK
  }

  return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
  GLint status;
  const GLchar *source;

  source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
  if (!source)
  {
    NSLog(@"Failed to load vertex shader");
    return NO;
  }

  *shader = glCreateShader(type);
  OPENGL_ERROR_CHECK
  glShaderSource(*shader, 1, &source, NULL);
  OPENGL_ERROR_CHECK
  glCompileShader(*shader);
  OPENGL_ERROR_CHECK

  glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
  OPENGL_ERROR_CHECK
  if (status == 0)
  {
    glDeleteShader(*shader);
    OPENGL_ERROR_CHECK
    return NO;
  }

  return YES;
}

- (BOOL)linkProgram:(GLuint)prog
{
  GLint status;
  glLinkProgram(prog);
  OPENGL_ERROR_CHECK

  glGetProgramiv(prog, GL_LINK_STATUS, &status);
  OPENGL_ERROR_CHECK
  if (status == 0)
  {
    return NO;
  }

  return YES;
}

- (BOOL)validateProgram:(GLuint)prog
{
  GLint logLength, status;

  glValidateProgram(prog);
  OPENGL_ERROR_CHECK
  glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
  OPENGL_ERROR_CHECK
  if (logLength > 0)
  {
    GLchar *log = (GLchar *)malloc(logLength);
    glGetProgramInfoLog(prog, logLength, &logLength, log);
    OPENGL_ERROR_CHECK
    NSLog(@"Program validate log:\n%s", log);
    free(log);
  }

  glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
  OPENGL_ERROR_CHECK
  if (status == 0)
  {
    return NO;
  }

  return YES;
}


-(void)displayError: (GLuint) err
{
  NSString *msg = [[NSString alloc] initWithFormat:@"OpenGL Error: %d", err];
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Error"
                                                  message: msg
                                                 delegate:self
                                        cancelButtonTitle:@"OK"
                                        otherButtonTitles:nil];

  [alert show];
}


@end

现在 - 我不知道这样做是否正确,但在 viewdiddisappear 方法中,我确保删除所有 ogl 数据。我想确保当我退出视图时,ogl 数据会从内存中删除。

所以,如果我进入模态视图,退出,然后再重复 5 次,我会出现黑屏崩溃,并且会中断,屏幕如下:

我已确保一切都在适当的情况下在单个线程中完成。我试图确保删除所有对象和缓冲区。

所以我想知道我到底做错了什么导致它突然崩溃......分析器中似乎没有任何指示。我似乎没有任何内存泄漏或任何会导致内存耗尽的实质性内容。

(我知道我正在渲染每一帧,当我不需要时 - 我会在稍后解决这个问题。)

我拥有的 globalstore 包含我使用的一些对象的实例。这些应该始终有效(我已经检查过)。

【问题讨论】:

  • 您是否尝试在所有异常上添加断点?如果幸运的话,它应该能够捕捉到崩溃的确切原因,只需转到断点导航器,左下角,按“+”和“添加异常断点”和“完成”。现在去重现崩溃并检查堆栈跟踪,看看你的代码的哪一部分是罪魁祸首。

标签: ios opengl-es crash


【解决方案1】:

我需要做的是删除 GLKView 可绘制对象。

在 ViewDidDisappear 方法中(可能不是正确的位置,但它有效)添加以下内容:

GLKView *view = (GLKView*) self.view;
[view deleteDrawable];

【讨论】:

  • 实际上发生了一些奇怪的事情——视图退出时调用了 viewdidload 方法。所以会发生什么,我会在退出屏幕时释放所有适当的内存,然后调用 viewdidload 方法,并重新设置所有内存分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
相关资源
最近更新 更多