3

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];
}
Guru
  • 21,652
  • 10
  • 63
  • 102
Bright Lee
  • 2,306
  • 2
  • 27
  • 55

3 Answers3

1

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];
Community
  • 1
  • 1
James Webster
  • 31,873
  • 11
  • 70
  • 114
0

I believe what is happening is that the main thread tries to use the images before the prepareTexture thread has had a chance to load the textures.

If you immediately try to create sprites with the new textures, for example right in the moveToStagePage method, then that is going to fail. Your threaded texture loading method will need to flag to the other thread that it has completed loading the textures. The easiest way is to simply toggle a BOOL variable. Only when the texture loading thread signaled that the textures have been loaded should you try to create sprites etc. using these textures.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • No, moveToStagePage also prepare just next step. I checked out what you are saying. but It's not true. I waited enough until textures are loaded. but Thanks – Bright Lee Oct 31 '11 at 23:00
  • You helped me a lot in many different question. I appreciate. – Bright Lee Oct 31 '11 at 23:03
  • @LearnCocos2D Though you make a valid point that the asset may not be ready, the limitation is down to OpenGL contexts and that they can't be multithreaded without shifting the context around. Have a look at the implementation of `[CCTextureCache addImageWithAsyncObject:]` – James Webster Nov 01 '11 at 00:39
0

When loading textures on a separate thread you need to use [[CCTextureCache sharedTextureCache] addImageAsync:texture target:target selector:@selector(onTextureLoaded)]; (else the `GLContext gets messed up).

jrtc27
  • 8,496
  • 3
  • 36
  • 68