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