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         self->_isSwPipe = NO;
 194         
 195         [self _assertContext];
 196         if ([self _supportsFbo] == NO)
 197         {
 198             [super dealloc];
 199             self = nil;
 200         }
 201     }
 202     return self;
 203 }
 204 
 205 - (void)dealloc
 206 {
 207     [self _assertContext];
 208     {
 209         [self _destroyFbo];
 210     }
 211     
 212     [super dealloc];
 213 }
 214 
 215 - (GLuint)width
 216 {
 217     return self->_width;
 218 }
 219 
 220 - (GLuint)height
 221 {
 222     return self->_height;
 223 }
 224 
 225 - (void)bindForWidth:(GLuint)width andHeight:(GLuint)height
 226 {
 227     LOG("           GlassFrameBufferObject bindForWidth:%d andHeight:%d", width, height);
 228     LOG("               context:%p", CGLGetCurrentContext());
 229     [self _assertContext];
 230     {
 231         if ((width > 0) && (height > 0))
 232         {
 233             if(self->_isSwPipe)
 234             {
 235                 self->_fboToRestore = 0; // default to screen
 236                 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, (GLint*)&self->_fboToRestore);
 237                 LOG("               will need to restore to FBO: %d", self->_fboToRestore);
 238             }
 239 
 240             [self _createFboIfNeededForWidth:width andHeight:height];
 241 
 242             if (self->_isSwPipe && (self->_fbo != 0))
 243             {
 244                 GLuint framebufferToBind = self->_fbo; // our own FBO
 245                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferToBind);
 246                 LOG("               bounded to FBO: %d", self->_fbo);
 247             }
 248         }
 249     }
 250     LOG("               BOUND");
 251     LOG("               glGetError(): %d", glGetError());
 252 }
 253 
 254 - (void)unbind
 255 {
 256     if (self->_isSwPipe)
 257     {
 258         LOG("           GlassFrameBufferObject unbind"); 
 259         [self _assertContext];
 260         {
 261             GLint framebufferCurrent = 0;
 262             glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &framebufferCurrent);
 263 
 264             if ((GLuint)framebufferCurrent != self->_fbo)
 265             {
 266                 fprintf(stderr, "ERROR: unexpected fbo is bound! Expected %d, but found %d\n", self->_fbo, framebufferCurrent);
 267             }
 268 
 269             if (![GlassApplication syncRenderingDisabled]) {         
 270                 glFinish();
 271             }
 272             GLuint framebufferToRevertTo = self->_fboToRestore;
 273             glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebufferToRevertTo);
 274             LOG("               restored to FBO: %d", framebufferToRevertTo);
 275             LOG("               glGetError(): %d", glGetError());
 276         }
 277     }
 278 }
 279 
 280 - (void)blitForWidth:(GLuint)width andHeight:(GLuint)height
 281 {
 282     LOG("           GlassFrameBufferObject blitForWidth:%d andHeight:%d [%p]", width, height, self);
 283     if (self->_texture != 0)
 284     {
 285         glMatrixMode(GL_PROJECTION);
 286         glLoadIdentity();
 287         glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
 288         
 289         glMatrixMode(GL_MODELVIEW);
 290         glLoadIdentity();
 291         
 292         glEnable(TARGET);
 293         {
 294             glActiveTextureARB(GL_TEXTURE0);
 295             glBindTexture(TARGET, self->_texture);
 296             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 297             {
 298                 glBegin(GL_QUADS);
 299                 {
 300                     glTexCoord2f(        0.0f, self->_height); glVertex2f(        0.0f,          0.0f);
 301                     glTexCoord2f(self->_width, self->_height); glVertex2f(self->_width,          0.0f);
 302                     glTexCoord2f(self->_width,          0.0f); glVertex2f(self->_width, self->_height);
 303                     glTexCoord2f(        0.0f,          0.0f); glVertex2f(        0.0f, self->_height);
 304                 }
 305                 glEnd();
 306             }
 307             glBindTexture(TARGET, 0);
 308         }
 309         glDisable(TARGET);
 310         LOG("               BLITED");
 311         LOG("               glGetError(): %d", glGetError());
 312     }
 313 }
 314 
 315 - (void)blitFromFBO:(GlassFrameBufferObject*)other_fbo
 316 {
 317     self->_fboToRestore = 0; // default to screen
 318     glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, (GLint*)&self->_fboToRestore);
 319     [self _createFboIfNeededForWidth:other_fbo->_width andHeight:other_fbo->_height];
 320     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self->_fbo);
 321     glBindFramebuffer(GL_READ_FRAMEBUFFER, other_fbo->_fbo);
 322     glBlitFramebuffer(0,0, other_fbo->_width, other_fbo->_height,
 323                       0,0, self->_width, self->_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 324     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, self->_fboToRestore);
 325 }
 326 
 327 
 328 - (GLuint)texture
 329 {
 330     return self->_texture;
 331 }
 332 
 333 - (GLuint)fbo
 334 {
 335     return self->_fbo;
 336 }
 337 
 338 - (void)setIsSwPipe:(BOOL)isSwPipe
 339 {
 340     self->_isSwPipe = isSwPipe;
 341 }
 342 
 343 @end