00001 /** 00002 \file Robots/LoBot/ui/LoRenderBuffer.C 00003 00004 \brief This file defines the non-inline member functions of the 00005 lobot::RenderBuffer class, which is used to implement an off-screen 00006 rendering buffer via OpenGL's framebuffer object API. 00007 */ 00008 00009 // //////////////////////////////////////////////////////////////////// // 00010 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00011 // by the University of Southern California (USC) and the iLab at USC. // 00012 // See http://iLab.usc.edu for information about this project. // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00015 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00016 // in Visual Environments, and Applications'' by Christof Koch and // 00017 // Laurent Itti, California Institute of Technology, 2001 (patent // 00018 // pending; application number 09/912,225 filed July 23, 2001; see // 00019 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00020 // //////////////////////////////////////////////////////////////////// // 00021 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00024 // redistribute it and/or modify it under the terms of the GNU General // 00025 // Public License as published by the Free Software Foundation; either // 00026 // version 2 of the License, or (at your option) any later version. // 00027 // // 00028 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00029 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00030 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00031 // PURPOSE. See the GNU General Public License for more details. // 00032 // // 00033 // You should have received a copy of the GNU General Public License // 00034 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00035 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00036 // Boston, MA 02111-1307 USA. // 00037 // //////////////////////////////////////////////////////////////////// // 00038 // 00039 // Primary maintainer for this file: mviswana usc edu 00040 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/ui/LoRenderBuffer.C $ 00041 // $Id: LoRenderBuffer.C 13838 2010-08-27 20:42:20Z mviswana $ 00042 // 00043 00044 //-------------------------- OPENGL MISSING ----------------------------- 00045 00046 #ifndef INVT_HAVE_LIBGL 00047 00048 #include "Robots/LoBot/ui/LoRenderBuffer.H" 00049 #include "Robots/LoBot/misc/LoExcept.H" 00050 00051 namespace lobot { 00052 00053 RenderBuffer::RenderBuffer(int, int) 00054 : m_width(0), m_height(0), 00055 m_size(0), m_cache(0), m_dirty(false), 00056 m_fbo(0), m_rbo(0) 00057 { 00058 throw missing_libs(MISSING_OPENGL) ; 00059 } 00060 00061 void RenderBuffer::setup(){} 00062 const unsigned char* RenderBuffer::pixels() const {return 0 ;} 00063 void RenderBuffer::to_screen(){} 00064 void RenderBuffer::clean_up(){} 00065 RenderBuffer::~RenderBuffer(){} 00066 00067 } // end of namespace encapsulating above empty definition 00068 00069 #else // OpenGL available 00070 00071 //------------------------------ HEADERS -------------------------------- 00072 00073 // lobot headers 00074 #include "Robots/LoBot/ui/LoRenderBuffer.H" 00075 #include "Robots/LoBot/misc/LoExcept.H" 00076 00077 // INVT headers 00078 #include "Util/log.H" 00079 00080 // OpenGL headers 00081 #ifdef INVT_HAVE_LIBGLEW 00082 #include <GL/glew.h> 00083 #endif 00084 00085 #define GL_GLEXT_PROTOTYPES 00086 #include <GL/gl.h> 00087 00088 //------------------------------ MACROS --------------------------------- 00089 00090 // Since off-screen rendering support depends heavily on OpenGL versions 00091 // and extension availability, it is useful to be able to see some 00092 // diagnostic messages about how this module goes about implementing its 00093 // off-screen rendering API. This symbol can be used to turn this 00094 // diagnostic tracing on. In normal use, it would be best to keep the 00095 // following line commented. 00096 //#define LOBOT_TRACE_RENDER_BUFFER 00097 00098 // We simply use INVT's LERROR to implement the tracing functionality 00099 // alluded to above. However, to be able to turn tracing on/off without 00100 // having to manually comment each and every LERROR or to wrap each 00101 // LERROR inside of an #ifdef LOBOT_TRACE_RENDER_BUFFER block, we use the 00102 // following macro, viz., LRB_TRACE (LRB = lobot render buffer), and 00103 // define it appropriately depending on whether tracing is on or off. 00104 #ifdef LOBOT_TRACE_RENDER_BUFFER // use INVT LERROR for trace messages 00105 #define LRB_TRACE LERROR 00106 #else // no tracing ==> LRB_TRACE = nop 00107 #define LRB_TRACE(...) 00108 #endif 00109 00110 //----------------------------- NAMESPACE ------------------------------- 00111 00112 namespace lobot { 00113 00114 //------------------ FRAMEBUFFER OBJECTS IN GL CORE --------------------- 00115 00116 // DEVNOTE: THIS CODE IS SUSPECT! 00117 // 00118 // It was never actually built and run on a machine that had the FBO API 00119 // in the GL core. 00120 #ifdef GL_VERSION_4_0 00121 //#warning "framebuffer objects are part of GL core" 00122 00123 // Initialize off-screen rendering buffer of specified size 00124 RenderBuffer::RenderBuffer(int W, int H) 00125 : m_width(W), m_height(H), 00126 m_size(W * H * 4), m_cache(new unsigned char[m_size]), m_dirty(true) 00127 { 00128 LRB_TRACE("initializing off-screen rendering using core GL API...") ; 00129 00130 glGenFramebuffers(1, &m_fbo) ; 00131 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo) ; 00132 00133 glGenRenderbuffers(1, &m_rbo) ; 00134 glBindRenderbuffer(GL_RENDERBUFFER, m_rbo) ; 00135 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, m_width, m_height) ; 00136 if (glGetError() != GL_NO_ERROR) { 00137 clean_up() ; 00138 throw misc_error(OPENGL_FBO_INIT_ERROR) ; 00139 } 00140 00141 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 00142 GL_RENDERBUFFER, m_rbo) ; 00143 00144 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 00145 clean_up() ; 00146 throw misc_error(OPENGL_FBO_INIT_ERROR) ; 00147 } 00148 00149 std::fill_n(m_cache, m_size, 0) ; 00150 } 00151 00152 // Setup off-screen rendering 00153 void RenderBuffer::setup() 00154 { 00155 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo) ; 00156 m_dirty = true ; 00157 } 00158 00159 // Retrieve off-screen buffer's pixel data 00160 const unsigned char* RenderBuffer::pixels() const 00161 { 00162 if (m_dirty) 00163 { 00164 LRB_TRACE("retrieving off-screen buffer's pixel data...") ; 00165 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo) ; 00166 glReadPixels(0, 0, m_width, m_height, 00167 GL_BGRA, GL_UNSIGNED_BYTE, m_cache) ; 00168 if (glGetError() == GL_NO_ERROR) 00169 m_dirty = false ; 00170 else 00171 std::fill_n(m_cache, m_size, 0) ; 00172 } 00173 else 00174 LRB_TRACE("returning cached off-screen buffer pixel data...") ; 00175 return m_cache ; 00176 } 00177 00178 // Copy contents of off-screen buffer to the back buffer 00179 void RenderBuffer::to_screen() 00180 { 00181 LRB_TRACE("blitting off-screen buffer to back buffer...") ; 00182 00183 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo) ; 00184 glReadBuffer(GL_COLOR_ATTACHMENT0) ; 00185 00186 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) ; 00187 glDrawBuffer(GL_BACK) ; 00188 00189 glBlitFramebuffer(0, 0, m_width, m_height, 00190 0, 0, m_width, m_height, 00191 GL_COLOR_BUFFER_BIT, GL_NEAREST) ; 00192 } 00193 00194 // Clean-up 00195 void RenderBuffer::clean_up() 00196 { 00197 LRB_TRACE("cleaning up off-screen buffer...") ; 00198 00199 glBindFramebuffer (GL_FRAMEBUFFER, 0) ; 00200 glBindRenderbuffer(GL_RENDERBUFFER, 0) ; 00201 00202 glDeleteRenderbuffers(1, &m_rbo) ; 00203 glDeleteFramebuffers (1, &m_fbo) ; 00204 00205 delete[] m_cache ; m_cache = 0 ; 00206 } 00207 00208 RenderBuffer::~RenderBuffer() 00209 { 00210 clean_up() ; 00211 } 00212 00213 //---------------- FRAMEBUFFER OBJECTS IN GL EXTENSION ------------------ 00214 00215 #elif defined(GL_EXT_framebuffer_object) 00216 //#warning "framebuffer objects are a GL extension" 00217 00218 #ifndef INVT_HAVE_LIBGLEW 00219 00220 RenderBuffer::RenderBuffer(int, int) 00221 : m_width(0), m_height(0), 00222 m_size(0), m_cache(0), m_dirty(true), 00223 m_fbo(0), m_rbo(0) 00224 { 00225 throw missing_libs(MISSING_GLEW) ; 00226 } 00227 00228 void RenderBuffer::setup(){} 00229 const unsigned char* RenderBuffer::pixels() const {return 0 ;} 00230 void RenderBuffer::to_screen(){} 00231 void RenderBuffer::clean_up(){} 00232 RenderBuffer::~RenderBuffer(){} 00233 00234 #else // OpenGL extension wrangler library (GLEW) is available 00235 00236 // Initialize off-screen rendering buffer of specified size 00237 RenderBuffer::RenderBuffer(int W, int H) 00238 : m_width(W), m_height(H), 00239 m_size(W * H * 4), m_cache(new unsigned char[m_size]), m_dirty(true) 00240 { 00241 if (glewInit() != GLEW_OK) { 00242 delete[] m_cache ; 00243 throw misc_error(GLEW_INIT_ERROR) ; 00244 } 00245 00246 if (GLEW_EXT_framebuffer_object) 00247 { 00248 LRB_TRACE("initializing off-screen rendering using GL extension API..."); 00249 00250 glGenFramebuffersEXT(1, &m_fbo) ; 00251 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo) ; 00252 00253 glGenRenderbuffersEXT(1, &m_rbo) ; 00254 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_rbo) ; 00255 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, 00256 m_width, m_height) ; 00257 if (glGetError() != GL_NO_ERROR) { 00258 clean_up() ; 00259 throw misc_error(OPENGL_FBO_INIT_ERROR) ; 00260 } 00261 00262 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 00263 GL_COLOR_ATTACHMENT0_EXT, 00264 GL_RENDERBUFFER_EXT, m_rbo) ; 00265 00266 GLenum fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ; 00267 if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) { 00268 clean_up() ; 00269 throw misc_error(OPENGL_FBO_INIT_ERROR) ; 00270 } 00271 } 00272 else 00273 LRB_TRACE("GL driver lacks FBO support ==> no off-screen rendering!") ; 00274 std::fill_n(m_cache, m_size, 0) ; 00275 } 00276 00277 // Setup off-screen rendering 00278 void RenderBuffer::setup() 00279 { 00280 if (GLEW_EXT_framebuffer_object) 00281 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo) ; 00282 m_dirty = true ; 00283 } 00284 00285 // Retrieve off-screen buffer's pixel data 00286 // 00287 // NOTE: If GL doesn't support framebuffer objects, we will simply read 00288 // the back buffer, which can cause bogus screen captures, e.g., window 00289 // obscured by another. But, unfortunately, that's the best we can do 00290 // with GLUT. If we were using GLX instead, we could have tested for the 00291 // pbuffer extension and used that... 00292 const unsigned char* RenderBuffer::pixels() const 00293 { 00294 if (m_dirty) 00295 { 00296 if (GLEW_EXT_framebuffer_object) 00297 { 00298 LRB_TRACE("retrieving off-screen buffer's pixel data...") ; 00299 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo) ; 00300 } 00301 else 00302 { 00303 LRB_TRACE("retrieving back buffer's pixel data...") ; 00304 glReadBuffer(GL_BACK) ; 00305 } 00306 glReadPixels(0, 0, m_width, m_height, 00307 GL_BGRA, GL_UNSIGNED_BYTE, m_cache) ; 00308 if (glGetError() == GL_NO_ERROR) 00309 m_dirty = false ; 00310 else 00311 std::fill_n(m_cache, m_size, 0) ; 00312 } 00313 else 00314 LRB_TRACE("returning cached off-screen buffer pixel data...") ; 00315 return m_cache ; 00316 } 00317 00318 // Copy contents of off-screen buffer to the back buffer 00319 // 00320 // DEVNOTE: The blit code is suspect! It was never run on a host where 00321 // the GL driver supported blitting. 00322 void RenderBuffer::to_screen() 00323 { 00324 #ifdef GL_EXT_framebuffer_blit 00325 //#warning "FBO extension supports blitting" 00326 if (GLEW_EXT_framebuffer_blit) 00327 { 00328 LRB_TRACE("blitting off-screen buffer to back buffer...") ; 00329 00330 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_fbo) ; 00331 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT) ; 00332 00333 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0) ; 00334 glDrawBuffer(GL_BACK) ; 00335 00336 glBlitFramebufferEXT(0, 0, m_width, m_height, 00337 0, 0, m_width, m_height, 00338 GL_COLOR_BUFFER_BIT, GL_NEAREST) ; 00339 } 00340 else 00341 #else 00342 //#warning "FBO extension does not support blitting" 00343 #endif 00344 if (GLEW_EXT_framebuffer_object) 00345 { 00346 LRB_TRACE("copying pixels from off-screen buffer to back buffer...") ; 00347 00348 // Read pixels from off-screen buffer 00349 pixels() ; // stuffs pixel data into m_cache 00350 00351 // Copy above pixels to GL back (on-screen) buffer 00352 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0) ; 00353 glViewport(0, 0, m_width, m_height) ; 00354 00355 // Since pixel copying will go through the usual graphics pipeline, 00356 // we should reset the projection and modelview matrices so that 00357 // the matrices used for rendering to the off-screen buffer don't 00358 // mess up the scene that finally gets shown on-screen. 00359 glMatrixMode(GL_PROJECTION) ; 00360 glPushMatrix() ; 00361 glLoadIdentity() ; 00362 glOrtho(0, m_width, 0, m_height, -1, 1) ; 00363 00364 glMatrixMode(GL_MODELVIEW) ; 00365 glPushMatrix() ; 00366 glLoadIdentity() ; 00367 glRasterPos2i(0, 0) ; 00368 00369 glDrawPixels(m_width, m_height, GL_BGRA, GL_UNSIGNED_BYTE, m_cache) ; 00370 00371 // Restore matrices so that clients can remain blissfully unaware 00372 // of the fact that they are actually rendering to an off-screen 00373 // buffer... 00374 glMatrixMode(GL_PROJECTION) ; 00375 glPopMatrix() ; 00376 glMatrixMode(GL_MODELVIEW) ; 00377 glPopMatrix() ; 00378 } 00379 } 00380 00381 // Clean-up 00382 void RenderBuffer::clean_up() 00383 { 00384 if (GLEW_EXT_framebuffer_object) 00385 { 00386 LRB_TRACE("cleaning up off-screen buffer related GL resources") ; 00387 00388 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ; 00389 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0) ; 00390 00391 glDeleteRenderbuffersEXT(1, &m_rbo) ; 00392 glDeleteFramebuffersEXT (1, &m_fbo) ; 00393 } 00394 00395 delete[] m_cache ; m_cache = 0 ; 00396 } 00397 00398 RenderBuffer::~RenderBuffer() 00399 { 00400 clean_up() ; 00401 } 00402 00403 #endif // #ifndef INVT_HAVE_LIBGLEW 00404 00405 //----------------- FRAMEBUFFER OBJECTS NOT AVAILABLE ------------------- 00406 00407 #else // GL does not support framebuffer objects 00408 //#warning "GL does not support framebuffer objects" 00409 00410 // Initialization: since we don't have off-screen rendering, all 00411 // "off-screen" rendering is in fact simply redirected to the GL back 00412 // (on-screen) buffer. 00413 RenderBuffer::RenderBuffer(int W, int H) 00414 : m_width(W), m_height(H), 00415 m_size(W * H * 4), m_cache(new unsigned char[m_size]), m_dirty(true), 00416 m_fbo(0), m_rbo(0) 00417 { 00418 LRB_TRACE("no compile or run-time off-screen rendering support in GL") ; 00419 std::fill_n(m_cache, m_size, 0) ; 00420 } 00421 00422 // Nothing to setup because all rendering is on-screen 00423 void RenderBuffer::setup(){} 00424 00425 // Retrieve pixel data from back buffer 00426 // 00427 // NOTE: This can be problematic due to pixel ownership tests. For 00428 // example, if the Robolocust window is partially obscured by another, 00429 // the screen capture can be corrupt. But without framebuffer objects, we 00430 // have little recourse short of switching from GLUT to GLX and trying 00431 // our luck with something like the pbuffer extension... 00432 const unsigned char* RenderBuffer::pixels() const 00433 { 00434 if (m_dirty) 00435 { 00436 LRB_TRACE("retrieving back buffer's pixel data...") ; 00437 glReadBuffer(GL_BACK) ; 00438 glReadPixels(0, 0, m_width, m_height, 00439 GL_BGRA, GL_UNSIGNED_BYTE, m_cache) ; 00440 if (glGetError() == GL_NO_ERROR) 00441 m_dirty = false ; 00442 else 00443 std::fill_n(m_cache, m_size, 0) ; 00444 } 00445 else 00446 LRB_TRACE("returning cached off-screen buffer pixel data...") ; 00447 return m_cache ; 00448 } 00449 00450 // Nothing to do for copying off-screen buffer to on-screen buffer 00451 // because all rendering is already taking place in on-screen buffer. 00452 void RenderBuffer::to_screen(){} 00453 00454 // Clean-up 00455 void RenderBuffer::clean_up(){} 00456 00457 RenderBuffer::~RenderBuffer() 00458 { 00459 delete[] m_cache ; 00460 } 00461 00462 #endif // defined(GL_EXT_framebuffer_object) 00463 00464 //----------------------------------------------------------------------- 00465 00466 } // end of namespace encapsulating this file's definitions 00467 00468 #endif // #ifndef INVT_HAVE_LIBGL 00469 00470 /* So things look consistent in everyone's emacs... */ 00471 /* Local Variables: */ 00472 /* indent-tabs-mode: nil */ 00473 /* End: */