Tom Hammersley
Tom Hammersley

Reputation: 279

glGenVertexArraysOES returns a zero VAO on iOS, sometimes

I have a problem with the creation of VAOs on iOS. Sometimes it works just fine, and glGenVertexArraysOES returns a non-zero VAO via the second argument, but other times, it simply returns zero and no error via glGetError()

I have two cases. One works, the other doesn't. They're both in initialisation code, and they're both more or less the same code:

Working:

glGenBuffers(1, &m_vertexBufferObject);
errorCheck();

GLuint vao = 0;
glGenVertexArraysOES(1, &vao);
errorCheck();
glBindVertexArrayOES(vao);
errorCheck();

(vao set up goes here)

(vao != 0)

Not working:

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArrayOES(0);

errorCheck();
GLuint vao = 0;
glGenVertexArraysOES(1, &vao);
errorCheck();
glBindVertexArrayOES(vao);
errorCheck();

(vao == 0)

The glBind* functions in the non-working block are largely just an attempt to fix the problem by mitigating any previously bound state. It didn't help.

The code is multi-threaded. I have one context, and only one thread is permitted to use it at one time by way of a mutex. When a thread acquires the context, I lock the mutex and call setCurrentContext. I restore the current context to nil afterwards. I believe this should be an acceptable multi-threading setup from what I've read elsewhere. In any case, disabling threading and running everything on the same thread makes no difference!

The successful case happens right at the start, at initialisation time. That works fine... the other case happens during loading time, whilst a render loop is going on (but still being marshalled by said critical sections and context assignments), and that's the case that fails.

I'm basically assuming there must be some sort of precondition to glGenVertexArrayOES's execution that isn't being met. I don't think it's really the body of the VAO set up in the latter case, I think it's something that happens between the successful initial VAO setups, and the later attempted set ups.

It fails on both the sim and the phone, so there's clearly something going on... yet, it's a silent error, so it's giving me no angle to investigate. It works just fine on OpenGL for OS X. Furthermore, attempts to replicate the error or similar conditions (eg, the threading in various pieces of sample code have failed.

Any ideas?

Thanks.

Upvotes: 1

Views: 2125

Answers (2)

Tom Hammersley
Tom Hammersley

Reputation: 279

Ok, I finally cracked this.

It turns out, that I had some RAII-like code that would call setCurrentContext before and after some OpenGL ops on a thread. Crucially, it would [EAGLContext setCurrentContext:nil] when a scope ended... but these scopes could be nested, and it did that for every nested scope, not the last scope.

The upshot of that was that the current context was nil when OpenGL was called. The OpenGL calls would just "do nothing", and "do nothing" included "throw no errors" - you'd think glGetError() might have said something at least.

So, if you don't have a current context set, then it seems GL on the mac crashes, but GLES on IOS just early-outs with no error.

Upvotes: 0

Cynichniy Bandera
Cynichniy Bandera

Reputation: 6103

This is a class of view derived from GLKViewController.

@interface IncubeViewController : GLKViewController {
      ...
}

This is how I load vao:

- (void) loadGeometry
{
        /* Pieces */
        for (int i = ptFirst; i < ptLast; i++) {
                glGenVertexArraysOES(1, &pieceObject[i].array);
                glBindVertexArrayOES(pieceObject[i].array);

                glGenBuffers(1, &pieceObject[i].buffer);
                glBindBuffer(GL_ARRAY_BUFFER, pieceObject[i].buffer);

                glBufferData(GL_ARRAY_BUFFER, vertextData[i].vertNumber * sizeof(Vertex),
                             vertextData[i].vertArray, GL_STATIC_DRAW);

                glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_TRUE,
                                      sizeof(Vertex), (void *)offsetof(Vertex, position));
                glEnableVertexAttribArray(GLKVertexAttribPosition);
                glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_TRUE,
                                      sizeof(Vertex), (void *)offsetof(Vertex, normal));
                glEnableVertexAttribArray(GLKVertexAttribNormal);
        }

        /* Cube */
        glGenVertexArraysOES(1, &cubeObject.array);
        glBindVertexArrayOES(cubeObject.array);

        glGenBuffers(1, &cubeObject.buffer);
        glBindBuffer(GL_ARRAY_BUFFER, cubeObject.buffer);

        glBufferData(GL_ARRAY_BUFFER, CubeDataNumberOfVertices * sizeof(Vector3),
                     CubeData, GL_STATIC_DRAW);

        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,
                              0, (void *)0);
        glEnableVertexAttribArray(GLKVertexAttribPosition);

        /* Sphere */
        glGenVertexArraysOES(1, &sphereObject.array);
        glBindVertexArrayOES(sphereObject.array);

        glGenBuffers(1, &sphereObject.buffer);
        glBindBuffer(GL_ARRAY_BUFFER, sphereObject.buffer);

        glBufferData(GL_ARRAY_BUFFER, SphereDataNumberOfVertices * sizeof(Vertex),
                     SphereData, GL_STATIC_DRAW);

        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,
                              sizeof(Vertex), (void *)offsetof(Vertex, position));
        glEnableVertexAttribArray(GLKVertexAttribPosition);
        glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE,
                              sizeof(Vertex), (void *)offsetof(Vertex, normal));
        glEnableVertexAttribArray(GLKVertexAttribNormal);

        /* Diagonal grid */
        glGenVertexArraysOES(1, &diagonalGridObject.array);
        glBindVertexArrayOES(diagonalGridObject.array);

        glGenBuffers(1, &diagonalGridObject.buffer);
        glBindBuffer(GL_ARRAY_BUFFER, diagonalGridObject.buffer);

        glBufferData(GL_ARRAY_BUFFER, sizeof(diagonalGridData),
                     diagonalGridData, GL_STATIC_DRAW);

        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,
                              sizeof(Vector3), (void *)0);
        glEnableVertexAttribArray(GLKVertexAttribPosition);

        glBindVertexArrayOES(0);
}

- (void) unloadGeometry
{
        glDeleteBuffers(1, &diagonalGridObject.buffer);
        glDeleteVertexArraysOES(1, &diagonalGridObject.array);
        glDeleteBuffers(1, &cubeObject.buffer);
        glDeleteVertexArraysOES(1, &cubeObject.array);
        glDeleteBuffers(1, &sphereObject.buffer);
        glDeleteVertexArraysOES(1, &sphereObject.array);
        for (int i = ptFirst; i < ptLast; i++) {
                glDeleteBuffers(1, &pieceObject[i].buffer);
                glDeleteVertexArraysOES(1, &pieceObject[i].array);
        }
}

While it is not showing you're doing something wrong, I add it here just for ref if anyone needs it.

One explanation I have is that you're trying to create your vao in wrong moment of app init or init was not quite correct (for example, context is not set).

I have the following stack here:

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

        self.effect = [[[GLKBaseEffect alloc] init] autorelease];
        if (self.effect) {
                self.effect.useConstantColor = GL_TRUE;
                self.effect.colorMaterialEnabled = GL_TRUE;
                self.effect.light0.enabled = GL_TRUE;
                self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
        }

        ((GLKView *)self.view).drawableMultisample = GLKViewDrawableMultisample4X;
        self.pauseOnWillResignActive = YES;
        self.resumeOnDidBecomeActive = YES;
        self.preferredFramesPerSecond = 30;

        glDisable(GL_DITHER);
        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);
        glLineWidth(2.0f);
        [self loadGeometry];
}

- (void)viewDidLoad
{
        [super viewDidLoad];
        self.context = [[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2] autorelease];
        if (!self.context)
                NSLog(@"Failed to create ES context");

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

        [self setupGL];
}

Upvotes: 1

Related Questions