1 /*
   2  * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import "GlassFrameBufferObject.h"
  27 #import "GlassMacros.h"
  28 #import "GlassApplication.h"
  29 
  30 #import <OpenGL/glu.h>
  31 
  32 #define TARGET GL_TEXTURE_RECTANGLE_EXT
  33 #define FORMAT GL_BGRA
  34 #define TYPE GL_UNSIGNED_INT_8_8_8_8_REV
  35 
  36 //#define VERBOSE
  37 #ifndef VERBOSE
  38     #define LOG(MSG, ...)
  39 #else
  40     #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  41 #endif
  42 
  43 @implementation GlassFrameBufferObject
  44 
  45 - (CGLContextObj)_assertContext
  46 {
  47     CGLContextObj cgl_ctx = CGLGetCurrentContext();
  48     assert(cgl_ctx != NULL);
  49     return cgl_ctx;
  50 }
  51 
  52 - (BOOL)_supportsFbo
  53 {
  54     return (gluCheckExtension((const GLubyte *)"GL_EXT_framebuffer_object", glGetString(GL_EXTENSIONS)) == GL_TRUE);
  55 }
  56 
  57 - (BOOL)_checkFbo
  58 {
  59     BOOL ok = NO;
  60     {
  61         switch (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT))
  62         {
  63             case GL_FRAMEBUFFER_COMPLETE_EXT:
  64                 ok = YES;
  65                 break;
  66             case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
  67                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT");
  68                 break;
  69             case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
  70                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT");
  71                 break;
  72             case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
  73                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT");
  74                 break;
  75             case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
  76                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT");
  77                 break;
  78             case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
  79                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT");                          
  80                 break;
  81             case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
  82                 NSLog(@"GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT");
  83                 break;
  84             case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
  85                 NSLog(@"GL_FRAMEBUFFER_UNSUPPORTED_EXT");
  86                 break;
  87             default:
  88                 NSLog(@"Unknown FBO Error");
  89                 break;
  90         }
  91     }
  92     return ok;
  93 }
  94 
  95 - (void)_destroyFbo
  96 {
  97     if (self->_texture != 0)
  98     {
  99         glDeleteTextures(1, &self->_texture);
 100         self->_texture = 0;
 101     }
 102     if (self->_fbo != 0)
 103     {
 104         glDeleteFramebuffersEXT(1, &self->_fbo);
 105         self->_fbo = 0;
 106     }
 107 }
 108 
 109 - (void)_createFboIfNeededForWidth:(GLuint)width andHeight:(GLuint)height
 110 {
 111     if ((self->_width != width) || (self->_height != height))
 112     {
 113         // TODO optimization: is it possible to just resize an FBO's texture without destroying it first?
 114         [self _destroyFbo];
 115     }
 116     
 117     if (self->_fbo == 0)
 118     {
 119         glEnable(TARGET);
 120         {
 121             glActiveTextureARB(GL_TEXTURE0);
 122             glGenTextures(1, &self->_texture);
 123             LOG("           GlassFrameBufferObject created Texture: %d", self->_texture);
 124             glBindTexture(TARGET, self->_texture);
 125             glTexParameteri(TARGET, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 126             glTexParameteri(TARGET, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 127             glTexParameteri(TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 128             glTexParameteri(TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 129             {
 130                 GLenum target = TARGET;
 131                 GLint level = 0;
 132                 GLint internalformat = GL_RGBA;
 133                 GLint border = 0;
 134                 GLenum format = FORMAT;
 135                 GLenum type = TYPE;
 136                 const GLvoid *pixels = NULL;
 137                 glTexImage2D(target, level, internalformat, (GLint)width, (GLint)height, border, format, type, pixels);
 138             }
 139             
 140             glGenFramebuffersEXT(1, &self->_fbo);
 141             LOG("           GlassFrameBufferObject created FBO: %d", self->_fbo);
 142             glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self->_fbo);
 143             {
 144                 GLenum target = GL_FRAMEBUFFER_EXT;
 145                 GLenum attachment = GL_COLOR_ATTACHMENT0_EXT;
 146                 GLenum textarget = TARGET;
 147                 GLuint texture = self->_texture;
 148                 GLint level = 0;
 149                 glFramebufferTexture2DEXT(target, attachment, textarget, texture, level);
 150             }
 151             LOG("           glGetError(): %d", glGetError());
 152             
 153             if ([self _checkFbo] == NO)
 154             {
 155                 [self _destroyFbo];
 156             }
 157         }
 158         glDisable(TARGET);
 159         
 160         glViewport(0, 0, (GLint)width, (GLint)height);
 161     }
 162 //    DOES NOT WORK
 163 //    else if ((self->_width != width) || (self->_height != height))
 164 //    {
 165 //        glEnable(TARGET);
 166 //        glBindTexture(TARGET, self->_texture);
 167 //        
 168 //        GLenum target = TARGET;
 169 //        GLint level = 0;
 170 //        GLint xoffset = 0;
 171 //        GLint yoffset = 0;
 172 //        GLenum format = FORMAT;
 173 //        GLenum type = TYPE;
 174 //        const GLvoid *pixels = NULL;
 175 //        glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
 176 //
 177 //        glDisable(TARGET);
 178 //    }
 179     
 180     self->_width = width;
 181     self->_height = height;
 182 }
 183 
 184 - (id)init
 185 {
 186     self = [super init];
 187     if (self != nil)
 188     {
 189         self->_width = 0;
 190         self->_height = 0;
 191         self->_texture = 0;
 192         self->_fbo = 0;
 193         
 194         [self _assertContext];
 195         if ([self _supportsFbo] == NO)
 196         {
 197             [super dealloc];
 198             self = nil;
 199         }
 200     }
 201     return self;
 202 }
 203 
 204 - (void)dealloc
 205 {
 206     [self _assertContext];
 207     {
 208         [self _destroyFbo];
 209     }
 210     
 211     [super dealloc];
 212 }
 213 
 214 - (GLuint)width
 215 {
 216     return self->_width;
 217 }
 218 
 219 - (GLuint)height
 220 {
 221     return self->_height;
 222 }
 223 
 224 - (void)bindForWidth:(GLuint)width andHeight:(GLuint)height
 225 {
 226     LOG("           GlassFrameBufferObject bindForWidth:%d andHeight:%d", width, height);
 227     LOG("               context:%p", CGLGetCurrentContext());
 228     [self _assertContext];
 229     {
 230         if ((width > 0) && (height > 0))
 231         {            
 232             [self _createFboIfNeededForWidth:width andHeight:height];
 233         }
 234     }
 235     LOG("               BOUND");
 236     LOG("               glGetError(): %d", glGetError());
 237 }
 238 
 239 - (void)blitForWidth:(GLuint)width andHeight:(GLuint)height
 240 {
 241     LOG("           GlassFrameBufferObject blitForWidth:%d andHeight:%d [%p]", width, height, self);
 242     if (self->_texture != 0)
 243     {
 244         glMatrixMode(GL_PROJECTION);
 245         glLoadIdentity();
 246         glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
 247         
 248         glMatrixMode(GL_MODELVIEW);
 249         glLoadIdentity();
 250         
 251         glEnable(TARGET);
 252         {
 253             glActiveTextureARB(GL_TEXTURE0);
 254             glBindTexture(TARGET, self->_texture);
 255             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 256             {
 257                 glBegin(GL_QUADS);
 258                 {
 259                     glTexCoord2f(        0.0f, self->_height); glVertex2f(        0.0f,          0.0f);
 260                     glTexCoord2f(self->_width, self->_height); glVertex2f(self->_width,          0.0f);
 261                     glTexCoord2f(self->_width,          0.0f); glVertex2f(self->_width, self->_height);
 262                     glTexCoord2f(        0.0f,          0.0f); glVertex2f(        0.0f, self->_height);
 263                 }
 264                 glEnd();
 265             }
 266             glBindTexture(TARGET, 0);
 267         }
 268         glDisable(TARGET);
 269         LOG("               BLITED");
 270         LOG("               glGetError(): %d", glGetError());
 271     }
 272 }
 273 
 274 - (void)blitFromFBO:(GlassFrameBufferObject*)other_fbo
 275 {
 276     self->_fboToRestore = 0; // default to screen
 277     glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, (GLint*)&self->_fboToRestore);
 278     [self _createFboIfNeededForWidth:other_fbo->_width andHeight:other_fbo->_height];
 279     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self->_fbo);
 280     glBindFramebuffer(GL_READ_FRAMEBUFFER, other_fbo->_fbo);
 281     glBlitFramebuffer(0,0, other_fbo->_width, other_fbo->_height,
 282                       0,0, self->_width, self->_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 283     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self->_fboToRestore);
 284 }
 285 
 286 
 287 - (GLuint)texture
 288 {
 289     return self->_texture;
 290 }
 291 
 292 - (GLuint)fbo
 293 {
 294     return self->_fbo;
 295 }
 296 
 297 @end