In an attempt to create a loading bar for an iPhone game I'm developing (using Cocos2D), I wanted to use a multithreaded approach.
One thread shows a loading screen and runs the main application event loop while a new thread silently loads all the Sprites in the background (through spriteWithFile) and then adds them to a layer.
I create the new thread using NSThread's detachNewThreadSelector method (which sends updates of the loading status to the main thread via performSelectorOnMainThread).
The problem I'm facing is that any OpenGL calls (such as those found within the spriteWithFile method) in the new thread die with a BUS ERROR or memory access error of some sort. I'm assuming this is because both threads are attempting to make OpenGL calls at the same time or the new thread is unaware of the OpenGL context.
What has to be done to allow multiple threads to make OpenGL calls on the iPhone using Cocos2D-iPhone.
For the record, the new thread needs to execute the following two lines to be able to use the OpenGL API from a concurrent thread:
EAGLContext *k_context = [[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:[[[[Director sharedDirector] openGLView] context] sharegroup]] autorelease];
[EAGLContext setCurrentContext:k_context];
This is now made obsolete by the addImageAsync method provided by the TextureMgr class in Cocos2D 0.8.x onwards that does asynchronous texture loading for you.
I want to do this too.
I'm starting from this thread.
PS: This answer is very old, now I'm not sure that asynchronous texture loading is as useful as it once was since iOS5 added "free" texture uploads via CVOpenGLESTextureCaches. Sure you still can (& should) load your assets in a secondary thread, but giving that thread an EAGLContext doesn't seem as necessary now.
Apple has some good guidelines for multithreaded OpenGL here.
Cocos2d best practices recommend against using NSTimer and I assume the same applies to threads as well. You should probably use Cocos' Timer object. This will leave the thread management to Cocos and should also let you access the correct graphics context.
HTH.
Related
Core animations run on their own threads. But is animationDidStop:finished: guaranteed to run on the main thread when the animation finishes? It does in my testing, but I don't know whether I can depend on that at all times and across all versions of iOS.
It does occurs on the main thread as mentioned also in this thread. Starting iOS 4.0 and later consider using the block-based animation methods though.
EDIT
From the official doc for the description of the animateWithDuration:animations:completion: it is mentioned that the completion block is performed at the beginning of the next run loop cycle. I believe we are talking about the loop cycle associated to the main thread here if you are invoking your method on the main thread.
It's my understanding that yes, the animationDidStop:finished: method is always called on the main thread, but that's an understanding, not a guaranteed.
You might want to post this question to the Core Animation area in Apple's developer forum with a specific request that an Apple engineer weigh in. There are a number of Apple employees who post their regularly. (Rincewind comes to mind for Core Animation).
I'm rendering OpenGL Context on a background thread with a different EAGLContext than the main thread.
I use something like this:
- (void)renderInBackground {
EAGLContext *context = [[EAGLContext] alloc] init];
[EAGLContext setCurrentContext:context];
Rendering..
}
However, even though this is performed in a background thread, when using a heavy shader, the main thread gets blocked and the UI gets stuck.
Why is the background thread blocking the main thread?
the methods are not synchronized.
You do have finite CPU and GPU resources to perform all of the rendering and UI interactions, so if you max out the GPU you will slow down everything else in your application.
That said, you should be able to render on a background thread without completely halting all UI elements. I do some fairly intense rendering in my open source Molecules application, all of it using a background GCD queue, yet you can still scroll in popovers and otherwise interact with the interface.
I describe the process I use in this answer, but the basic setup is a single-wide GCD queue that relies on a dispatch semaphore to prevent the enqueueing of additional rendering frames while one is still being processed. Threads and blocks have some overhead to them, so if they are being fired off faster than they can be processed, this can lead to resource exhaustion. The semaphore prevents this.
Wrapping all interactions with my OpenGL ES context in this queue provides lock-free usage of this shared resource, and I see a significant performance boost over simply running this all on the main thread on the multicore devices. As I said, I'm still able to interact with the UI during even heavy rendering here.
I am using CATiledLayer to render NSManagedObjects.
But you know, CATiledLayer render objects in background threads. This make my app crash on iOS5
I know that I should use separated NSManagedContext for each of threads but this way make performance get bad. (because I have to save the NSManagedContext more often to transfer data to other threads).
Do you guys know the better way to work around my problem? Please help!!!
Sorry for my poor English!
NSManagedObjectContext is not thread safe, nor are NSMangedObjects. You should create a MOC on the background thread, pass in any IDs (which ARE thread safe), and load them on the background thread context.
UPDATE:
One alternative is to create plain old obj-c objects, or even just a regular NSDictionay, which contains the necessary data and pass those to the background thread. So after your MO is populated, create a POOCO, copy in the necessary data, and pass that to your background thread for processing. This will avoid disk access.
I created EAGLContext and use it in the secondary thread. But there is no output displayed. The same code works fine if run in main loop.
Do I need to notify somewhere on each render completion?
EGL Context may be only active in one thread. You need to use eglMakeCurrent(). Also, you need to use eglSwapBuffers() once your rendering is done.
I know that drawLayer: and drawlayer:inContext: are called on multiple threads when using a CATiledlayer, but what about drawRect:?
Apple's PhotoScroller example code uses drawRect: to get its images from disk, and it has no special code for handling threads.
I am trying to determine whether my model for a CATiledLayer must be thread-safe.
I have found CATiledLayer is using multiple background threads in the iOS Simulator, but a single background thread on my iPhone.
My Mac has a dual core processor, while my iPhone has a single core (A4).
I suspect an iOS device with an A5 CPU will also use multiple threads.
Yes, drawRect can and will be called on multiple threads (tested on OS 4.2).
This behaviour is less obvious if your drawing is fast enough to outpace the arrival of new zoom gestures so your app may work fine until tested with rapid input of zoom gestures.
One alternative is to make your model thread-safe.
If thread-safety is achieved by synchronizing most of the access to the data model to one drawing thread at a time then then you might do just as well to mutex the body of drawRect with something like #syncrhonize(self) which seems to work.
I haven't found a way to request that CATiledLayer only uses one background thread.
Have you seen this technical Q&A from Apple?
It doesn't answer your question directly, but it could help you decide how to implement your model.