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