1

In my game I'm using such method for loading sprites asynchronously:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 [physicsSprite loadSprite];
dispatch_async(dispatch_get_main_queue(), ^{
                                    [physicsSprite createAnimation];
                                    [physicsSprite createBodyInWorld:world;
                                });
                            });

physicsSprite it's just node that holds CCSprite child. In method loadSprite - I do just self creating sprite by

physicsSprite.sprite = [CCSprite spriteWithSpriteFrameName:@"bla.png"];

And the method

[physicsSprite createAnimation];

using for adding loaded sprite to mainLayer node.

All this logic works well. However I'm thinking if I'm doing something wrong, because I'm NOT creating OpenGL context

So, I've tried my code with context:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            CCGLView *view = (CCGLView*)[[CCDirector sharedDirector] view];
                            EAGLContext *auxGLcontext = [[[EAGLContext alloc]
                                                          initWithAPI:kEAGLRenderingAPIOpenGLES2
                                                          sharegroup:[[view context] sharegroup]] autorelease];
                            if( [EAGLContext setCurrentContext:auxGLcontext] ) {

                                [physicsSprite loadSprite];
                                glFlush();
                                [EAGLContext setCurrentContext:nil];
                            }
                            dispatch_async(dispatch_get_main_queue(), ^{
                                [physicsSprite createAnimation];
                                [physicsSprite createBodyInWorld:world];
                            });
                        });

And as result I don't see any difference in gameplay.

But as I know, I need to create context for any sprite that loaded in another thread. So, is what I'm doing wrong? Without creating context...

KAMIKAZE
  • 420
  • 6
  • 27

2 Answers2

2

Cocos2d is not thread-safe, and any code that changes the OpenGL state or uses the OpenGL context must run on the main thread (same thread as OpenGL context). Attempts at using dispatch_async to load textures (sprites) will be futile.

Most definitely you should not create your own EAGLContext and don't create the cocos2d view in a dispatched block either. This has to be handled by cocos2d internally.

However CCTextureCache and other classes have methods to load textures and sprite frames asynchronously, for instance:

CCTextureCache* texCache = [CCTextureCache sharedTextureCache];
[texCache addImageAsync:image target:self selector:@selector(didLoadTexture:)];

This then calls the selector when the texture is available:

-(void) didLoadTexture:(CCTexture2D*)texture
{
}

You can use the selector to initialize the sprite using the texture. Because if you do so earlier, cocos2d will attempt to load the texture right away using normal, synchronous methods. If this happens at the same time as asynchronous loading it may even crash.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • I'm using **addImageAsync** at loading screen. So, textures already loaded into memory. However sometimes some of these textures unloaded, because game receive memory warning. I have 3 very big textures, around 3500x4000px and maybe 2 smaller textures 1000x1500px. And when game running it's loads all them at loading screen, however later, after memory warning it unload some of them. Anyway even in this situation I got all working well.. So, I don't understand if I need context in my code. – KAMIKAZE May 22 '14 at 12:13
  • 1
    You are aware that texture sizes larger than 2048x2048 exclude your app from running on iPad 1 and iPhone 4 and earlier? And that each of your 3500x4000 (times 32 bits per pixel) consumes about 53 Megabytes? Your best option is to reduce color depth to 16 bit to half memory usage, or use .pvr.ccz to make them load super-fast and use a lot less memory. TexturePacker can create .pvr.ccz textures. – CodeSmile May 22 '14 at 12:27
  • Yeah, it's nuts (and largely pointless) to use textures this large. – ipmcc May 22 '14 at 12:31
  • Yes, for sure, min iOS 7, so only iPhone 4 is here... and yes, 53mb, but what I can do, I need so beautiful graphics in my game :) And of course I'm using pvr.ccz. but anyway sometimes my game get lags.. such as small hangs.. – KAMIKAZE May 22 '14 at 12:33
  • I've checked some other formats, and 5551 looks good for most images.. maybe I need to try it.. – KAMIKAZE May 22 '14 at 12:46
  • For images without transparent areas you can also use RGB565. – CodeSmile May 22 '14 at 12:49
  • Yes, 565 good too and already used.. But main thing - do I need context or no? :) – KAMIKAZE May 22 '14 at 12:57
  • No, definitely no. Cocos2d creates and owns the EAGL context, and there should only be one on iOS. If for some reason you need a context, just get the current one. You have to get it from the main thread, using https://developer.apple.com/Library/ios/documentation/OpenGLES/Reference/EAGLContext_ClassRef/Reference/EAGLContext.html#//apple_ref/occ/clm/EAGLContext/currentContext – CodeSmile May 22 '14 at 13:30
  • By creating, I mean to use my code with context... in question I post it. – KAMIKAZE May 22 '14 at 13:49
0

FRom CCTextureCache:addImageAsync:

if( [EAGLContext setCurrentContext:_auxGLcontext] ) {

    // load / create the texture
    texture = [self addImage:path];

    glFlush();

    // callback should be executed in cocos2d thread
    [target performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:texture waitUntilDone:NO];

    [EAGLContext setCurrentContext:nil];
}

So, If take code as example: and I need to use code with context. Just checked this. When texture unload from memory and I try to load it using just loadSprite method - it first loads texture file pvr.ccz(because it's not loaded to memory) and then creates sprite from texture, but without context it will be just black square instead of image. And the reason why I didn't get such problem - I've tested on iPad 4 :p, it has enough memory. Checking this of iPad 3 or iPhone4\4s give immediately results.

p.s.: funny to answer own questions.

KAMIKAZE
  • 420
  • 6
  • 27