Panda3D
|
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