Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to load/setup scene in background thread results in "OpenGL error 0x0502 in -[CCSprite draw] 530"

I have a cocos2d v2.x app which has a scene that has a lot of sprites, nodes, configuration, data, etc... It is quite expensive to load, so much that when adding the scene to the director, there is a 1/2 to 1 second pause, causing the current running animations to freeze until this the scene is loaded. I profiled the slowest methods and am trying to execute them asynchronously in a background thread and display a progress spinner while it loads.

My implementation is something like this:

-(void)performAsyncLoad {
    self.progressSpinner.visible = YES;
    self.containerForLoadedStuff.visible = NO;
    self.mainContext = [EAGLContext currentContext];

    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                            selector:@selector(loadDependencies)
                                                                              object:nil];
    [queue addOperation:operation];
}

-(void)loadDependencies {
    @autoreleasepool {
        glFlush();
        EAGLSharegroup *shareGroup = [[(CCGLView*)[[CCDirector sharedDirector] view] context] sharegroup];
        EAGLContext *context = [[EAGLContext alloc] initWithAPI:[[EAGLContext currentContext] API] sharegroup:shareGroup];
        [EAGLContext setCurrentContext:context];

        // ... expensive stuff here
        // [self.containerForLoadedStuff addChild:sprites, etc...]

        [self performSelectorOnMainThread:@selector(done) withObject:nil waitUntilDone:NO];
    }
}

-(void)done {
    glFlush();
    [EAGLContext setCurrentContext:self.mainContext];
    self.progressSpinner.visible = NO;
    self.containerForLoadedStuff.visible = YES;
}

Unfortunately this is not working, once the operation is invoked, it crashes with EXC_BAD_ACCESS on line 523 of CCTextureAtlas on

glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(_indices[0])) );

and the console log shows billions of:

OpenGL error 0x0502 in -[CCSprite draw] 530

what am I doing wrong?

UPDATE

I changed my code to do:

dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
CCGLView *view = (CCGLView*)[[Director sharedDirector] view];
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:[[view context] sharegroup]];

dispatch_async(queue, ^{
   [EAGLContext setCurrentContext:context];

   // expensive calls

   glFlush();
   [self performSelector:@selector(done) onThread:[[CCDirector sharedDirector] runningThread] withObject:nil waitUntilDone:NO];
   [EAGLContext setCurrentContext:nil];
});

And it stopped the crashing, and everything works, however I still get a billion:

OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530

Any ideas why these errors are happening and how I can stop them?

YET ANOTHER UPDATE

This makes no sense... apparently these errors come from adding sprites to a CCSpriteBatchNode. If I put those on a regular CCNode then everything works fine. WHAT THE HELL!?!?!?!?!

AND ONE LAST FINAL UPDATE*

It appears that there is just a whole lot of nonsense that I just don't understand. I've managed to make these errors go away 98%, but they still seem to randomly happen extremely intermittently. I did a ton of debugger and trial & error testing, and found that this code:

-(void)loadDependencies {
    dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
    CCGLView *view = (CCGLView*)[[Director sharedDirector] view];
    EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:[[view context] sharegroup]];

    dispatch_async(queue, ^{
       [EAGLContext setCurrentContext:context];

       [self.myObject doExpensiveStuff];

       glFlush();
       [self performSelector:@selector(done) onThread:[[CCDirector sharedDirector] runningThread] withObject:nil waitUntilDone:NO];
       [EAGLContext setCurrentContext:nil];
    });
}

-(void)done {
    [self.delegate completedAsyncStuff];
}

Caused random crashes-- usually cocos removeFromParent trying to remove a quad at an invalid index... So then I tried pausing the object before doing work on it..

//... background thread stuff:
[self.myObject pauseSchedulerAndActions];
[self.myObject doExpensiveStuff];
[self.myObject resumeSchedulerAndActions];

Then it no longer crashed, but put gazillions of those OpenGL error 0x0502 in -[CCSprite draw] 530 in the log......

So then I did some extreme logging to try to find where these errors were happening...

... // previous code above... etc
       NSLog(@"gl flush...");
       glFlush();
       NSLog(@"after gl flush...");
       [self performSelector:@selector(done) onThread:[[CCDirector sharedDirector] runningThread] withObject:nil waitUntilDone:NO];
       [EAGLContext setCurrentContext:nil];
    });
}

-(void)done {
    NSLog(@"done scheduling notify delegate");
    [self scheduleOnce:@selector(notifyDelegate) delay:1];
}

-(void)notifyDelegate {
    NSLog(@"about to notify delegate");
    [self.delegate completedAsyncStuff];
}

In my log I see:

gl flush
after gl flush
done scheduling notify delegate
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
OpenGL error 0x0502 in -[CCSprite draw] 530
about to notify delegate

So these errors happen when cocos is waiting to fire of a scheduled selector???? What the hell!? I can't stand this anymore, and no one has been able to help me, so it's time for a bounty.

like image 692
patrick Avatar asked Dec 09 '15 09:12

patrick


2 Answers

Here's how I do it:

LoadingScene

-(void) onEnterTransitionDidFinish {
    [super onEnterTransitionDidFinish];
    [NSThread detachNewThreadSelector:@selector(loadGameSceneInAnotherThread) toTarget:self withObject:nil];
}

- (void) loadGameSceneInAnotherThread {
    NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
    CCGLView *view = (CCGLView*)[[CCDirector sharedDirector] view];
    EAGLContext *auxGLcontext = [[EAGLContext alloc]
                                 initWithAPI:kEAGLRenderingAPIOpenGLES2
                                 sharegroup:[[view context] sharegroup]];

    if( [EAGLContext setCurrentContext:auxGLcontext] ) {
       self.gameScene = [GameScene sceneWithLevelName:levelName];
       self.gameLoadingDone = YES;
       glFlush();
       [EAGLContext setCurrentContext:nil];
    }
    [auxGLcontext release];
    [autoreleasepool release];
}


//this method ticks every 0.5sec
-(void) checkIfGameSceneLoaded {
    if (self.gameLoadingDone) {
        [self unschedule:@selector(checkIfGameSceneLoaded)];
        [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.75 scene:self.gameScene]];
    }
}

However, I don't recommend background loading for scenes.. because cocos2d not really designed to do so...

like image 128
KAMIKAZE Avatar answered Oct 21 '22 08:10

KAMIKAZE


Can you tell me why you do what you want THAT hard?

If I were you, I would do the following, to load scene async.

  1. Load a middle scene with loading indicator, or add it to the current one. + Create a new scene object.
  2. Dispatch asynchrounously expensive method for that new scene.
  3. After that is done replace the scene with the CCDirector, dispatch tihs operation synchronously

Use GCD as you do right now. You don't really wanna draw your scene while loading it, believe me.

like image 23
s1ddok Avatar answered Oct 21 '22 07:10

s1ddok