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