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