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