Panda3D

wglGraphicsBuffer.cxx

00001 // Filename: wglGraphicsBuffer.cxx
00002 // Created by:  drose (08Feb04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "wglGraphicsBuffer.h"
00016 #include "wglGraphicsPipe.h"
00017 #include "config_wgldisplay.h"
00018 #include "glgsg.h"
00019 #include "pStatTimer.h"
00020 
00021 #include <wingdi.h>
00022 
00023 TypeHandle wglGraphicsBuffer::_type_handle;
00024 
00025 
00026 ////////////////////////////////////////////////////////////////////
00027 //     Function: wglGraphicsBuffer::Constructor
00028 //       Access: Public
00029 //  Description:
00030 ////////////////////////////////////////////////////////////////////
00031 wglGraphicsBuffer::
00032 wglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
00033                   const string &name,
00034                   const FrameBufferProperties &fb_prop,
00035                   const WindowProperties &win_prop,
00036                   int flags,
00037                   GraphicsStateGuardian *gsg,
00038                   GraphicsOutput *host) :
00039   GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
00040 {
00041   _pbuffer = (HPBUFFERARB)0;
00042   _pbuffer_dc = (HDC)0;
00043   release_pbuffer();
00044   
00045   // Since the pbuffer never gets flipped, we get screenshots from the
00046   // same buffer we draw into.
00047   _screenshot_buffer_type = _draw_buffer_type;
00048 }
00049 
00050 ////////////////////////////////////////////////////////////////////
00051 //     Function: wglGraphicsBuffer::Destructor
00052 //       Access: Public, Virtual
00053 //  Description:
00054 ////////////////////////////////////////////////////////////////////
00055 wglGraphicsBuffer::
00056 ~wglGraphicsBuffer() {
00057 }
00058  
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: wglGraphicsBuffer::begin_frame
00061 //       Access: Public, Virtual
00062 //  Description: This function will be called within the draw thread
00063 //               before beginning rendering for a given frame.  It
00064 //               should do whatever setup is required, and return true
00065 //               if the frame should be rendered, or false if it
00066 //               should be skipped.
00067 ////////////////////////////////////////////////////////////////////
00068 bool wglGraphicsBuffer::
00069 begin_frame(FrameMode mode, Thread *current_thread) {
00070 
00071   begin_frame_spam(mode);
00072   if (_gsg == (GraphicsStateGuardian *)NULL) {
00073     return false;
00074   }
00075 
00076   wglGraphicsStateGuardian *wglgsg;
00077   DCAST_INTO_R(wglgsg, _gsg, false);
00078 
00079   if (_fb_properties.is_single_buffered()) {
00080     wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
00081   } else {
00082     wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
00083   }
00084 
00085   if (!rebuild_bitplanes()) {
00086     wglGraphicsPipe::wgl_make_current(0, 0, &_make_current_pcollector);
00087     return false;
00088   }
00089   
00090   wglGraphicsPipe::wgl_make_current(_pbuffer_dc, wglgsg->get_context(_pbuffer_dc),
00091                                     &_make_current_pcollector);
00092   
00093   if (mode == FM_render) {
00094     for (int i=0; i<count_textures(); i++) {
00095       if (get_texture_plane(i) != RTP_color) {
00096         if (get_rtm_mode(i) == RTM_bind_or_copy) {
00097           _textures[i]._rtm_mode = RTM_copy_texture;
00098         }
00099       }
00100     }
00101     clear_cube_map_selection();
00102   }
00103 
00104   _gsg->set_current_properties(&get_fb_properties());
00105   return _gsg->begin_frame(current_thread);
00106 }
00107 
00108 ////////////////////////////////////////////////////////////////////
00109 //     Function: wglGraphicsBuffer::end_frame
00110 //       Access: Public, Virtual
00111 //  Description: This function will be called within the draw thread
00112 //               after rendering is completed for a given frame.  It
00113 //               should do whatever finalization is required.
00114 ////////////////////////////////////////////////////////////////////
00115 void wglGraphicsBuffer::
00116 end_frame(FrameMode mode, Thread *current_thread) {
00117   end_frame_spam(mode);
00118   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
00119 
00120   if (mode == FM_render) {
00121     copy_to_textures();
00122     bind_texture_to_pbuffer();
00123   }
00124   
00125   _gsg->end_frame(current_thread);
00126   
00127   if (mode == FM_render) {
00128     trigger_flip();
00129     if (_one_shot) {
00130       prepare_for_deletion();
00131     }
00132     clear_cube_map_selection();
00133   }
00134 }
00135 
00136 ////////////////////////////////////////////////////////////////////
00137 //     Function: GraphicsOutput::bind_texture_to_pbuffer
00138 //       Access: Private
00139 //  Description: Looks for the appropriate texture,
00140 //               and binds that texture to the pbuffer.
00141 ////////////////////////////////////////////////////////////////////
00142 void wglGraphicsBuffer::
00143 bind_texture_to_pbuffer() {
00144   wglGraphicsStateGuardian *wglgsg;
00145   DCAST_INTO_V(wglgsg, _gsg);
00146 
00147   // Find the color texture, if there is one. That one can be bound to
00148   // the framebuffer.  All others must be marked RTM_copy_to_texture.
00149 
00150   int tex_index = -1;
00151   for (int i=0; i<count_textures(); i++) {
00152     if (get_texture_plane(i) == RTP_color) {
00153       tex_index = i;
00154       break;
00155     }
00156   }
00157 
00158   if (tex_index >= 0) {
00159     Texture *tex = get_texture(tex_index);
00160     if ((_pbuffer_bound != 0)&&(_pbuffer_bound != tex)) {
00161       _pbuffer_bound->release(wglgsg->get_prepared_objects());
00162       _pbuffer_bound = 0;
00163     }
00164     tex->set_size_padded(_x_size, _y_size);
00165     if (tex->get_match_framebuffer_format()) {
00166       if (_fb_properties.get_alpha_bits()) {
00167         tex->set_format(Texture::F_rgba);
00168       } else {
00169         tex->set_format(Texture::F_rgb);
00170       }
00171     }
00172     TextureContext *tc = tex->prepare_now(_gsg->get_prepared_objects(), _gsg);
00173     nassertv(tc != (TextureContext *)NULL);
00174     CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
00175     GLenum target = wglgsg->get_texture_target(tex->get_texture_type());
00176     if (target == GL_NONE) {
00177       _textures[tex_index]._rtm_mode = RTM_copy_texture;
00178       return;
00179     }
00180     GLP(BindTexture)(target, gtc->_index);
00181     if (_fb_properties.is_single_buffered()) {
00182       wglgsg->_wglBindTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
00183     } else {
00184       wglgsg->_wglBindTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
00185     }
00186     _pbuffer_bound = tex;
00187   } else {
00188     if (_pbuffer_bound != 0) {
00189       _pbuffer_bound->release(wglgsg->get_prepared_objects());
00190       _pbuffer_bound = 0;
00191     }
00192   }
00193 }
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: wglGraphicsBuffer::select_cube_map
00197 //       Access: Public, Virtual
00198 //  Description: Called internally when the window is in
00199 //               render-to-a-texture mode and we are in the process of
00200 //               rendering the six faces of a cube map.  This should
00201 //               do whatever needs to be done to switch the buffer to
00202 //               the indicated face.
00203 ////////////////////////////////////////////////////////////////////
00204 void wglGraphicsBuffer::
00205 select_cube_map(int cube_map_index) {
00206   wglGraphicsStateGuardian *wglgsg;
00207   DCAST_INTO_V(wglgsg, _gsg);
00208 
00209   nassertv(wglgsg->_wglSetPbufferAttribARB != NULL);
00210 
00211   static const int max_attrib_list = 64;
00212   int iattrib_list[max_attrib_list];
00213   int ni = 0;
00214 
00215   iattrib_list[ni++] = WGL_CUBE_MAP_FACE_ARB;
00216   iattrib_list[ni++] = WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cube_map_index;
00217 
00218   // Terminate the list.
00219   nassertv(ni <= max_attrib_list);
00220   iattrib_list[ni] = 0;
00221 
00222   wglgsg->_wglSetPbufferAttribARB(_pbuffer, iattrib_list);
00223 }
00224 
00225 ////////////////////////////////////////////////////////////////////
00226 //     Function: wglGraphicsBuffer::process_events
00227 //       Access: Public, Virtual
00228 //  Description: Do whatever processing is necessary to ensure that
00229 //               the window responds to user events.  Also, honor any
00230 //               requests recently made via request_properties()
00231 //
00232 //               This function is called only within the window
00233 //               thread.
00234 ////////////////////////////////////////////////////////////////////
00235 void wglGraphicsBuffer::
00236 process_events() {
00237   GraphicsBuffer::process_events();
00238 
00239   MSG msg;
00240     
00241   // Handle all the messages on the queue in a row.  Some of these
00242   // might be for another window, but they will get dispatched
00243   // appropriately.
00244   while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
00245     process_1_event();
00246   }
00247 }
00248 
00249 ////////////////////////////////////////////////////////////////////
00250 //     Function: wglGraphicsBuffer::close_buffer
00251 //       Access: Protected, Virtual
00252 //  Description: Closes the buffer right now.  Called from the window
00253 //               thread.
00254 ////////////////////////////////////////////////////////////////////
00255 void wglGraphicsBuffer::
00256 close_buffer() {
00257   if (_gsg != (GraphicsStateGuardian *)NULL) {
00258     wglGraphicsStateGuardian *wglgsg;
00259     DCAST_INTO_V(wglgsg, _gsg);
00260 
00261     _gsg.clear();
00262     _active = false;
00263   }
00264   
00265   release_pbuffer();
00266   
00267   _is_valid = false;
00268 }
00269 
00270 ////////////////////////////////////////////////////////////////////
00271 //     Function: wglGraphicsBuffer::open_buffer
00272 //       Access: Protected, Virtual
00273 //  Description: Opens the window right now.  Called from the window
00274 //               thread.  Returns true if the window is successfully
00275 //               opened, or false if there was a problem.
00276 ////////////////////////////////////////////////////////////////////
00277 bool wglGraphicsBuffer::
00278 open_buffer() {
00279 
00280   // pbuffers don't seem to work correctly in double-buffered
00281   // mode. Besides, the back buffer is a pointless waste of space.  
00282   // So always use a single-buffered gsg.
00283   
00284   _fb_properties.set_back_buffers(0);
00285   _draw_buffer_type = RenderBuffer::T_front;
00286   _screenshot_buffer_type = RenderBuffer::T_front;
00287   
00288   // GSG creation/initialization.
00289 
00290   wglGraphicsStateGuardian *wglgsg;
00291   if (_gsg == 0) {
00292     // There is no old gsg.  Create a new one.
00293     wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, NULL);
00294     wglgsg->choose_pixel_format(_fb_properties, true);
00295     _gsg = wglgsg;
00296   } else {
00297     // If the old gsg has the wrong pixel format, create a
00298     // new one that shares with the old gsg.
00299     DCAST_INTO_R(wglgsg, _gsg, false);
00300     if ((!wglgsg->get_fb_properties().subsumes(_fb_properties))||
00301         (!wglgsg->get_fb_properties().is_single_buffered())||
00302         (!wglgsg->pfnum_supports_pbuffer())) {
00303       wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, wglgsg);
00304       wglgsg->choose_pixel_format(_fb_properties, true);
00305       _gsg = wglgsg;
00306     }
00307   }
00308   
00309   // Use the temp window to initialize the gsg.
00310   
00311   HDC twindow_dc = wglgsg->get_twindow_dc();
00312   if (twindow_dc == 0) {
00313     // If we couldn't make a window, we can't get a GL context.
00314     return false;
00315   }
00316   wglGraphicsPipe::wgl_make_current(twindow_dc, wglgsg->get_context(twindow_dc),
00317                                     &_make_current_pcollector);
00318   wglgsg->reset_if_new();
00319   wglgsg->report_my_gl_errors();
00320   if (!wglgsg->get_fb_properties().verify_hardware_software
00321       (_fb_properties,wglgsg->get_gl_renderer())) {
00322     return false;
00323   }
00324   _fb_properties = wglgsg->get_fb_properties();
00325   
00326   // Now that we have fully made a window and used that window to
00327   // create a rendering context, we can attempt to create a pbuffer.
00328   // This might fail if the pbuffer extensions are not supported.
00329 
00330   if (!rebuild_bitplanes()) {
00331     wglGraphicsPipe::wgl_make_current(0, 0, &_make_current_pcollector);
00332     return false;
00333   }
00334   
00335   _is_valid = true;
00336 
00337   return true;
00338 }
00339 
00340 ////////////////////////////////////////////////////////////////////
00341 //     Function: wglGraphicsBuffer::release_pbuffer
00342 //       Access: Private
00343 //  Description: Destroys the pbuffer if it has been created.  The
00344 //               intent is that this may allow it to be recreated
00345 //               with different options.
00346 ////////////////////////////////////////////////////////////////////
00347 void wglGraphicsBuffer::
00348 release_pbuffer() {
00349   if (_gsg == 0) {
00350     return;
00351   }
00352   
00353   wglGraphicsStateGuardian *wglgsg;
00354   DCAST_INTO_V(wglgsg, _gsg);
00355 
00356   if (_pbuffer_bound != 0) {
00357     _pbuffer_bound->release(wglgsg->get_prepared_objects());
00358     _pbuffer_bound = 0;
00359   }
00360   wglGraphicsPipe::wgl_make_current(0, 0, NULL);
00361   if (_pbuffer_dc) {
00362     wglgsg->_wglReleasePbufferDCARB(_pbuffer, _pbuffer_dc);
00363   }
00364   if (_pbuffer) {
00365     wglgsg->_wglDestroyPbufferARB(_pbuffer);
00366   }
00367   _pbuffer = (HPBUFFERARB)0;
00368   _pbuffer_dc = (HDC)0;
00369   _pbuffer_mipmap = false;
00370   _pbuffer_sizex = 0;
00371   _pbuffer_sizey = 0;
00372   _pbuffer_type = Texture::TT_2d_texture;
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: wglGraphicsBuffer::rebuild_bitplanes
00377 //       Access: Private
00378 //  Description: Once the GL context has been fully realized, attempts
00379 //               to create an offscreen pbuffer if the graphics API
00380 //               supports it.  Returns true if successful, false on
00381 //               failure.
00382 ////////////////////////////////////////////////////////////////////
00383 bool wglGraphicsBuffer::
00384 rebuild_bitplanes() {
00385   wglGraphicsStateGuardian *wglgsg;
00386   DCAST_INTO_R(wglgsg, _gsg, false);
00387 
00388   if (!wglgsg->_supports_pbuffer) {
00389     wgldisplay_cat.info()
00390       << "PBuffers not supported by GL implementation.\n";
00391     return false;
00392   }
00393 
00394   // Find the texture to bind to the color buffer.
00395   Texture *bindtexture = NULL;
00396   for (int i=0; i<count_textures(); i++) {
00397     if ((get_rtm_mode(i) == RTM_bind_or_copy)&&
00398         (get_texture(i)->get_format() != Texture::F_depth_stencil)) {
00399       bindtexture = get_texture(i);
00400       break;
00401     }
00402   }
00403 
00404   // If we already have a pbuffer, and if it's lost, then 
00405   // force the rebuild.
00406 
00407   if (_pbuffer_dc) {
00408     int flag = 0;
00409     wglgsg->_wglQueryPbufferARB(_pbuffer, WGL_PBUFFER_LOST_ARB, &flag);
00410     if (flag != 0) {
00411       release_pbuffer();
00412     }
00413   }
00414   
00415   // Determine what pbuffer attributes are needed
00416   // for currently-applicable textures.
00417 
00418   if ((_host != 0)&&(_creation_flags & GraphicsPipe::BF_size_track_host)) {
00419     if ((_host->get_x_size() != _x_size)||
00420         (_host->get_y_size() != _y_size)) {
00421       set_size_and_recalc(_host->get_x_size(),
00422                           _host->get_y_size());
00423     }
00424   }
00425   int desired_x = _x_size;
00426   int desired_y = _y_size;
00427   if ((bindtexture != 0)&&(Texture::get_textures_power_2() != ATS_none)) {
00428     desired_x = Texture::up_to_power_2(desired_x);
00429     desired_y = Texture::up_to_power_2(desired_y);
00430   }
00431   bool desired_mipmap = false;
00432   Texture::TextureType desired_type = Texture::TT_2d_texture;
00433   if (bindtexture != 0) {
00434     desired_mipmap = bindtexture->uses_mipmaps();
00435     desired_type = bindtexture->get_texture_type();
00436   }
00437 
00438   if ((_pbuffer != 0)&&
00439       (_pbuffer_sizex == desired_x)&&
00440       (_pbuffer_sizey == desired_y)&&
00441       (_pbuffer_mipmap == desired_mipmap)&&
00442       (_pbuffer_type == desired_type)) {
00443     // the pbuffer we already have is fine. Do not rebuild.
00444     return true;
00445   }
00446 
00447   // Release the old pbuffer, if there was one.
00448   
00449   release_pbuffer();
00450 
00451   // Allocate the new pbuffer.
00452 
00453   int pfnum = wglgsg->get_pfnum();
00454 
00455   static const int max_attrib_list = 64;
00456   int iattrib_list[max_attrib_list];
00457   int ni = 0;
00458   
00459   if (_fb_properties.get_alpha_bits()) {
00460     iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
00461     iattrib_list[ni++] = WGL_TEXTURE_RGBA_ARB;
00462   } else {
00463     iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
00464     iattrib_list[ni++] = WGL_TEXTURE_RGB_ARB;
00465   }
00466 
00467   if (desired_mipmap) {
00468     iattrib_list[ni++] = WGL_MIPMAP_TEXTURE_ARB;
00469     iattrib_list[ni++] = 1;
00470   }
00471 
00472   switch (desired_type) {
00473   case Texture::TT_cube_map:
00474     iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
00475     iattrib_list[ni++] = WGL_TEXTURE_CUBE_MAP_ARB;
00476     break;
00477     
00478   case Texture::TT_1d_texture:
00479     iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
00480     iattrib_list[ni++] = WGL_TEXTURE_1D_ARB;
00481     break;
00482     
00483   default:
00484     iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
00485     iattrib_list[ni++] = WGL_TEXTURE_2D_ARB;
00486   }
00487   
00488   // Terminate the list.
00489   nassertr(ni <= max_attrib_list, false);
00490   iattrib_list[ni] = 0;
00491 
00492   HDC twindow_dc = wglgsg->get_twindow_dc();
00493   if (twindow_dc == 0) {
00494     return false;
00495   }
00496   
00497   wglGraphicsPipe::wgl_make_current(twindow_dc, wglgsg->get_context(twindow_dc),
00498                                     &_make_current_pcollector);
00499 
00500   _pbuffer = wglgsg->_wglCreatePbufferARB(twindow_dc, pfnum, 
00501                                           desired_x, desired_y, iattrib_list);
00502   
00503   if (_pbuffer == 0) {
00504     wgldisplay_cat.info()
00505       << "Attempt to create pbuffer failed.\n";
00506     return false;
00507   }
00508 
00509   _pbuffer_dc = wglgsg->_wglGetPbufferDCARB(_pbuffer);
00510   _pbuffer_mipmap = desired_mipmap;
00511   _pbuffer_type = desired_type;
00512   _pbuffer_sizex = desired_x;
00513   _pbuffer_sizey = desired_y;
00514   
00515   return true;
00516 }
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: wglGraphicsBuffer::process_1_event
00520 //       Access: Private, Static
00521 //  Description: Handles one event from the message queue.
00522 ////////////////////////////////////////////////////////////////////
00523 void wglGraphicsBuffer::
00524 process_1_event() {
00525   MSG msg;
00526 
00527   if (!GetMessage(&msg, NULL, 0, 0)) {
00528     // WM_QUIT received.  We need a cleaner way to deal with this.
00529     //    DestroyAllWindows(false);
00530     exit(msg.wParam);  // this will invoke AtExitFn
00531   }
00532 
00533   // Translate virtual key messages
00534   TranslateMessage(&msg);
00535   // Call window_proc
00536   DispatchMessage(&msg);
00537 }
00538 
00539 
 All Classes Functions Variables Enumerations