display link to drive opengl loop?

Apprentice
Posts: 15
Joined: 2009.01
Post: #1
I tried to follow the guidelines here: http://developer.apple.com/qa/qa2004/qa1385.html

I keep getting errors using it, I believe related to threading, I put locks around everything anytime I draw, but after the program loads for a few seconds it will run, then gdb will be called up with four threads, one will be mach_msg_trap, two in _semwait_signal, I believe these are just normal threads and the fourth will be stuck on whatever happens to be the first gl function, which will give me a exc_bad_access. This program works fine if I just call the draw function normally.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #2
Did you try it without the locks?
Quote this message in a reply
Apprentice
Posts: 15
Joined: 2009.01
Post: #3
I did try it without the locks before I read the second guideline that mentioned that you needed to lock opengl, it was crashing then also.

I actually stopped it from crashing, when I switched over to the display link function I hadn't properly set my context, now I have it set right but it's just drawing a white screen... I think maybe it's the wrong context.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #4
alloca Wrote:I did try it without the locks before I read the second guideline that mentioned that you needed to lock opengl, it was crashing then also.

I guess I recall reading dev docs somewhere that OpenGL is not thread safe, but you should know that it *is* thread-safe nowadays, just not the contexts. So you should do something like:

Code:
NSOpenGLContext        *currentContext;

currentContext = [self openGLContext];
[currentContext makeCurrentContext];

CGLLockContext([currentContext CGLContextObj]);

[self drawMyGLFrame];

[currentContext flushBuffer];

CGLUnlockContext([currentContext CGLContextObj]);

Last I recall, that was all the locking I needed for a display link.

Yeah, if it's white after that, I'm stumped without more code to look at.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #5
Umm, the contexts are the main thing you are dealing with, so, no, OpenGL never was, and isn't, thread safe. You need to manually lock it if you want to draw to a context from multiple threads.

However, if you only ever touch the context from the display link callback, there's no need to lock it, you just need to make it current.

The fact that it doesn't display what you want doesn't say much, it could be any number of: forgetting to set/clear state, wrong color, wrong matrices, no buffer swap, etc.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #6
DoG Wrote:Umm, the contexts are the main thing you are dealing with, so, no, OpenGL never was, and isn't, thread safe. You need to manually lock it if you want to draw to a context from multiple threads.

Lest you think I'm completely nuts, here's a quote from OSC:

Quote:Jake: OpenGL contexts are not thread-safe, but OpenGL is. OpenGL contexts are current per-thread. It's perfectly possible to call GL from a thread other than the main one; just make sure that your context is current in that secondary thread, and that no other thread touches the context simultaneously.

I guess you can take that however you want. To me it means OpenGL is thread safe in the sense that you can access it from multiple threads, but you can't access it simultaneously from multiple threads.

DoG Wrote:However, if you only ever touch the context from the display link callback, there's no need to lock it, you just need to make it current.

I just tested this twice. First time it crashed. Second time it froze my machine and I had to restart (and type this in twice Mad ). However, if I CGLLockContext in the display link callback it works just peachy. I guess you can take that however you want too. My take is that it's a good idea to CGLLockContext, and it certainly won't hurt anything to do so anyway.
Quote this message in a reply
Apprentice
Posts: 15
Joined: 2009.01
Post: #7
I found Dave Dribin's sample code here: http://www.dribin.org/dave/blog/archives...engl_view/

That helped me figure out what was going on and it's all working now, man this stuff is confusing!
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #8
alloca Wrote:... man this stuff is confusing!

It can get worse too, depending on how many little details you want to handle, and on how many versions back of the OS you want to support. There are things like: if you want a resize control you have to draw it yourself. Then there's handling keyboard input. Mouse input. Capture mouse/hide mouse in windowed mode. Do you want to allow the user to choose screen resolutions? How about Wacom mouse compatibility. Oh, and then it gets even worse trying to figure out why the windowing system fights you for coordinates with multiple monitors. And much much more...

Yah, it's confusing. iPhone is much easier.

Have you considered SDL lately? Smile
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #9
AnotherJake Wrote:I guess you can take that however you want. To me it means OpenGL is thread safe in the sense that you can access it from multiple threads, but you can't access it simultaneously from multiple threads.


I just tested this twice. First time it crashed. Second time it froze my machine and I had to restart (and type this in twice Mad ). However, if I CGLLockContext in the display link callback it works just peachy. I guess you can take that however you want too. My take is that it's a good idea to CGLLockContext, and it certainly won't hurt anything to do so anyway.

Well, that sounds like a bug, or your app is doing something with the context from another thread, too. OpenGL Profiler should be able to tell you.

And as for thread safety, it really does depend on your definition. However, I am not sure you would say, for example, NSArray is thread safe. You can create multiple NSArray instances, and manipulate them on different threads, but you can't manipulate the same instance from multiple threads without locking. So, saying OpenGL is thread safe, but not really, isn't helping, without further... context.

Alas, it does indeed get more complicated, and the docs aren't helping, as evidenced by http://www.mikeash.com/?page=pyblog/frid...01-09.html
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #10
That's a *great* link on the subject!

Quote:... So, saying OpenGL is thread safe, but not really, isn't helping, without further... context.

Hehe, context, I get it. Rasp

Yes, I have to say I agree with you about saying it is thread safe but not thread safe. Definitely need to make a point of being more clear about it I think.

And as to whether my test was indicating a bug or not, I don't know, but I do know I have no other threads spawned. I should probably investigate, but I don't use display link, so there is little incentive.
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #11
I'm not a multithreading expert by any means, but I have two observations which might be useful:
  • The big problem with thread safety is shared data access. In OpenGL, one piece of critical shared data is the command buffer associated with the context. The command buffer is hidden to you, the application writer, but every time you call an API (glDrawArrays, glTexImage etc) you're pushing commands through your context into a command buffer. The details of this are highly driver-specific, but the key point is that commands are encoded serially in the order they were executed.

    If you use the same context from multiple threads without locking, sooner or later, you're going to be half-way through writing a command into that buffer when the OS switches you to another thread. If that thread then writes a command to the same context, the result is a scrambled (interleaved) command buffer, which the GPU won't be able to process correctly, and your app dies.

    This is why you have to guarantee exclusive access to a context from only one thread at a time.
  • In a typical Mac app, a lot of things happen on the main thread, like the event loop. If you have an NSOpenGLView with the regular drawRect: machinery, a critical point is that even if you set up your timer and drawing to happen on a second thread, drawRect: is still going to periodically be called from the main thread. This happens any time the app needs to respond to an OS event, like minimizing, moving, or resizing the window. So the default mechanism, out of the box, is not thread-safe. You have to add locks, and finding every NSOpenGLView method that can be invoked via some superclass update is not obvious or well documented.

    My point here is that it's possible for you to put your drawing into a second thread and have it appear to work perfectly. But if you sit there in a loop, minimizing the window over and over, or toggling between fullscreen and a window, you're likely to expose a hole where you forgot to lock. This is what makes multithreaded programming hard-- the errors are timing sensitive and hard to reproduce.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #12
I do not have much knowledge of multi-threaded programming myself, but I've done it in a few of my own projects successfully (so far). Yes, you are right about holes in the multi-threaded scheme, having to do with the multitude of Cocoa operations being done solely on the main thread. The only big ones I've encountered myself are, as you already mentioned:

reshape
prepareToMiniaturize
drawRect

Moving the window hasn't turned out to be a problem for me yet, and neither has toggling in-out of full screen, although I seem to recall stumbling upon countless little issues with the OS which had nothing to do with being threaded, including numerous issues with multi-display setups.

I also had [a multi-thread] issue during image loading, but that was by way of my own design.

For sure, the hardest part in identifying thread problems is that many (most?) times they are simply impossible to know where they came from. Essentially, in most cases, the debugger is virtually worthless. The way I've approached it is by testing as thoroughly as possible as I've built things up. If all of the sudden something doesn't work for some unknown reason, the prime suspect is always threads. Back up one or two dev steps and reason it out. The other thing I do is I make it so I can optionally enable/disable multi-threaded operation on the fly, so if a new bug comes up just turn off multi-threaded mode and see if it happens like that too. If not, it is likely a thread conflict. It's a little extra work to implement it this way, but I find it more than makes up for itself in bug diagnostics.

Apple suggests a few different models for approaches one might take in a multi-threaded OpenGL design (sorry I don't have a link handy). I chose the one that seemed most simple to me: I usually have all my GL access confined to one thread, so it is guaranteed to be serialized. And then I have all my update and physics on its own thread. This has worked really well for me and has allowed me to keep the need for any locks to a bare minimum.

As I've said before, multi-threading can offer nice performance improvements for some situations, and it works. It's also not nearly as hard to do as I had been led to believe over the previous years, so I encourage others to give it a shot... But I would highly warn against it if you don't have at least a reasonable amount of programming experience, and especially not if you haven't done a fair amount of OpenGL development.
Quote this message in a reply
Apprentice
Posts: 15
Joined: 2009.01
Post: #13
Okay so I'm getting it to draw well and my game is running and everythings fine and dandy, but i'm getting tearing and some weird graphical glitches occasionally, just with drawing a grid of textured tiles, 8x12 tiles on screen, each 128x128.

With a display link timer and a drawing method that takes significantly less than 1/60th of a second, I shouldn't be getting tears right?
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #14
No you shouldn't be getting tearing with display link. Dribin's project has regular NSTimer code in it too, are you sure you aren't using that by accident? If you do use an NSTimer, please change the firing rate from 60 Hz to 1000 Hz and turn on VBL synch for best performance.
Quote this message in a reply
Apprentice
Posts: 15
Joined: 2009.01
Post: #15
No I'm still using a display link, it's weird though because sometimes it's tearing and sometimes the white part of the texture turns slightly tinted red.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Error trying to link an OpenGL app memon 4 3,706 Mar 19, 2007 12:33 PM
Last Post: memon
  OpenGL Program / Loop Help Jones 5 2,983 Jan 4, 2006 08:36 PM
Last Post: Jones
  Texturing with OpenGL & Cocoa (link) Taxxodium 0 2,447 Dec 2, 2004 06:31 AM
Last Post: Taxxodium
  OpenGL profiler & display lists MattDiamond 9 4,282 Sep 1, 2003 08:07 PM
Last Post: MattDiamond