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.
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...
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.
Use GCD as you do right now. You don't really wanna draw your scene while loading it, believe me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With