3

I'm creating and loading a lot of textures (made of strings). To keep the animation running smoothly, I offload the work to a separate worker thread. It seems to work more or less exactly the way I want, but on older devices (iPhone 3GS) I sometimes notice a long (1 sec) lag. It only occurs sometimes. Now I'm wondering if I'm doing this correctly or if there is any conceptual issue. I paste the source code below.

I should also mention that I do not want to use the GLKit TextureLoader because I also want to offload the texture generating work to the other thread, not just the loading part.

In case you're wondering what I need these textures for, have a look at this video: http://youtu.be/U03p4ZhLjvY?hd=1

NSLock*                     _textureLock;
NSMutableDictionary*        _texturesWithString;
NSMutableArray*             _texturesWithStringLoading;

// This is called when I request a new texture from the drawing routine. 
// If this function returns 0, it means the texture is not ready and Im not displaying it.

-(unsigned int)getTextureWithString:(NSString*)string {
    Texture2D* _texture = [_texturesWithString objectForKey:string];
    if (_texture==nil){
        if (![_texturesWithStringLoading containsObject:string]){
            [_texturesWithStringLoading addObject:string];
            NSDictionary* dic = [[NSDictionary alloc] initWithObjectsAndKeys:string,@"string", nil];
            NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadTextureWithDictionary:)object:dic];
            thread.threadPriority = 0.01;
            [thread start];
            [thread release];
        }
        return 0;
    }
    return _texture.name;
}

// This is executed on a separate worker thread.
// The lock makes sure that there are not hundreds of separate threads all creating a texture at the same time and therefore slowing everything down. 
// There must be a smarter way of doing that. Please let me know if you know how! ;-)
-(void)loadTextureWithOptions:(NSDictionary*)_dic{
    [_textureLock lock];
    EAGLContext* context = [[SharegroupManager defaultSharegroup] getNewContext];
    [EAGLContext setCurrentContext: context];

    NSString* string = [_dic objectForKey:@"string"];
    Texture2D* _texture = [[Texture2D alloc] initWithStringModified:string];

    if (_texture!=nil) {
        NSDictionary* _newdic = [[NSDictionary alloc] initWithObjectsAndKeys:_texture,@"texture",string,@"string", nil];
        [self performSelectorOnMainThread:@selector(doneLoadingTexture:) withObject:_newdic waitUntilDone:NO];
        [_newdic release];
        [_texture release];
    }
    [EAGLContext setCurrentContext: nil];
    [context release];
    [_textureLock unlock];
}

// This callback is executed on the main thread and marks adds the texture to the texture cache. 
-(void)doneLoadingTextureWithDictionary:(NSDictionary*)_dic{
    [_texturesWithString setValue:[_dic objectForKey:@"texture"] forKey:[_dic objectForKey:@"string"]];
    [_texturesWithStringLoading removeObject:[_dic objectForKey:@"string"]];
}
hanno
  • 6,401
  • 8
  • 48
  • 80
  • As usual, shortly after posting a question here, I get it to work. I now use a NSOperationQueue rather than created too many NSThreads. That allows me to set 'maxConcurrentOperationCount' which seems to solve the problem. I'd still love to hear if I'm doing anything else that looks stupid to you. – hanno Jul 07 '12 at 00:50
  • 1
    If it's a resource limitation issue, you might also look into dispatch semaphores: http://www.mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html . I commonly use these for items like this where I/O speed might be more of a limitation than processing speed. – Brad Larson Jul 08 '12 at 17:48
  • Thanks. Something else: getting and setting a new context from the sharegroup takes a lot of time according to instruments. Should I only create one of extra context and reuse it among threads while making sure I only access it from one thread at a time? I remember reading somewhere that I shouldn't do that by can't remember why. – hanno Jul 08 '12 at 19:01
  • 1
    You only have to worry about simultaneous access to a context on more than one thread. As long as you're setting the context to be active on a given thread (using EAGLContext's `-setCurrentContext:`) and guaranteeing only one access at a time (using a single-wide dispatch queue, for example), it's perfectly safe to hand it off between different threads. – Brad Larson Jul 08 '12 at 19:04
  • 1
    If you have an answer to your question, please create an answer and accept it. Otherwise this question stays in the "unanswered" pile. Cheers! – Jeff Wolski Jul 13 '12 at 14:31

1 Answers1

0

The problem was that too many threads were started at the same time. Now I am using a NSOperationQueue rather than NSThreads. That allows me to set maxConcurrentOperationCount and only run one extra background thread that does the texture loading.

hanno
  • 6,401
  • 8
  • 48
  • 80