Double Buffering in Cocoa OpenGL

Discussion in 'Mac Programming' started by hoschi, Sep 22, 2005.

  1. hoschi macrumors newbie

    Joined:
    Sep 20, 2005
    #1
    I have been using this standard code for single buffered project in Cocoa.

    -(void)drawRect:(NSRect)rect
    {
    glClearColor( 0, 0, 0, 0 ) ;
    glClear( GL_COLOR_BUFFER_BIT ) ;
    glColor3f( 0.0f, 1.0f, 0.0f ) ;
    glBegin( GL_TRIANGLES) ;
    {
    glVertex3f( ver1x, ver1y, 0.0 ) ;
    glVertex3f( ver2x, ver2y, 0.0 ) ;
    glVertex3f( ver3x , ver3y, 0.0 ) ;
    }
    glEnd() ;
    glFlush();


    -----
    My question is how does this get modified in the case of a double buffered opengl program. Also is the call to drawRect ie [self setNeedsDisplay: YES] changed or does it remain the same..

    Thanks in Advance,
    Vyom
     
  2. gekko513 macrumors 603

    gekko513

    Joined:
    Oct 16, 2003
    #2
    I would expect double buffering using OpenGL in Cocoa works the same as double buffering using OpenGL elsewhere. I'm not exactly sure how it is done, but you should be able to find some tutorials on that if you google for it.

    That said, I have done a couple of Cocoa / OpenGL projects and in my case double buffering has been a non-issue since Cocoa (Aqua?) uses double buffering in itself. I think double buffering in OpenGL would make for an effective triple buffering that would degrade performance unless you can turn off the default buffering. I haven't done full screen OpenGL projects, so the situation may be different in that case.

    [self setNeedsDisplay:YES] can be a bit slow if you're doing OpenGL animations. I have found that it can be faster to use something like:
    [myView lockFocus];
    [myView drawRect:dirtyRect];
    [myView unlockFocus];
     
  3. hoschi thread starter macrumors newbie

    Joined:
    Sep 20, 2005
    #3
    Fast Looping

    In my case I use a "for" loop to reconstruct a triangle and then call [self setNeedsDisplay:YES] to call the drawRect back. The looping is quite fast - like 10000 steps in 10 seconds- but I only see the first and the last states... Even though I have just now modified my program to below, I do not see any intermidiate states...

    -(void)drawRect:(NSRect)rect
    {
    glClearColor( 0, 0, 0, 0 ) ;
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
    glColor3f( 0.0f, 1.0f, 0.0f ) ;
    glBegin( GL_TRIANGLES) ;
    {
    glVertex3f( ver1x, ver1y, 0.0 ) ;
    glVertex3f( ver2x, ver2y, 0.0 ) ;
    glVertex3f( ver3x , ver3y, 0.0 ) ;
    }
    glEnd() ;
    glFinish();
    [[self openGLContext] flushBuffer] ;
    }

    ANy help would be highly appreciated
     
  4. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #4
    I think you need to make a custom pixel format in your view's init: method, like this:
    Code:
    - (id)initWithFrame:(NSRect)aRect {
    	NSOpenGLPixelFormatAttribute attr[] = {
            NSOpenGLPFADoubleBuffer,
            0
    	};
    	
        // create pixel format
        NSOpenGLPixelFormat *nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
    
        // create the context...
        if (!(self = [super initWithFrame:aRect pixelFormat:nsglFormat])) {
    		return nil;
    	}
    	
        // make the context current
        [[self openGLContext] makeCurrentContext];
    
    	// enable vertical sychronization to refresh rate
    	const long vals = 0x01;
    	[[self openGLContext] setValues:&vals forParameter:NSOpenGLCPSwapInterval];
    
    	// rest of your init method ...
    }
    Then in your drawRect method:
    Code:
    - (void)drawRect:(NSRect) aRect {
    	// OpenGL code here...
    
    	glFlush();
    	[[self openGLContext] flushBuffer];
    }
    But yeah, I haven't really noticed any tearing simply using glFlush() and no custom pixel format recently, so I'm not so sure they haven't changed it in Tiger so that double-buffering happens by default in an OpenGL view (that would kind of make more sense to me, seeing as how everything else is double-buffered by default in Cocoa).
     
  5. gekko513 macrumors 603

    gekko513

    Joined:
    Oct 16, 2003
    #5
    I'm guessing that the problem is with the [self setNeedsDisplay:YES]. [self setNeedsDisplay:YES] just marks the view as needing do be drawn some time soon, it does not actually draw anything. Your program could mark the view as needed to be drawn 10000 times in the loop, then the loop ends and not until then is the drawing actually done.

    Try to replace [self setNeedsDisplay:YES] with
    [self lockFocus];
    [self drawRect:[self frame]];
    [self unlockFocus];

    If that doesn't help, it could be that Cocoa refuses to switch buffers before the method call ends. That can be fixed by using NSTimer to schedule animation method calls. Here's one solution from developer.apple.com
    Code:
    - (void) startAnimationTimer
    {
        if (animationTimer == nil) {
            animationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES] retain];
        }
    }
    
    - (void) stopAnimationTimer
    {
        if (animationTimer != nil) {
            [animationTimer invalidate];
            [animationTimer release];
            animationTimer = nil;
        }
    }
    
    - (void) animationTimerFired:(NSTimer *)timer
    {
        Scene *scene = [openGLView scene];
        [scene advanceTimeBy:0.017];
        [openGLView setNeedsDisplay:YES];
    }
     
  6. hoschi thread starter macrumors newbie

    Joined:
    Sep 20, 2005
    #6
    NSOpenGLContext

    I was looking for places which say more about NSOpenGLContext and NSOpenGLPixelFormat, but only found some text with no explained examples. DOes anyone know about tutorials online? NeHe only talks about OpenGL and given the code in Cocoa- however they barely explain what they do in the Cocoa code. Wondering if there is an alternative site.

    Thanks
    Vyom
     
  7. hoschi thread starter macrumors newbie

    Joined:
    Sep 20, 2005
    #7
    Hi Guys:

    This program still doesnt work even though I have kind of tried all the possible above mentioned suggestions. I am posting the implementation file and hope that some of you could be the saviours..

    The program esentially takes a triangle and varies the coords of one of the vertices and redraws the whole thing... or should i say it "is supposed to" redraw the whole thing but hasn't been able to ..
     

    Attached Files:

  8. gekko513 macrumors 603

    gekko513

    Joined:
    Oct 16, 2003
    #8
    I see one possible error ...
    Code:
    for (i = 0; i< 10000; i++){
      ver3x = ver1x+ 0.00005;
      ver3y = ver1y - 0.00005;
      [self lockFocus];
      [self drawRect:[self frame]];
      [self unlockFocus];
      NSLog (@"Drawing in step%d", i);
    }
    
    The previous loop doesn't change the coordinates as it goes along. You would need something like the following ...
    Code:
    for (i = 0; i< 10000; i++){
      ver3x += 0.00005;
      ver3y -= 0.00005;
      [self lockFocus];
      [self drawRect:[self frame]];
      [self unlockFocus];
      NSLog (@"Drawing in step%d", i);
    }
    
     
  9. hoschi thread starter macrumors newbie

    Joined:
    Sep 20, 2005
    #9
    This really works now... thanks.. but I dont understand why a command like ver3x = ver3x - 0.0005 wouldnt work and a command like ver3x += 0.0005 would work...

    Never came across such a thing in any book
     
  10. gekko513 macrumors 603

    gekko513

    Joined:
    Oct 16, 2003
    #10
    ver3x = ver3x + 0.0005 would work just as well, but you had written ver3x = ver1x + 0.0005, probably by mistake.
     

Share This Page