iphonecocos2d-iphoneccscrolllayer

Image which I loaded in thread is not appeared


I'm using the CCScrollLayer and I wanna prepare images during player select the stages. It's simple but when I scroll stages, it tasks time(delay to load images).

so I decided to use NSThread. and I got a message "cocos2d: CCSpriteFrameCache: Trying to use file 'Level3.png' as texture" from cocos2d. then It's supposed to appear. but These images I loaded on thread doesn't to appear as I want. just nothing.

-(void) moveToStagePage:(int)page
{   
    ... 
    [NSThread detachNewThreadSelector:@selector(prepareTexture:) toTarget:self withObject:[NSNumber numberWithInt:page]];
    ...
}

below source is the code which preparing images.(Thread)

-(void) prepareTexture:(NSNumber*)number
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    int _page = [number intValue];
    NSLog(@"%d Thread start", _page);

    if(loadingTexNum != 0 && (_page + 1) != loadingTexNum)
    {
        [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromFile:[NSString stringWithFormat:@"Level%d.plist", loadingTexNum]];
        loadingTexNum = _page + 1;
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:@"Level%d.plist", loadingTexNum]];
    }

    if(loadingTexNum == 0 && (_page + 1) != loadingTexNum)
    {
        loadingTexNum = _page + 1;
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:@"Level%d.plist", loadingTexNum]];
    }

    [NSThread sleepForTimeInterval:10.0];
    NSLog(@"%d Thread release", _page);
    [pool release];
}

Solution

  • OpenGL doesn't support loading on multiple threads unless you pass the openGL context.

    Each thread in a Mac OS X process has a single current OpenGL rendering context. Every time your application calls an OpenGL function, OpenGL implicitly looks up the context associated with the current thread and modifies the state or objects associated with that context.

    OpenGL is not reentrant. If you modify the same context from multiple threads simultaneously, the results are unpredictable. Your application might crash or it might render improperly. If for some reason you decide to set more than one thread to target the same context, then you must synchronize threads by placing a mutex around all OpenGL calls to the context, such as gl* and CGL*. OpenGL commands that block—such as fence commands—do not synchronize threads.

    Source

    You can either use - (void) addImageAsync:(NSString *)filename target:(id) target selector:(SEL)selector; in the CCTextureCache class, calling this on your main thread.

    Or you can implement your own, effectively the same as addImageAsync.

    For your reference, this is how CCTextureCache achieves loading the images:

     NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
            
            // textures will be created on the main OpenGL context
            // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time
            // the lock is used for this purpose: issue #472
            [contextLock lock];
            if( auxEAGLcontext == nil ) {
                    auxEAGLcontext = [[EAGLContext alloc]
                                                               initWithAPI:kEAGLRenderingAPIOpenGLES1
                                                               sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]];
                    
                    if( ! auxEAGLcontext )
                            CCLOG(@"cocos2d: TextureCache: Could not create EAGL context");
            }
            
            if( [EAGLContext setCurrentContext:auxEAGLcontext] ) {
    
                    // load / create the texture
                    CCTexture2D *tex = [self addImage:async.data];
    
                    // The callback will be executed on the main thread
                    [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO];
                    
                    [EAGLContext setCurrentContext:nil];
            } else {
                    CCLOG(@"cocos2d: TetureCache: EAGLContext error");
            }
            [contextLock unlock];
            
            [autoreleasepool release];