about getting rid of glFlush()

ylaporte
Unregistered
 
Post: #1
Still no luck, I decided to start a new thread with this subject.

The goal is to find a way to draw using OpenGL without having to call glFlush() (as recommended by Apple).

I tried using a new PixelFormat:
NSOpenGLPixelFormatAttribute requestedAttrib[3];
NSOpenGLPixelFormat *pixelFormat;
long attr;
requestedAttrib[0]=NSOpenGLPFADoubleBuffer;
requestedAttrib[1]=NSOpenGLPFAAccelerated;
requestedAttrib[2]=nil;
pixelFormat=[[NSOpenGLPixelFormat alloc] initWithAttributes:requestedAttrib];
if(pixelFormat!=nil){
[view setPixelFormat:pixelFormat];
}else{
NSLog(@"No appropriate PixelFormat found!");
}

The pixel format is initialized correctly but I still have to call glFlush() to be able to see something. [[view openGLContext] flushBuffer]; doesn't make it appear... any other suggestions?
Quote this message in a reply
w_reade
Unregistered
 
Post: #2
If you were using AGL, you could do aglSwapBuffers() with a double-buffered pixel format - is there some equivalent method for an NSOpenGLView? You'll certainly need some specific command for swapping the buffers before you'll be able to see anything.

I thinkÖ
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #3
That's what [[view openGLContext] flushBuffer]; should do...

Here's the relevant code from my NSOpenGLView subclass:

Code:
@implementation STOpenGLView

static NSOpenGLPixelFormatAttribute STOpenGLAttributes[] =
{
    NSOpenGLPFADoubleBuffer,
    0
};

- (id)initWithFrame:(NSRect)frame
{
    NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:
        STOpenGLAttributes] autorelease];

    self = [super initWithFrame:frame pixelFormat:pixelFormat];
    if (self == nil)
    {
        STFatalError(NSLocalizedString(@"Can't create OpenGL context",
                                       @"GL context creation failure message"));
    }

    _lastFrameTime = 0;

    (void)[NSTimer scheduledTimerWithTimeInterval:0.001
                                           target:self
                                         selector:@selector(display)
                                         userInfo:nil
                                          repeats:YES];

    return self;
}

- (void)drawRect:(NSRect)rect
{
    glClearColor(1.0f, 0.0f, 1.0f, 0.0f);

    glClear(GL_COLOR_BUFFER_BIT);

    // drawing...

    [[self openGLContext] flushBuffer];
}

- (void)update
{
    [super update];
}

- (void)reshape
{
    [super reshape];

    NSRect bounds = [self bounds];

    glViewport(0, 0, bounds.size.width, bounds.size.height);
}

@end
Quote this message in a reply
ylaporte
Unregistered
 
Post: #4
Still no light. I looked at two Apple examples: the CocoaInitGL and NSTeapot snippets.

CocoaInitGL works as advertised (without glFlush()) but I can't to make my app work even if I reproduce the settings and even just place the demo code in my app.
NSTeapot on the other hand uses glFinish()... and if I replace it with [[self openGLContext] flushBuffer], it doesn't draw properly and doesn't animate at all. If I use glFlush(), it works...

I can make my stuff work with glFlush() but I want to get to the bottom of this... Or at least make sense out of it.
Quote this message in a reply
ylaporte
Unregistered
 
Post: #5
Aaaah ah!
The way I was changing my pixelFormat wasn't working...
And when I had put the code in
- (id)initWithFrame:(NSRect) it just wasn't being called... I just discovered this.

The reason it was never called is that I had placed an NSOpenGLView in IB and then chose a custom class. In that case, initWithFrame is never called (I guess it calls initWithFrame:(NSRect)pixelFormat:(NSOpenGLPixelFormat*) instead). But if I use a customView in IB and chose my NSOpenGLView subclass, initWithFrame is being called and it works perfectly.

So my conclusion so far is if you use a double buffered context, use flushBuffer:, if not, use glFlush().

Now... I am starting starting to wonder if to a buffered window using a double buffered context is a good idea...
I don't know if graphics from an OpenGL context are being buffered with the rest of the graphics from the window or not. But that's another question completely and for now, I'm going to bed. I think I'll watch the WWDC sessions about Quartz extreme, I feel like I need them just now.

As usual thanks everyone (in particular OneSadCookie) for suffering my experiments.
Quote this message in a reply
Member
Posts: 104
Joined: 2002.04
Post: #6
Actually, when you place it in IB, it gets called by -(id) initWithCoder. Also, realize that your context is not really initialized in any of the init methods, NSOpenGLView does lazy initialization and waits until it needs to have the context.

So, any initialization that you want to do should be done in your drawRect using a flag. There are a couple of examples here that show how that works.
Quote this message in a reply
ylaporte
Unregistered
 
Post: #7
I am sure that initWithFrame:(NSRect) gets called at runtime when using a customView. I checked this by tracing. I do know that IB initializes and configures all the interface elements and only "freeze dries" them when you save the bundle.

However, it still get's called. I wonder if somehow the process is different when you use a customView... Or if NSView's initWithCoder calls initWithFrame: even tough initWithCoder doesn't have to call the deignated initializer.

As for the init code, can't awakeFromNib do the trick?

Note that in my case it doesn't matters since the graphic code is in different object and initialization is all done at instantiation.

I should test these things but since I start a new job on monday, I am busy finishing everything I can and writing reference documents for whoever will replace me.
Quote this message in a reply
Member
Posts: 104
Joined: 2002.04
Post: #8
Unfortunately, awakeFromNib gets called too early for OpenGL views. You are only guaranteed that the context will be valid the first run through drawRect.
Quote this message in a reply
Feanor
Unregistered
 
Post: #9
GoodDoug, I have to strongly disagree with that philosophy.

That's a goofy hack, resulting from an overuse of Interface Builder. Create your openglview object programmatically in your controller's awake from nib, and then set it to be your window's content view, or make it a subview of the content view. Bryan Blackburn shows how to do this nice and cleanly. If you don't have a controller, then you should make one. People put a lot of stuff into their view which just makes a mess. The view is just a rectangle that holds content. The view doesn't need to produce or manage any of that content.

If you have other interface elements in your window and you want to make it easy to graphically set where your opengl view is going to be in relation to them, just put a vanilla NSView there to remember the frame for you, and then swap it for one of your custom opengl views.

This way, YOU call your view's init method, from whence it happily builds and sends a pixelFormat to super (if you subclass NSOpenGLView), or builds its own format. Or you can build a format and set it yourself. Moreover, you can even do your drawing elsewhere if you remember to call [lockFocusIfCanDraw] on the context. Apple have made this much too complicated with their bad examples.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #10
Why not do your setup in NSOpenGLView's reshape method (just override it in your subclass)?

Is this new for Jaguar? It doesn't seem to say so...
Quote this message in a reply
ZekeMan
Unregistered
 
Post: #11
Here is the relevant documentation on this subject from the NSOpenGL_Class_Ref manual. I think after reading it you will see that flushBuffer only works with double buffered contexts. If the context is not double buffered, flushBuffer does nothing. In that case you would want to use glFlush. Also, don't use glFlush with flushBuffer as it will reduce performance. flushBuffer calls glFlush for you.


flushBuffer

- (void)flushBuffer

Copies the back buffer to the front buffer of the receiver. If the receiver is not a double buffered context, this call does nothing.

If the pixel formatís backing store attribute is false, the buffers may be exchanged rather than copied. This is often the case in full-screen mode.

According to the swap interval context attribute (see the table in the ìConstantsî (page 12) section), the copy may take place during the vertical retrace of the monitor, rather than immediately after flushBuffer is called. An implicit glFlush is done by flushBuffer before it returns. For optimal performance, an application should not call glFlush immediately before calling flushBuffer. Subsequent OpenGL commands can be issued immediately after calling
flushBuffer, but are not executed until the buffer copy is completed.
Quote this message in a reply
Feanor
Unregistered
 
Post: #12
Quote:Originally posted by OneSadCookie
Why not do your setup in NSOpenGLView's reshape method (just override it in your subclass)?

Is this new for Jaguar? It doesn't seem to say so...

Here is a very late response! Smile

Well I've discovered that if you call [[self openGLContext] setView:self] or equivalent within -reshape, you will get a mutual recursion problem, because setView: calls reshape! You can do the usual trick of having a flag so that setView: is only called once, of course -- you'd want to -- but you have to set the flag *before* calling setView:. Anyway I usually only use -reshape to fix the aspect ration and update the context. The only init stuff you ever need to do outside of --initWithFrame: or -awakeFromNib: (if you put the view in the nib) is setting the context's view.

But I've discovered something else, too. Something which I discovered quite a while ago, in fact, but never mentioned, since I've been a proponent of not using NSOpenGLView (because it does things like the following that a normal subclass of NSView cannot, and also just because it's educational to not use "convenience classes" -- if that's really all that they are).

If you call [self openGLContext] in the -initWithFrame: method of your subclass of NSOpenGLView, you do not have to initialize the context's view at all.(!) The machinery does it for you (maybe in the private _surfaceNeedsUpdate: method). This is convenient, certainly, and now I understand why they created the class in the first place, and I'll probably go back to using it.

But for anybody who does
Code:
-drawRect:(NSRect)frameRect {

    static BOOL firstTime = YES;

    if( firstTime == YES ) {
        [[self openGLContext] setView:self];
        firstTime = NO;
    }
    ...
}

*and* is subclassing NSOpenGLView -- you don't need to do that anymore. Just do

Code:
-initWithFrame:(NSRect)frameRect {

    NSOpenGLPixelFormat *pf;
    // create PFA list and init a pixel format
    ...
    [super initWithFrame:frameRect pixelFormat:pf];
    [[self openGLContext] makeCurrentContext];

    // rest of init ...
    return self;
}

Just a pet pattern of mine.
--FÎanor

UPDATE: Well, I should have done more investigative work... While what I wrote is accurate, the situation isn't all that mysterious. NSOpenGLView just calls [_openGLContext setView:self] in its implementation of LockFocus, probably using some flag to test for a new context.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Why do I need to glFlush? gazliddon 3 5,339 Oct 21, 2009 11:37 PM
Last Post: gazliddon