فهرست منبع

Added multisampling support for MacOSX and iOS.
Cleaned up various aspects of iOS platform impl.

Steve Grenier 13 سال پیش
والد
کامیت
d0920de7f5
3فایلهای تغییر یافته به همراه212 افزوده شده و 59 حذف شده
  1. 47 5
      gameplay/src/PlatformMacOSX.mm
  2. 3 2
      gameplay/src/PlatformWin32.cpp
  3. 162 52
      gameplay/src/PlatformiOS.mm

+ 47 - 5
gameplay/src/PlatformMacOSX.mm

@@ -104,7 +104,7 @@ static View* __view = NULL;
     }
     CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
     CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
-    
+
     [gameLock unlock];
 }
 
@@ -116,9 +116,21 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 }
 
 - (id) initWithFrame: (NSRect) frame
-{    
+{
+    _game = Game::getInstance();
+    
+    Properties* config = _game->getConfig()->getNamespace("window", true);
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 4.
     NSOpenGLPixelFormatAttribute windowedAttrs[] = 
     {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
         NSOpenGLPFAAccelerated,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFAColorSize, 32,
@@ -129,6 +141,9 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     };
     NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
     {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
         NSOpenGLPFAFullScreen,
@@ -140,16 +155,38 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     };
     NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
     
+    // Try to choose a supported pixel format
     NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
     if (!pf)
-        NSLog(@"OpenGL pixel format not supported.");
+    {
+        bool valid = false;
+        while (!pf && samples > 0)
+        {
+            samples /= 2;
+            attrs[2] = samples ? 1 : 0;
+            attrs[4] = samples;
+            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+            if (pf)
+            {
+                valid = true;
+                break;
+            }
+        }
+        
+        if (!valid)
+        {
+            NSLog(@"OpenGL pixel format not supported.");
+            GP_ERROR("Failed to create a valid OpenGL pixel format.");
+            return nil;
+        }
+    }
     
-    if((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
+    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
     {
         gameLock = [[NSRecursiveLock alloc] init];
-        _game = Game::getInstance();
         __timeStart = getMachTimeInMilliseconds();
     }
+    
     return self;
 }
 
@@ -745,6 +782,11 @@ int Platform::enterMessagePump()
     NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
     
     __view = [[View alloc] initWithFrame:viewBounds];
+    if (__view == NULL)
+    {
+        GP_ERROR("Failed to create view: exiting.");
+        return EXIT_FAILURE;
+    }
     
     NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
                                  NSMidY(screenBounds) - NSMidY(viewBounds),

+ 3 - 2
gameplay/src/PlatformWin32.cpp

@@ -737,6 +737,9 @@ bool initializeGL(WindowCreationParams* params)
 
     // Choose pixel format using wglChoosePixelFormatARB, which allows us to specify
     // additional attributes such as multisampling.
+    //
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 3.
     int attribList[] = {
         WGL_SAMPLES_ARB, params->samples,
         WGL_SAMPLE_BUFFERS_ARB, params->samples > 0 ? 1 : 0,
@@ -887,8 +890,6 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
             // Read multisampling state.
             params.samples = config->getInt("samples");
-            if (params.samples == 0)
-                params.samples = 1;
         }
     }
 

+ 162 - 52
gameplay/src/PlatformiOS.mm

@@ -53,14 +53,19 @@ int getKey(unichar keyCode);
 {
     EAGLContext* context;
     CADisplayLink* displayLink;
+    BOOL updateFramebuffer;
     GLuint defaultFramebuffer;
     GLuint colorRenderbuffer;
     GLuint depthRenderbuffer;
     GLint framebufferWidth;
-    GLint framebufferHeight;    
+    GLint framebufferHeight;
+    GLuint multisampleFramebuffer;
+    GLuint multisampleRenderbuffer;
+    GLuint multisampleDepthbuffer;
     NSInteger swapInterval;
     BOOL updating;
-    Game* _game;
+    Game* game;
+    BOOL oglDiscardSupported;
     
     UITapGestureRecognizer *_tapRecognizer;
     UIPinchGestureRecognizer *_pinchRecognizer;
@@ -82,11 +87,10 @@ int getKey(unichar keyCode);
 @end
 
 @interface View (Private)
-- (void)createFramebuffer;
+- (BOOL)createFramebuffer;
 - (void)deleteFramebuffer;
 @end
 
-
 @implementation View
 
 @synthesize updating;
@@ -111,11 +115,21 @@ int getKey(unichar keyCode);
         }
         else
         {
-            print("Invalid OS Version: %s\n", (currSysVer == NULL?"NULL":[currSysVer cStringUsingEncoding:NSASCIIStringEncoding]));
+            GP_ERROR("Invalid OS Version: %s\n", (currSysVer == NULL?"NULL":[currSysVer cStringUsingEncoding:NSASCIIStringEncoding]));
             [self release];
             return nil;
         }
         
+        // Check for OS 4.0+ features
+        if ([currSysVer compare:@"4.0" options:NSNumericSearch] != NSOrderedAscending)
+        {
+            oglDiscardSupported = YES;
+        }
+        else
+        {
+            oglDiscardSupported = NO;
+        }
+        
         // Configure the CAEAGLLayer and setup out the rendering context
         CGFloat scale = [[UIScreen mainScreen] scale];
         CAEAGLLayer* layer = (CAEAGLLayer *)self.layer;
@@ -129,42 +143,36 @@ int getKey(unichar keyCode);
         context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
         if (!context || ![EAGLContext setCurrentContext:context])
         {
+            GP_ERROR("Failed to make context current.");
             [self release];
             return nil;
         }
 
-        if (!defaultFramebuffer)
-        {
-            [self createFramebuffer];
-        }
-            
-        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
-        glViewport(0, 0, framebufferWidth, framebufferHeight);
-
         // Initialize Internal Defaults
         displayLink = nil;
+        updateFramebuffer = YES;
         defaultFramebuffer = 0;
         colorRenderbuffer = 0;
         depthRenderbuffer = 0;
         framebufferWidth = 0;
         framebufferHeight = 0;
+        multisampleFramebuffer = 0;
+        multisampleRenderbuffer = 0;
+        multisampleDepthbuffer = 0;
         swapInterval = 1;        
         updating = FALSE;
-        
-        [self createFramebuffer];
+        game = Game::getInstance();
         
         // Set the resource path and initalize the game
         NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
         FileSystem::setResourcePath([bundlePath fileSystemRepresentation]); 
-        
     }
     return self;
 }
 
-
 - (void) dealloc
 {
-    _game->exit();
+    game->exit();
     [self deleteFramebuffer];
     
     if ([EAGLContext currentContext] == context)
@@ -183,35 +191,85 @@ int getKey(unichar keyCode);
 
 - (void) layoutSubviews
 {
-    // Called on 'resize'
-    [self deleteFramebuffer];
+    // Called on 'resize'.
+    // Mark that framebuffer needs to be updated.
+    updateFramebuffer = YES;
 }
 
-- (void)createFramebuffer
+- (BOOL)createFramebuffer
 {
     // iOS Requires all content go to a rendering buffer then it is swapped into the windows rendering surface
     assert(defaultFramebuffer == 0);
     
-    // Create the default frame buffer, and render buffer
-    glGenFramebuffers(1, &defaultFramebuffer);
-    glGenRenderbuffers(1, &colorRenderbuffer);
-    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);    
-    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
+    // Create the default frame buffer
+    GL_ASSERT( glGenFramebuffers(1, &defaultFramebuffer) );
+    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+    
+    // Create a color buffer to attach to the frame buffer
+    GL_ASSERT( glGenRenderbuffers(1, &colorRenderbuffer) );
+    GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer) );
     
-    // request storage, width, and height of the view that we will render in
+    // Associate our render buffer with CAEAGLLauyer so that the rendered content is display on our UI layer.
     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
     
-    glGenRenderbuffers(1, &depthRenderbuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
-    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight);
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
+    // Attach the color buffer to our frame buffer
+    GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer) );
+    
+    // Retrieve framebuffer size
+    GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth) );
+    GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight) );
+    
+    // If multisampling is enabled in config, create and setup a multisample buffer
+    Properties* config = game->getConfig()->getNamespace("window");
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    if (samples)
+    {
+        GL_ASSERT( glGenFramebuffers(1, &multisampleFramebuffer) );
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer) );
+        
+        GL_ASSERT( glGenRenderbuffers(1, &multisampleRenderbuffer) );
+        GL_ASSERT( glBindRenderbuffer(1, multisampleRenderbuffer) );
+        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_RGBA8_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisampleRenderbuffer) );
+        
+        GL_ASSERT( glGenRenderbuffers(1, &multisampleDepthbuffer) );
+        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, multisampleDepthbuffer) );
+        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, multisampleDepthbuffer) );
+        
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+        {
+            NSLog(@"Failed to make complete multisample buffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+            [self deleteFramebuffer];
+            return NO;
+        }
+        
+        // Re-bind the default framebuffer
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+    }
+    
+    // Create default depth buffer and attach to the frame buffer.
+    // Note: If we are using multisample buffers, we can skip depth buffer creation here since we only
+    // need the color buffer to resolve to.
+    if (multisampleFramebuffer == 0)
+    {
+        GL_ASSERT( glGenRenderbuffers(1, &depthRenderbuffer) );
+        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer) );
+        GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer) );
+    }
     
     // Sanity check, ensure that the framebuffer is valid
     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+    {
         NSLog(@"ERROR: Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+        [self deleteFramebuffer];
+        return NO;
+    }
+    
+    return YES;
 }
 
 - (void)deleteFramebuffer
@@ -234,6 +292,21 @@ int getKey(unichar keyCode);
             glDeleteRenderbuffers(1, &depthRenderbuffer);
             depthRenderbuffer = 0;
         }
+        if (multisampleFramebuffer)
+        {
+            glDeleteFramebuffers(1, &multisampleFramebuffer);
+            multisampleFramebuffer = 0;
+        }
+        if (multisampleRenderbuffer)
+        {
+            glDeleteRenderbuffers(1, &multisampleRenderbuffer);
+            multisampleRenderbuffer = 0;
+        }
+        if (multisampleDepthbuffer)
+        {
+            glDeleteRenderbuffers(1, &multisampleDepthbuffer);
+            multisampleDepthbuffer = 0;
+        }
     }
 }
 
@@ -257,8 +330,35 @@ int getKey(unichar keyCode);
 
 - (void)swapBuffers
 {
-    if (context != nil)
+    if (context)
     {
+        if (multisampleFramebuffer)
+        {
+            // Multisampling is enabled: resolve the multisample buffer into the default framebuffer
+            glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer);
+            glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, multisampleFramebuffer);
+            glResolveMultisampleFramebufferAPPLE();
+            
+            if (oglDiscardSupported)
+            {
+                // Performance hint that the GL driver can discard the contents of the multisample buffers
+                // since they have now been resolved into the default framebuffer
+                const GLenum discards[]  = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
+                glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards);
+            }
+        }
+        else
+        {
+            if (oglDiscardSupported)
+            {
+                // Performance hint to the GL driver that the depth buffer is no longer required.
+                const GLenum discards[]  = { GL_DEPTH_ATTACHMENT };
+                glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
+                glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);
+            }
+        }
+        
+        // Present the color buffer
         glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
         [context presentRenderbuffer:GL_RENDERBUFFER];
     }
@@ -266,9 +366,9 @@ int getKey(unichar keyCode);
 
 - (void)startGame 
 {
-    _game = Game::getInstance();
+    game = Game::getInstance();
     __timeStart = getMachTimeInMilliseconds();
-    _game->run();  
+    game->run();
 }
 
 - (void)startUpdating
@@ -278,7 +378,7 @@ int getKey(unichar keyCode);
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         [displayLink setFrameInterval:swapInterval];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-        _game->resume();
+        game->resume();
         updating = TRUE;
     }
 }
@@ -287,7 +387,7 @@ int getKey(unichar keyCode);
 {
     if (updating)
     {
-        _game->pause();
+        game->pause();
         [displayLink invalidate];
         displayLink = nil;
         updating = FALSE;
@@ -298,18 +398,28 @@ int getKey(unichar keyCode);
 {   
     if (context != nil)
     {
+        // Ensure our context is current
         [EAGLContext setCurrentContext:context];
-        if (!defaultFramebuffer)
+        
+        // If the framebuffer needs (re)creating, do so
+        if (updateFramebuffer)
+        {
+            updateFramebuffer = NO;
+            [self deleteFramebuffer];
             [self createFramebuffer];
+        }
         
-        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
-        glViewport(0, 0, framebufferWidth, framebufferHeight);
-
-        if (_game && _game->getState() == Game::RUNNING)       
-            _game->frame();
+        // Bind our framebuffer for rendering.
+        // If multisampling is enabled, bind the multisample buffer - otherwise bind the default buffer
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer ? multisampleFramebuffer : defaultFramebuffer) );
+        GL_ASSERT( glViewport(0, 0, framebufferWidth, framebufferHeight) );
         
-        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
-        [context presentRenderbuffer:GL_RENDERBUFFER];
+        // Execute a single game frame
+        if (game && game->getState() == Game::RUNNING)
+            game->frame();
+        
+        // Present the contents of the color buffer
+        [self swapBuffers];
     }
 }
 
@@ -448,14 +558,14 @@ int getKey(unichar keyCode);
 - (void)handleTapGesture:(UITapGestureRecognizer *)sender
 {
     CGPoint location = [sender locationInView:self];
-    _game->gestureTapEvent(location.x, location.y);
+    game->gestureTapEvent(location.x, location.y);
 }
 
 - (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender
 {
     CGFloat factor = [sender scale];
     CGPoint location = [sender locationInView:self];
-    _game->gesturePinchEvent(location.x, location.y, factor);
+    game->gesturePinchEvent(location.x, location.y, factor);
 }
 
 - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
@@ -477,7 +587,7 @@ int getKey(unichar keyCode);
             gameplayDirection = Gesture::SWIPE_DIRECTION_DOWN;
             break;
     }
-    _game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
+    game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
 }
 
 @end
@@ -940,7 +1050,7 @@ int Platform::enterMessagePump()
     [pool release];
     return EXIT_SUCCESS;
 }
-    
+
 void Platform::signalShutdown() 
 {
     // Cannot 'exit' an iOS Application
@@ -948,7 +1058,7 @@ void Platform::signalShutdown()
     [__view stopUpdating];
     exit(0);
 }
-    
+
 unsigned int Platform::getDisplayWidth()
 {
     CGSize size = DeviceOrientedSize([__appDelegate.viewController interfaceOrientation]);