Panda3D
|
00001 // Filename: graphicsEngine.cxx 00002 // Created by: drose (24Feb02) 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 "graphicsEngine.h" 00016 #include "graphicsPipe.h" 00017 #include "parasiteBuffer.h" 00018 #include "config_gobj.h" 00019 #include "config_display.h" 00020 #include "pipeline.h" 00021 #include "drawCullHandler.h" 00022 #include "binCullHandler.h" 00023 #include "cullResult.h" 00024 #include "cullTraverser.h" 00025 #include "clockObject.h" 00026 #include "pStatTimer.h" 00027 #include "pStatClient.h" 00028 #include "pStatCollector.h" 00029 #include "mutexHolder.h" 00030 #include "lightReMutexHolder.h" 00031 #include "cullFaceAttrib.h" 00032 #include "string_utils.h" 00033 #include "geomCacheManager.h" 00034 #include "renderState.h" 00035 #include "transformState.h" 00036 #include "thread.h" 00037 #include "pipeline.h" 00038 #include "throw_event.h" 00039 #include "bamCache.h" 00040 #include "cullableObject.h" 00041 #include "geomVertexArrayData.h" 00042 #include "vertexDataSaveFile.h" 00043 #include "vertexDataBook.h" 00044 #include "vertexDataPage.h" 00045 #include "config_pgraph.h" 00046 #include "displayRegionCullCallbackData.h" 00047 #include "displayRegionDrawCallbackData.h" 00048 00049 #if defined(WIN32) 00050 #define WINDOWS_LEAN_AND_MEAN 00051 #include <WinSock2.h> 00052 #include <wtypes.h> 00053 #undef WINDOWS_LEAN_AND_MEAN 00054 #else 00055 #include <sys/time.h> 00056 #endif 00057 00058 PT(GraphicsEngine) GraphicsEngine::_global_ptr; 00059 00060 PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync"); 00061 PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle"); 00062 PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General"); 00063 PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame"); 00064 PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame"); 00065 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield"); 00066 PStatCollector GraphicsEngine::_cull_pcollector("Cull"); 00067 PStatCollector GraphicsEngine::_cull_setup_pcollector("Cull:Setup"); 00068 PStatCollector GraphicsEngine::_cull_sort_pcollector("Cull:Sort"); 00069 PStatCollector GraphicsEngine::_draw_pcollector("Draw"); 00070 PStatCollector GraphicsEngine::_sync_pcollector("Draw:Sync"); 00071 PStatCollector GraphicsEngine::_flip_pcollector("Wait:Flip"); 00072 PStatCollector GraphicsEngine::_flip_begin_pcollector("Wait:Flip:Begin"); 00073 PStatCollector GraphicsEngine::_flip_end_pcollector("Wait:Flip:End"); 00074 PStatCollector GraphicsEngine::_transform_states_pcollector("TransformStates"); 00075 PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformStates:Unused"); 00076 PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates"); 00077 PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused"); 00078 PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers"); 00079 PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers"); 00080 PStatCollector GraphicsEngine::_delete_pcollector("App:Delete"); 00081 00082 00083 PStatCollector GraphicsEngine::_sw_sprites_pcollector("SW Sprites"); 00084 PStatCollector GraphicsEngine::_vertex_data_small_pcollector("Vertex Data:Small"); 00085 PStatCollector GraphicsEngine::_vertex_data_independent_pcollector("Vertex Data:Independent"); 00086 PStatCollector GraphicsEngine::_vertex_data_pending_pcollector("Vertex Data:Pending"); 00087 PStatCollector GraphicsEngine::_vertex_data_resident_pcollector("Vertex Data:Resident"); 00088 PStatCollector GraphicsEngine::_vertex_data_compressed_pcollector("Vertex Data:Compressed"); 00089 PStatCollector GraphicsEngine::_vertex_data_unused_disk_pcollector("Vertex Data:Disk:Unused"); 00090 PStatCollector GraphicsEngine::_vertex_data_used_disk_pcollector("Vertex Data:Disk:Used"); 00091 00092 // These are counted independently by the collision system; we 00093 // redefine them here so we can reset them at each frame. 00094 PStatCollector GraphicsEngine::_cnode_volume_pcollector("Collision Volumes:CollisionNode"); 00095 PStatCollector GraphicsEngine::_gnode_volume_pcollector("Collision Volumes:GeomNode"); 00096 PStatCollector GraphicsEngine::_geom_volume_pcollector("Collision Volumes:Geom"); 00097 PStatCollector GraphicsEngine::_node_volume_pcollector("Collision Volumes:PandaNode"); 00098 PStatCollector GraphicsEngine::_volume_pcollector("Collision Volumes:CollisionSolid"); 00099 PStatCollector GraphicsEngine::_test_pcollector("Collision Tests:CollisionSolid"); 00100 PStatCollector GraphicsEngine::_volume_polygon_pcollector("Collision Volumes:CollisionPolygon"); 00101 PStatCollector GraphicsEngine::_test_polygon_pcollector("Collision Tests:CollisionPolygon"); 00102 PStatCollector GraphicsEngine::_volume_plane_pcollector("Collision Volumes:CollisionPlane"); 00103 PStatCollector GraphicsEngine::_test_plane_pcollector("Collision Tests:CollisionPlane"); 00104 PStatCollector GraphicsEngine::_volume_sphere_pcollector("Collision Volumes:CollisionSphere"); 00105 PStatCollector GraphicsEngine::_test_sphere_pcollector("Collision Tests:CollisionSphere"); 00106 PStatCollector GraphicsEngine::_volume_tube_pcollector("Collision Volumes:CollisionTube"); 00107 PStatCollector GraphicsEngine::_test_tube_pcollector("Collision Tests:CollisionTube"); 00108 PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:CollisionInvSphere"); 00109 PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere"); 00110 PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom"); 00111 PStatCollector GraphicsEngine::_test_geom_pcollector("Collision Tests:CollisionGeom"); 00112 PStatCollector GraphicsEngine::_occlusion_untested_pcollector("Occlusion results:Not tested"); 00113 PStatCollector GraphicsEngine::_occlusion_passed_pcollector("Occlusion results:Visible"); 00114 PStatCollector GraphicsEngine::_occlusion_failed_pcollector("Occlusion results:Occluded"); 00115 PStatCollector GraphicsEngine::_occlusion_tests_pcollector("Occlusion tests"); 00116 00117 //////////////////////////////////////////////////////////////////// 00118 // Function: GraphicsEngine::Constructor 00119 // Access: Published 00120 // Description: Creates a new GraphicsEngine object. The Pipeline is 00121 // normally left to default to NULL, which indicates the 00122 // global render pipeline, but it may be any Pipeline 00123 // you choose. 00124 //////////////////////////////////////////////////////////////////// 00125 GraphicsEngine:: 00126 GraphicsEngine(Pipeline *pipeline) : 00127 _pipeline(pipeline), 00128 _app("app"), 00129 _lock("GraphicsEngine::_lock") 00130 { 00131 if (_pipeline == (Pipeline *)NULL) { 00132 _pipeline = Pipeline::get_render_pipeline(); 00133 } 00134 00135 _windows_sorted = true; 00136 _window_sort_index = 0; 00137 _needs_open_windows = false; 00138 00139 set_threading_model(GraphicsThreadingModel(threading_model)); 00140 if (!_threading_model.is_default()) { 00141 display_cat.info() 00142 << "Using threading model " << _threading_model << "\n"; 00143 } 00144 _auto_flip = auto_flip; 00145 _portal_enabled = false; 00146 _flip_state = FS_flip; 00147 00148 _singular_warning_last_frame = false; 00149 _singular_warning_this_frame = false; 00150 } 00151 00152 //////////////////////////////////////////////////////////////////// 00153 // Function: GraphicsEngine::Destructor 00154 // Access: Published 00155 // Description: Gracefully cleans up the graphics engine and its 00156 // related threads and windows. 00157 //////////////////////////////////////////////////////////////////// 00158 GraphicsEngine:: 00159 ~GraphicsEngine() { 00160 #ifdef DO_PSTATS 00161 if (_app_pcollector.is_started()) { 00162 _app_pcollector.stop(); 00163 } 00164 #endif 00165 00166 remove_all_windows(); 00167 } 00168 00169 //////////////////////////////////////////////////////////////////// 00170 // Function: GraphicsEngine::set_threading_model 00171 // Access: Published 00172 // Description: Specifies how future objects created via make_gsg(), 00173 // make_buffer(), and make_window() will be threaded. 00174 // This does not affect any already-created objects. 00175 //////////////////////////////////////////////////////////////////// 00176 void GraphicsEngine:: 00177 set_threading_model(const GraphicsThreadingModel &threading_model) { 00178 if (!Thread::is_threading_supported()) { 00179 if (!threading_model.is_single_threaded()) { 00180 display_cat.warning() 00181 << "Threading model " << threading_model 00182 << " requested but threading is not available. Ignoring.\n"; 00183 return; 00184 } 00185 } 00186 00187 #ifndef THREADED_PIPELINE 00188 if (!threading_model.is_single_threaded()) { 00189 display_cat.warning() 00190 << "Threading model " << threading_model 00191 << " requested but multithreaded render pipelines not enabled in build.\n"; 00192 if (!allow_nonpipeline_threads) { 00193 display_cat.warning() 00194 << "Ignoring requested threading model.\n"; 00195 return; 00196 } 00197 display_cat.warning() 00198 << "Danger! Creating requested render threads anyway!\n"; 00199 } 00200 #endif // THREADED_PIPELINE 00201 LightReMutexHolder holder(_lock); 00202 _threading_model = threading_model; 00203 } 00204 00205 //////////////////////////////////////////////////////////////////// 00206 // Function: GraphicsEngine::get_threading_model 00207 // Access: Published 00208 // Description: Returns the threading model that will be applied to 00209 // future objects. See set_threading_model(). 00210 //////////////////////////////////////////////////////////////////// 00211 GraphicsThreadingModel GraphicsEngine:: 00212 get_threading_model() const { 00213 GraphicsThreadingModel result; 00214 { 00215 LightReMutexHolder holder(_lock); 00216 result = _threading_model; 00217 } 00218 return result; 00219 } 00220 00221 // THIS IS THE OLD CODE FOR make_gsg 00222 // PT(GraphicsStateGuardian) gsg = pipe->make_gsg(properties, share_with); 00223 00224 00225 00226 //////////////////////////////////////////////////////////////////// 00227 // Function: GraphicsEngine::make_output 00228 // Access: Published 00229 // Description: Creates a new window (or buffer) and returns it. 00230 // The GraphicsEngine becomes the owner of the window, 00231 // it will persist at least until remove_window() is 00232 // called later. 00233 // 00234 // If a null pointer is supplied for the gsg, then this 00235 // routine will create a new gsg. 00236 // 00237 // This routine is only called from the app thread. 00238 //////////////////////////////////////////////////////////////////// 00239 00240 GraphicsOutput *GraphicsEngine:: 00241 make_output(GraphicsPipe *pipe, 00242 const string &name, int sort, 00243 const FrameBufferProperties &fb_prop, 00244 const WindowProperties &win_prop, 00245 int flags, 00246 GraphicsStateGuardian *gsg, 00247 GraphicsOutput *host) { 00248 00249 // The code here is tricky because the gsg that is passed in 00250 // might be in the uninitialized state. As a result, 00251 // pipe::make_output may not be able to tell which DirectX 00252 // capabilities or OpenGL extensions are supported and which 00253 // are not. Worse yet, it can't query the API, because that 00254 // can only be done from the draw thread, and this is the app 00255 // thread. 00256 // 00257 // So here's the workaround: this routine calls pipe::make_output, 00258 // which returns a "non-certified" window. That means that 00259 // the pipe doesn't promise that the draw thread will actually 00260 // succeed in initializing the window. This routine then calls 00261 // open_windows, which attempts to initialize the window. 00262 // 00263 // If open_windows fails to initialize the window, then 00264 // this routine will ask pipe::make_output to try again, this 00265 // time using a different set of OpenGL extensions or DirectX 00266 // capabilities. This is what the "retry" parameter to 00267 // pipe::make_output is for - it specifies, in an abstract 00268 // manner, which set of capabilties/extensions to try. 00269 // 00270 // The only problem with this design is that it requires the 00271 // engine to call open_windows, which is slow. To make 00272 // things faster, the pipe can choose to "precertify" 00273 // its creations. If it chooses to do so, this is a guarantee 00274 // that the windows it returns will not fail in open_windows. 00275 // However, most graphics pipes will only precertify if you 00276 // pass them an already-initialized gsg. Long story short, 00277 // if you want make_output to be fast, use an 00278 // already-initialized gsg. 00279 00280 // Simplify the input parameters. 00281 00282 int x_size = 0, y_size = 0; 00283 if (win_prop.has_size()) { 00284 x_size = win_prop.get_x_size(); 00285 y_size = win_prop.get_y_size(); 00286 } 00287 if ((x_size == 0)&&(y_size == 0)) { 00288 flags |= GraphicsPipe::BF_size_track_host; 00289 } 00290 if (host != 0) { 00291 host = host->get_host(); 00292 } 00293 00294 // If a gsg or host was supplied, and either is not yet initialized, 00295 // then call open_windows to get both ready. If that fails, 00296 // give up on using the supplied gsg and host. 00297 00298 if (host == (GraphicsOutput *)NULL) { 00299 if (gsg != (GraphicsStateGuardian*)NULL) { 00300 if ((!gsg->is_valid())||(gsg->needs_reset())) { 00301 open_windows(); 00302 } 00303 if ((!gsg->is_valid())||(gsg->needs_reset())) { 00304 gsg = NULL; 00305 } 00306 } 00307 } else { 00308 if ((host->get_gsg()==0)|| 00309 (!host->is_valid())|| 00310 (!host->get_gsg()->is_valid())|| 00311 (host->get_gsg()->needs_reset())) { 00312 open_windows(); 00313 } 00314 if ((host->get_gsg()==0)|| 00315 (!host->is_valid())|| 00316 (!host->get_gsg()->is_valid())|| 00317 (host->get_gsg()->needs_reset())) { 00318 host = NULL; 00319 gsg = NULL; 00320 } else { 00321 gsg = host->get_gsg(); 00322 } 00323 } 00324 00325 // Sanity check everything. 00326 00327 GraphicsThreadingModel threading_model = get_threading_model(); 00328 nassertr(pipe != (GraphicsPipe *)NULL, NULL); 00329 if (gsg != (GraphicsStateGuardian *)NULL) { 00330 nassertr(pipe == gsg->get_pipe(), NULL); 00331 nassertr(this == gsg->get_engine(), NULL); 00332 nassertr(threading_model.get_draw_name() == 00333 gsg->get_threading_model().get_draw_name(), NULL); 00334 } 00335 00336 // Determine if a parasite buffer meets the user's specs. 00337 00338 bool can_use_parasite = false; 00339 if ((host != 0)&& 00340 ((flags&GraphicsPipe::BF_require_window)==0)&& 00341 ((flags&GraphicsPipe::BF_refuse_parasite)==0)&& 00342 ((flags&GraphicsPipe::BF_can_bind_color)==0)&& 00343 ((flags&GraphicsPipe::BF_can_bind_every)==0)&& 00344 ((flags&GraphicsPipe::BF_rtt_cumulative)==0)) { 00345 if ((flags&GraphicsPipe::BF_fb_props_optional) || 00346 (host->get_fb_properties().subsumes(fb_prop))) { 00347 can_use_parasite = true; 00348 } 00349 } 00350 00351 // If parasite buffers are preferred, then try a parasite first. 00352 // Even if prefer-parasite-buffer is set, parasites are not preferred 00353 // if the host window is too small, or if the host window does not 00354 // have the requested properties. 00355 00356 if ((prefer_parasite_buffer) && 00357 (can_use_parasite) && 00358 (x_size <= host->get_x_size())&& 00359 (y_size <= host->get_y_size())&& 00360 (host->get_fb_properties().subsumes(fb_prop))) { 00361 ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); 00362 buffer->_sort = sort; 00363 do_add_window(buffer, threading_model); 00364 do_add_gsg(host->get_gsg(), pipe, threading_model); 00365 return buffer; 00366 } 00367 00368 // If force-parasite-buffer is set, we create a parasite buffer even 00369 // if it's less than ideal. You might set this if you really don't 00370 // trust your graphics driver's support for offscreen buffers. 00371 if (force_parasite_buffer && can_use_parasite) { 00372 ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); 00373 buffer->_sort = sort; 00374 do_add_window(buffer, threading_model); 00375 do_add_gsg(host->get_gsg(), pipe, threading_model); 00376 return buffer; 00377 } 00378 00379 // Ask the pipe to create a window. 00380 00381 for (int retry=0; retry<10; retry++) { 00382 bool precertify = false; 00383 PT(GraphicsOutput) window = 00384 pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify); 00385 if (window != (GraphicsOutput *)NULL) { 00386 window->_sort = sort; 00387 if ((precertify) && (gsg != 0) && (window->get_gsg()==gsg)) { 00388 do_add_window(window, threading_model); 00389 do_add_gsg(window->get_gsg(), pipe, threading_model); 00390 return window; 00391 } 00392 do_add_window(window, threading_model); 00393 open_windows(); 00394 if (window->is_valid()) { 00395 do_add_gsg(window->get_gsg(), pipe, threading_model); 00396 if (window->get_fb_properties().subsumes(fb_prop)) { 00397 return window; 00398 } else { 00399 if (flags & GraphicsPipe::BF_fb_props_optional) { 00400 display_cat.warning() 00401 << "FrameBufferProperties available less than requested.\n"; 00402 return window; 00403 } 00404 display_cat.error() 00405 << "Could not get requested FrameBufferProperties; abandoning window.\n"; 00406 display_cat.error(false) 00407 << " requested: " << fb_prop << "\n" 00408 << " got: " << window->get_fb_properties() << "\n"; 00409 } 00410 } else { 00411 display_cat.info() 00412 << window->get_type() << " wouldn't open; abandoning.\n"; 00413 } 00414 00415 // No good; delete the window and keep trying. 00416 bool removed = remove_window(window); 00417 nassertr(removed, NULL); 00418 } 00419 } 00420 00421 // Parasite buffers were not preferred, but the pipe could not 00422 // create a window to the user's specs. Try a parasite as a 00423 // last hope. 00424 00425 if (can_use_parasite) { 00426 ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); 00427 buffer->_sort = sort; 00428 do_add_window(buffer, threading_model); 00429 do_add_gsg(host->get_gsg(), pipe, threading_model); 00430 return buffer; 00431 } 00432 00433 // Could not create a window to the user's specs. 00434 00435 return NULL; 00436 } 00437 00438 //////////////////////////////////////////////////////////////////// 00439 // Function: GraphicsEngine::remove_window 00440 // Access: Published 00441 // Description: Removes the indicated window or offscreen buffer from 00442 // the set of windows that will be processed when 00443 // render_frame() is called. This also closes the 00444 // window if it is open, and removes the window from its 00445 // GraphicsPipe, allowing the window to be destructed if 00446 // there are no other references to it. (However, the 00447 // window may not be actually closed until next frame, 00448 // if it is controlled by a sub-thread.) 00449 // 00450 // The return value is true if the window was removed, 00451 // false if it was not found. 00452 // 00453 // Unlike remove_all_windows(), this function does not 00454 // terminate any of the threads that may have been 00455 // started to service this window; they are left running 00456 // (since you might open a new window later on these 00457 // threads). If your intention is to clean up before 00458 // shutting down, it is better to call 00459 // remove_all_windows() then to call remove_window() one 00460 // at a time. 00461 //////////////////////////////////////////////////////////////////// 00462 bool GraphicsEngine:: 00463 remove_window(GraphicsOutput *window) { 00464 nassertr(window != NULL, false); 00465 Thread *current_thread = Thread::get_current_thread(); 00466 00467 // First, make sure we know what this window is. 00468 PT(GraphicsOutput) ptwin = window; 00469 size_t count; 00470 { 00471 LightReMutexHolder holder(_lock, current_thread); 00472 if (!_windows_sorted) { 00473 do_resort_windows(); 00474 } 00475 count = _windows.erase(ptwin); 00476 } 00477 if (count == 0) { 00478 // Never heard of this window. Do nothing. 00479 return false; 00480 } 00481 00482 do_remove_window(window, current_thread); 00483 00484 GraphicsStateGuardian *gsg = window->get_gsg(); 00485 if (gsg != (GraphicsStateGuardian *)NULL) { 00486 PreparedGraphicsObjects *pgo = gsg->get_prepared_objects(); 00487 if (pgo != (PreparedGraphicsObjects *)NULL) { 00488 // Check to see if any other still-active windows share this 00489 // context. 00490 bool any_common = false; 00491 { 00492 LightReMutexHolder holder(_lock, current_thread); 00493 Windows::iterator wi; 00494 for (wi = _windows.begin(); wi != _windows.end() && !any_common; ++wi) { 00495 GraphicsStateGuardian *gsg2 = (*wi)->get_gsg(); 00496 if (gsg2 != (GraphicsStateGuardian *)NULL && 00497 gsg2->get_prepared_objects() == pgo) { 00498 any_common = true; 00499 } 00500 } 00501 } 00502 if (!any_common) { 00503 // If no windows still use this context, release all textures, 00504 // etc. We do this in case there is a floating pointer 00505 // somewhere keeping the GSG from destructing when its window 00506 // goes away. A leaked GSG pointer is bad enough, but there's 00507 // no reason we also need to keep around all of the objects 00508 // allocated on graphics memory. 00509 pgo->release_all(); 00510 } 00511 } 00512 } 00513 00514 nassertr(count == 1, true); 00515 return true; 00516 } 00517 00518 //////////////////////////////////////////////////////////////////// 00519 // Function: GraphicsEngine::remove_all_windows 00520 // Access: Published 00521 // Description: Removes and closes all windows from the engine. This 00522 // also cleans up and terminates any threads that have 00523 // been started to service those windows. 00524 //////////////////////////////////////////////////////////////////// 00525 void GraphicsEngine:: 00526 remove_all_windows() { 00527 Thread *current_thread = Thread::get_current_thread(); 00528 00529 // Let's move the _windows vector into a local copy first, and walk 00530 // through that local copy, just in case someone we call during the 00531 // loop attempts to modify _windows. I don't know what code would 00532 // be doing this, but it appeared to be happening, and this worked 00533 // around it. 00534 Windows old_windows; 00535 old_windows.swap(_windows); 00536 Windows::iterator wi; 00537 for (wi = old_windows.begin(); wi != old_windows.end(); ++wi) { 00538 GraphicsOutput *win = (*wi); 00539 nassertv(win != NULL); 00540 do_remove_window(win, current_thread); 00541 GraphicsStateGuardian *gsg = win->get_gsg(); 00542 if (gsg != (GraphicsStateGuardian *)NULL) { 00543 gsg->release_all(); 00544 } 00545 } 00546 00547 _app.do_close(this, current_thread); 00548 _app.do_pending(this, current_thread); 00549 terminate_threads(current_thread); 00550 00551 // It seems a safe assumption that we're about to exit the 00552 // application or otherwise shut down Panda. Although it's a bit of 00553 // a hack, since it's not really related to removing windows, this 00554 // would nevertheless be a fine time to ensure the model cache (if 00555 // any) has been flushed to disk. 00556 BamCache *cache = BamCache::get_global_ptr(); 00557 cache->flush_index(); 00558 00559 // And, hey, let's stop the vertex paging threads, if any. 00560 VertexDataPage::stop_threads(); 00561 00562 AsyncTaskManager::get_global_ptr()->stop_threads(); 00563 00564 #ifdef DO_PSTATS 00565 PStatClient::get_global_pstats()->disconnect(); 00566 #endif 00567 00568 // Well, and why not clean up all threads here? 00569 Thread::prepare_for_exit(); 00570 } 00571 00572 //////////////////////////////////////////////////////////////////// 00573 // Function: GraphicsEngine::reset_all_windows 00574 // Access: Published 00575 // Description: Resets the framebuffer of the current window. This 00576 // is currently used by DirectX 8 only. It calls a 00577 // reset_window function on each active window to 00578 // release/create old/new framebuffer 00579 //////////////////////////////////////////////////////////////////// 00580 void GraphicsEngine:: 00581 reset_all_windows(bool swapchain) { 00582 Windows::iterator wi; 00583 for (wi = _windows.begin(); wi != _windows.end(); ++wi) { 00584 GraphicsOutput *win = (*wi); 00585 // if (win->is_active()) 00586 win->reset_window(swapchain); 00587 } 00588 } 00589 00590 //////////////////////////////////////////////////////////////////// 00591 // Function: GraphicsEngine::is_empty 00592 // Access: Published 00593 // Description: Returns true if there are no windows or buffers 00594 // managed by the engine, false if there is at least 00595 // one. 00596 //////////////////////////////////////////////////////////////////// 00597 bool GraphicsEngine:: 00598 is_empty() const { 00599 return _windows.empty(); 00600 } 00601 00602 //////////////////////////////////////////////////////////////////// 00603 // Function: GraphicsEngine::get_num_windows 00604 // Access: Published 00605 // Description: Returns the number of windows (or buffers) managed by 00606 // the engine. 00607 //////////////////////////////////////////////////////////////////// 00608 int GraphicsEngine:: 00609 get_num_windows() const { 00610 return _windows.size(); 00611 } 00612 00613 //////////////////////////////////////////////////////////////////// 00614 // Function: GraphicsEngine::get_window 00615 // Access: Published 00616 // Description: Returns the nth window or buffers managed by the 00617 // engine, in sorted order. 00618 //////////////////////////////////////////////////////////////////// 00619 GraphicsOutput *GraphicsEngine:: 00620 get_window(int n) const { 00621 nassertr(n >= 0 && n < (int)_windows.size(), NULL); 00622 00623 if (!_windows_sorted) { 00624 ((GraphicsEngine *)this)->do_resort_windows(); 00625 } 00626 return _windows[n]; 00627 } 00628 00629 //////////////////////////////////////////////////////////////////// 00630 // Function: GraphicsEngine::render_frame 00631 // Access: Published 00632 // Description: Renders the next frame in all the registered windows, 00633 // and flips all of the frame buffers. 00634 //////////////////////////////////////////////////////////////////// 00635 void GraphicsEngine:: 00636 render_frame() { 00637 Thread *current_thread = Thread::get_current_thread(); 00638 00639 // Since this gets called every frame, we should take advantage of 00640 // the opportunity to flush the cache if necessary. 00641 BamCache *cache = BamCache::get_global_ptr(); 00642 cache->consider_flush_index(); 00643 00644 // Anything that happens outside of GraphicsEngine::render_frame() 00645 // is deemed to be App. 00646 #ifdef DO_PSTATS 00647 _render_frame_pcollector.start(); 00648 if (_app_pcollector.is_started()) { 00649 _app_pcollector.stop(); 00650 } 00651 #endif 00652 00653 if (_needs_open_windows) { 00654 // Make sure our buffers and windows are fully realized before we 00655 // render a frame. We do this particularly to realize our 00656 // offscreen buffers, so that we don't render a frame before the 00657 // offscreen buffers are ready (which might result in a frame 00658 // going by without some textures having been rendered). 00659 open_windows(); 00660 } 00661 00662 ClockObject *global_clock = ClockObject::get_global_clock(); 00663 00664 if (display_cat.is_spam()) { 00665 display_cat.spam() 00666 << "render_frame() - frame " << global_clock->get_frame_count() << "\n"; 00667 } 00668 00669 { 00670 LightReMutexHolder holder(_lock, current_thread); 00671 00672 if (!_windows_sorted) { 00673 do_resort_windows(); 00674 } 00675 00676 if (sync_flip && _flip_state != FS_flip) { 00677 do_flip_frame(current_thread); 00678 } 00679 00680 // Are any of the windows ready to be deleted? 00681 Windows new_windows; 00682 new_windows.reserve(_windows.size()); 00683 Windows::iterator wi; 00684 for (wi = _windows.begin(); wi != _windows.end(); ++wi) { 00685 GraphicsOutput *win = (*wi); 00686 nassertv(win != NULL); 00687 if (win->get_delete_flag()) { 00688 do_remove_window(win, current_thread); 00689 00690 } else { 00691 new_windows.push_back(win); 00692 00693 // Let's calculate each scene's bounding volume here in App, 00694 // before we cycle the pipeline. The cull traversal will 00695 // calculate it anyway, but if we calculate it in App first 00696 // before it gets calculated in the Cull thread, it will be more 00697 // likely to stick for subsequent frames, so we won't have to 00698 // recompute it each frame. 00699 int num_drs = win->get_num_active_display_regions(); 00700 for (int i = 0; i < num_drs; ++i) { 00701 DisplayRegion *dr = win->get_active_display_region(i); 00702 if (dr != (DisplayRegion *)NULL) { 00703 NodePath camera_np = dr->get_camera(current_thread); 00704 if (!camera_np.is_empty()) { 00705 Camera *camera = DCAST(Camera, camera_np.node()); 00706 NodePath scene = camera->get_scene(); 00707 if (scene.is_empty()) { 00708 scene = camera_np.get_top(current_thread); 00709 } 00710 if (!scene.is_empty()) { 00711 scene.get_bounds(current_thread); 00712 } 00713 } 00714 } 00715 } 00716 } 00717 } 00718 _windows.swap(new_windows); 00719 00720 // Go ahead and release any textures' ram images for textures that 00721 // were drawn in the previous frame. 00722 LoadedTextures::iterator lti; 00723 for (lti = _loaded_textures.begin(); lti != _loaded_textures.end(); ++lti) { 00724 LoadedTexture < = (*lti); 00725 if (lt._tex->get_image_modified() == lt._image_modified) { 00726 lt._tex->texture_uploaded(); 00727 } 00728 } 00729 _loaded_textures.clear(); 00730 00731 // Now it's time to do any drawing from the main frame--after all of 00732 // the App code has executed, but before we begin the next frame. 00733 _app.do_frame(this, current_thread); 00734 00735 // Grab each thread's mutex again after all windows have flipped, 00736 // and wait for the thread to finish. 00737 { 00738 PStatTimer timer(_wait_pcollector, current_thread); 00739 Threads::const_iterator ti; 00740 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 00741 RenderThread *thread = (*ti).second; 00742 thread->_cv_mutex.acquire(); 00743 00744 while (thread->_thread_state != TS_wait) { 00745 thread->_cv_done.wait(); 00746 } 00747 } 00748 } 00749 00750 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS) 00751 _cyclers_pcollector.set_level(_pipeline->get_num_cyclers()); 00752 _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers()); 00753 00754 #ifdef DEBUG_THREADS 00755 if (PStatClient::is_connected()) { 00756 _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this); 00757 _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this); 00758 } 00759 #endif // DEBUG_THREADS 00760 00761 #endif // THREADED_PIPELINE && DO_PSTATS 00762 00763 GeomCacheManager::flush_level(); 00764 CullTraverser::flush_level(); 00765 RenderState::flush_level(); 00766 TransformState::flush_level(); 00767 CullableObject::flush_level(); 00768 00769 // Now cycle the pipeline and officially begin the next frame. 00770 #ifdef THREADED_PIPELINE 00771 { 00772 PStatTimer timer(_cycle_pcollector, current_thread); 00773 _pipeline->cycle(); 00774 } 00775 #endif // THREADED_PIPELINE 00776 00777 global_clock->tick(current_thread); 00778 if (global_clock->check_errors(current_thread)) { 00779 throw_event("clock_error"); 00780 } 00781 00782 #ifdef DO_PSTATS 00783 PStatClient::main_tick(); 00784 00785 // Reset our pcollectors that track data across the frame. 00786 CullTraverser::_nodes_pcollector.clear_level(); 00787 CullTraverser::_geom_nodes_pcollector.clear_level(); 00788 CullTraverser::_geoms_pcollector.clear_level(); 00789 GeomCacheManager::_geom_cache_active_pcollector.clear_level(); 00790 GeomCacheManager::_geom_cache_record_pcollector.clear_level(); 00791 GeomCacheManager::_geom_cache_erase_pcollector.clear_level(); 00792 GeomCacheManager::_geom_cache_evict_pcollector.clear_level(); 00793 00794 GraphicsStateGuardian::init_frame_pstats(); 00795 00796 _transform_states_pcollector.set_level(TransformState::get_num_states()); 00797 _render_states_pcollector.set_level(RenderState::get_num_states()); 00798 if (pstats_unused_states) { 00799 _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states()); 00800 _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states()); 00801 } 00802 00803 _sw_sprites_pcollector.clear_level(); 00804 00805 _cnode_volume_pcollector.clear_level(); 00806 _gnode_volume_pcollector.clear_level(); 00807 _geom_volume_pcollector.clear_level(); 00808 _node_volume_pcollector.clear_level(); 00809 _volume_pcollector.clear_level(); 00810 _test_pcollector.clear_level(); 00811 _volume_polygon_pcollector.clear_level(); 00812 _test_polygon_pcollector.clear_level(); 00813 _volume_plane_pcollector.clear_level(); 00814 _test_plane_pcollector.clear_level(); 00815 _volume_sphere_pcollector.clear_level(); 00816 _test_sphere_pcollector.clear_level(); 00817 _volume_tube_pcollector.clear_level(); 00818 _test_tube_pcollector.clear_level(); 00819 _volume_inv_sphere_pcollector.clear_level(); 00820 _test_inv_sphere_pcollector.clear_level(); 00821 _volume_geom_pcollector.clear_level(); 00822 _test_geom_pcollector.clear_level(); 00823 _occlusion_untested_pcollector.clear_level(); 00824 _occlusion_passed_pcollector.clear_level(); 00825 _occlusion_failed_pcollector.clear_level(); 00826 _occlusion_tests_pcollector.clear_level(); 00827 00828 if (PStatClient::is_connected()) { 00829 size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size(); 00830 size_t independent = GeomVertexArrayData::get_independent_lru()->get_total_size(); 00831 size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size(); 00832 size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size(); 00833 size_t pending = VertexDataPage::get_pending_lru()->get_total_size(); 00834 00835 VertexDataSaveFile *save_file = VertexDataPage::get_save_file(); 00836 size_t total_disk = save_file->get_total_file_size(); 00837 size_t used_disk = save_file->get_used_file_size(); 00838 00839 _vertex_data_small_pcollector.set_level(small_buf); 00840 _vertex_data_independent_pcollector.set_level(independent); 00841 _vertex_data_pending_pcollector.set_level(pending); 00842 _vertex_data_resident_pcollector.set_level(resident); 00843 _vertex_data_compressed_pcollector.set_level(compressed); 00844 _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk); 00845 _vertex_data_used_disk_pcollector.set_level(used_disk); 00846 } 00847 00848 #endif // DO_PSTATS 00849 00850 GeomVertexArrayData::lru_epoch(); 00851 00852 // Now signal all of our threads to begin their next frame. 00853 Threads::const_iterator ti; 00854 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 00855 RenderThread *thread = (*ti).second; 00856 if (thread->_thread_state == TS_wait) { 00857 thread->_thread_state = TS_do_frame; 00858 thread->_cv_start.notify(); 00859 } 00860 thread->_cv_mutex.release(); 00861 } 00862 00863 // Some threads may still be drawing, so indicate that we have to 00864 // wait for those threads before we can flip. 00865 _flip_state = _auto_flip ? FS_flip : FS_draw; 00866 } 00867 00868 // Now the lock is released. 00869 00870 if (yield_timeslice) { 00871 // Nap for a moment to yield the timeslice, to be polite to other 00872 // running applications. 00873 PStatTimer timer(_yield_pcollector, current_thread); 00874 Thread::force_yield(); 00875 } else if (!Thread::is_true_threads()) { 00876 PStatTimer timer(_yield_pcollector, current_thread); 00877 Thread::consider_yield(); 00878 } 00879 00880 // Anything that happens outside of GraphicsEngine::render_frame() 00881 // is deemed to be App. 00882 _app_pcollector.start(); 00883 _render_frame_pcollector.stop(); 00884 } 00885 00886 00887 //////////////////////////////////////////////////////////////////// 00888 // Function: GraphicsEngine::open_windows 00889 // Access: Published 00890 // Description: Fully opens (or closes) any windows that have 00891 // recently been requested open or closed, without 00892 // rendering any frames. It is not necessary to call 00893 // this explicitly, since windows will be automatically 00894 // opened or closed when the next frame is rendered, but 00895 // you may call this if you want your windows now 00896 // without seeing a frame go by. 00897 //////////////////////////////////////////////////////////////////// 00898 void GraphicsEngine:: 00899 open_windows() { 00900 Thread *current_thread = Thread::get_current_thread(); 00901 00902 LightReMutexHolder holder(_lock, current_thread); 00903 00904 if (!_windows_sorted) { 00905 do_resort_windows(); 00906 } 00907 00908 // We do it twice, to allow both cull and draw to process the 00909 // window. 00910 for (int i = 0; i < 2; ++i) { 00911 _app.do_windows(this, current_thread); 00912 _app.do_pending(this, current_thread); 00913 00914 PStatTimer timer(_wait_pcollector, current_thread); 00915 Threads::const_iterator ti; 00916 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 00917 RenderThread *thread = (*ti).second; 00918 thread->_cv_mutex.acquire(); 00919 00920 while (thread->_thread_state != TS_wait) { 00921 thread->_cv_done.wait(); 00922 } 00923 00924 thread->_thread_state = TS_do_windows; 00925 thread->_cv_start.notify(); 00926 thread->_cv_mutex.release(); 00927 } 00928 } 00929 00930 _needs_open_windows = false; 00931 } 00932 00933 //////////////////////////////////////////////////////////////////// 00934 // Function: GraphicsEngine::sync_frame 00935 // Access: Published 00936 // Description: Waits for all the threads that started drawing their 00937 // last frame to finish drawing. The windows are not 00938 // yet flipped when this returns; see also flip_frame(). 00939 // It is not usually necessary to call this explicitly, 00940 // unless you need to see the previous frame right away. 00941 //////////////////////////////////////////////////////////////////// 00942 void GraphicsEngine:: 00943 sync_frame() { 00944 Thread *current_thread = Thread::get_current_thread(); 00945 LightReMutexHolder holder(_lock, current_thread); 00946 00947 if (_flip_state == FS_draw) { 00948 do_sync_frame(current_thread); 00949 } 00950 } 00951 00952 //////////////////////////////////////////////////////////////////// 00953 // Function: GraphicsEngine::flip_frame 00954 // Access: Published 00955 // Description: Waits for all the threads that started drawing their 00956 // last frame to finish drawing, and then flips all the 00957 // windows. It is not usually necessary to call this 00958 // explicitly, unless you need to see the previous frame 00959 // right away. 00960 //////////////////////////////////////////////////////////////////// 00961 void GraphicsEngine:: 00962 flip_frame() { 00963 Thread *current_thread = Thread::get_current_thread(); 00964 LightReMutexHolder holder(_lock, current_thread); 00965 00966 if (_flip_state != FS_flip) { 00967 do_flip_frame(current_thread); 00968 } 00969 } 00970 00971 //////////////////////////////////////////////////////////////////// 00972 // Function: GraphicsEngine::extract_texture_data 00973 // Access: Published 00974 // Description: Asks the indicated GraphicsStateGuardian to retrieve 00975 // the texture memory image of the indicated texture and 00976 // store it in the texture's ram_image field. The image 00977 // can then be written to disk via Texture::write(), or 00978 // otherwise manipulated on the CPU. 00979 // 00980 // This is useful for retrieving the contents of a 00981 // texture that has been somehow generated on the 00982 // graphics card, instead of having been loaded the 00983 // normal way via Texture::read() or Texture::load(). 00984 // It is particularly useful for getting the data 00985 // associated with a compressed texture image. 00986 // 00987 // Since this requires a round-trip to the draw thread, 00988 // it may require waiting for the current thread to 00989 // finish rendering if it is called in a multithreaded 00990 // environment. However, you can call this several 00991 // consecutive times on different textures for little 00992 // additional cost. 00993 // 00994 // If the texture has not yet been loaded to the GSG in 00995 // question, it will be loaded immediately. 00996 // 00997 // The return value is true if the operation is 00998 // successful, false otherwise. 00999 //////////////////////////////////////////////////////////////////// 01000 bool GraphicsEngine:: 01001 extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { 01002 LightReMutexHolder holder(_lock); 01003 01004 string draw_name = gsg->get_threading_model().get_draw_name(); 01005 if (draw_name.empty()) { 01006 // A single-threaded environment. No problem. 01007 return gsg->extract_texture_data(tex); 01008 01009 } else { 01010 // A multi-threaded environment. We have to wait until the draw 01011 // thread has finished its current task. 01012 WindowRenderer *wr = get_window_renderer(draw_name, 0); 01013 RenderThread *thread = (RenderThread *)wr; 01014 MutexHolder holder2(thread->_cv_mutex); 01015 01016 while (thread->_thread_state != TS_wait) { 01017 thread->_cv_done.wait(); 01018 } 01019 01020 // OK, now the draw thread is idle. That's really good enough for 01021 // our purposes; we don't *actually* need to make the draw thread 01022 // do the work--it's sufficient that it's not doing anything else 01023 // while we access the GSG. 01024 return gsg->extract_texture_data(tex); 01025 } 01026 } 01027 01028 //////////////////////////////////////////////////////////////////// 01029 // Function: GraphicsEngine::get_global_ptr 01030 // Access: Published, Static 01031 // Description: 01032 //////////////////////////////////////////////////////////////////// 01033 GraphicsEngine *GraphicsEngine:: 01034 get_global_ptr() { 01035 if (_global_ptr == NULL) { 01036 _global_ptr = new GraphicsEngine; 01037 PandaNode::set_scene_root_func(&scene_root_func); 01038 } 01039 return _global_ptr; 01040 } 01041 01042 //////////////////////////////////////////////////////////////////// 01043 // Function: GraphicsEngine::texture_uploaded 01044 // Access: Public 01045 // Description: This method is called by the GraphicsStateGuardian 01046 // after a texture has been successfully uploaded to 01047 // graphics memory. It is intended as a callback so the 01048 // texture can release its RAM image, if _keep_ram_image 01049 // is false. 01050 // 01051 // Normally, this is not called directly except by the 01052 // GraphicsStateGuardian. It will be called in the draw 01053 // thread. 01054 //////////////////////////////////////////////////////////////////// 01055 void GraphicsEngine:: 01056 texture_uploaded(Texture *tex) { 01057 LightReMutexHolder holder(_lock); 01058 // We defer this until the end of the frame; multiple GSG's might be 01059 // rendering the texture within the same frame, and we don't want to 01060 // dump the texture image until they've all had a chance at it. 01061 _loaded_textures.push_back(LoadedTexture()); 01062 LoadedTexture < = _loaded_textures.back(); 01063 lt._tex = tex; 01064 lt._image_modified = tex->get_image_modified(); 01065 } 01066 01067 //////////////////////////////////////////////////////////////////// 01068 // Function: GraphicsEngine::do_cull 01069 // Access: Public, Static 01070 // Description: Fires off a cull traversal using the indicated camera. 01071 //////////////////////////////////////////////////////////////////// 01072 void GraphicsEngine:: 01073 do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, 01074 GraphicsStateGuardian *gsg, Thread *current_thread) { 01075 DisplayRegion *dr = scene_setup->get_display_region(); 01076 PStatTimer timer(dr->get_cull_region_pcollector(), current_thread); 01077 01078 CullTraverser *trav = dr->get_cull_traverser(); 01079 trav->set_cull_handler(cull_handler); 01080 trav->set_scene(scene_setup, gsg, dr->get_incomplete_render()); 01081 01082 trav->set_view_frustum(NULL); 01083 if (view_frustum_cull) { 01084 // If we're to be performing view-frustum culling, determine the 01085 // bounding volume associated with the current viewing frustum. 01086 01087 // First, we have to get the current viewing frustum, which comes 01088 // from the lens. 01089 PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds(); 01090 01091 if (bv != (BoundingVolume *)NULL && 01092 bv->is_of_type(GeometricBoundingVolume::get_class_type())) { 01093 // Transform it into the appropriate coordinate space. 01094 PT(GeometricBoundingVolume) local_frustum; 01095 local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy()); 01096 01097 NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread); 01098 CPT(TransformState) cull_center_transform = 01099 scene_setup->get_cull_center().get_transform(scene_parent, current_thread); 01100 local_frustum->xform(cull_center_transform->get_mat()); 01101 01102 trav->set_view_frustum(local_frustum); 01103 } 01104 } 01105 01106 trav->traverse(scene_setup->get_scene_root()); 01107 trav->end_traverse(); 01108 } 01109 01110 01111 //////////////////////////////////////////////////////////////////// 01112 // Function: GraphicsEngine::scene_root_func 01113 // Access: Private, Static 01114 // Description: This function is added to PandaNode::scene_root_func 01115 // to implement PandaNode::is_scene_root(). 01116 //////////////////////////////////////////////////////////////////// 01117 bool GraphicsEngine:: 01118 scene_root_func(const PandaNode *node) { 01119 return _global_ptr->is_scene_root(node); 01120 } 01121 01122 //////////////////////////////////////////////////////////////////// 01123 // Function: GraphicsEngine::is_scene_root 01124 // Access: Private 01125 // Description: Returns true if the indicated node is known to be 01126 // the render root of some active DisplayRegion 01127 // associated with this GraphicsEngine, false otherwise. 01128 //////////////////////////////////////////////////////////////////// 01129 bool GraphicsEngine:: 01130 is_scene_root(const PandaNode *node) { 01131 Thread *current_thread = Thread::get_current_thread(); 01132 01133 Windows::const_iterator wi; 01134 for (wi = _windows.begin(); wi != _windows.end(); ++wi) { 01135 GraphicsOutput *win = (*wi); 01136 if (win->is_active() && win->get_gsg()->is_active()) { 01137 int num_display_regions = win->get_num_active_display_regions(); 01138 for (int i = 0; i < num_display_regions; i++) { 01139 DisplayRegion *dr = win->get_active_display_region(i); 01140 if (dr != (DisplayRegion *)NULL) { 01141 NodePath camera = dr->get_camera(); 01142 if (camera.is_empty()) { 01143 continue; 01144 } 01145 01146 Camera *camera_node; 01147 DCAST_INTO_R(camera_node, camera.node(), false); 01148 if (!camera_node->is_active()) { 01149 continue; 01150 } 01151 01152 NodePath scene_root = camera_node->get_scene(); 01153 if (scene_root.is_empty()) { 01154 // If there's no explicit scene specified, use whatever scene the 01155 // camera is parented within. This is the normal and preferred 01156 // case; the use of an explicit scene is now deprecated. 01157 scene_root = camera.get_top(current_thread); 01158 } 01159 01160 if (scene_root.node() == node) { 01161 return true; 01162 } 01163 } 01164 } 01165 } 01166 } 01167 01168 return false; 01169 } 01170 01171 //////////////////////////////////////////////////////////////////// 01172 // Function: GraphicsEngine::set_window_sort 01173 // Access: Private 01174 // Description: Changes the sort value of a particular window (or 01175 // buffer) on the GraphicsEngine. This requires 01176 // securing the mutex. 01177 // 01178 // Users shouldn't call this directly; use 01179 // GraphicsOutput::set_sort() instead. 01180 //////////////////////////////////////////////////////////////////// 01181 void GraphicsEngine:: 01182 set_window_sort(GraphicsOutput *window, int sort) { 01183 LightReMutexHolder holder(_lock); 01184 window->_sort = sort; 01185 _windows_sorted = false; 01186 } 01187 01188 //////////////////////////////////////////////////////////////////// 01189 // Function: GraphicsEngine::cull_and_draw_together 01190 // Access: Private 01191 // Description: This is called in the cull+draw thread by individual 01192 // RenderThread objects during the frame rendering. It 01193 // culls the geometry and immediately draws it, without 01194 // first collecting it into bins. This is used when the 01195 // threading model begins with the "-" character. 01196 //////////////////////////////////////////////////////////////////// 01197 void GraphicsEngine:: 01198 cull_and_draw_together(const GraphicsEngine::Windows &wlist, 01199 Thread *current_thread) { 01200 PStatTimer timer(_cull_pcollector, current_thread); 01201 01202 Windows::const_iterator wi; 01203 for (wi = wlist.begin(); wi != wlist.end(); ++wi) { 01204 GraphicsOutput *win = (*wi); 01205 if (win->is_active() && win->get_gsg()->is_active()) { 01206 if (win->flip_ready()) { 01207 { 01208 PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); 01209 win->begin_flip(); 01210 } 01211 { 01212 PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); 01213 win->end_flip(); 01214 } 01215 } 01216 01217 if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { 01218 win->clear(current_thread); 01219 01220 int num_display_regions = win->get_num_active_display_regions(); 01221 for (int i = 0; i < num_display_regions; i++) { 01222 DisplayRegion *dr = win->get_active_display_region(i); 01223 if (dr != (DisplayRegion *)NULL) { 01224 cull_and_draw_together(win, dr, current_thread); 01225 } 01226 } 01227 win->end_frame(GraphicsOutput::FM_render, current_thread); 01228 01229 if (_auto_flip) { 01230 if (win->flip_ready()) { 01231 { 01232 PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); 01233 win->begin_flip(); 01234 } 01235 { 01236 PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); 01237 win->end_flip(); 01238 } 01239 } 01240 } 01241 } 01242 } 01243 } 01244 } 01245 01246 //////////////////////////////////////////////////////////////////// 01247 // Function: GraphicsEngine::cull_and_draw_together 01248 // Access: Private 01249 // Description: Called only from within the inner loop in 01250 // cull_and_draw_together(), above. 01251 //////////////////////////////////////////////////////////////////// 01252 void GraphicsEngine:: 01253 cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, 01254 Thread *current_thread) { 01255 GraphicsStateGuardian *gsg = win->get_gsg(); 01256 nassertv(gsg != (GraphicsStateGuardian *)NULL); 01257 01258 DisplayRegionPipelineReader *dr_reader = 01259 new DisplayRegionPipelineReader(dr, current_thread); 01260 01261 win->change_scenes(dr_reader); 01262 gsg->prepare_display_region(dr_reader, dr_reader->get_stereo_channel()); 01263 01264 if (dr_reader->is_any_clear_active()) { 01265 gsg->clear(dr); 01266 } 01267 01268 PT(SceneSetup) scene_setup = setup_scene(gsg, dr_reader); 01269 if (scene_setup == (SceneSetup *)NULL) { 01270 // Never mind. 01271 01272 } else if (dr_reader->get_object()->is_stereo()) { 01273 // Don't draw stereo DisplayRegions directly. 01274 01275 } else if (!gsg->set_scene(scene_setup)) { 01276 // The scene or lens is inappropriate somehow. 01277 display_cat.error() 01278 << gsg->get_type() << " cannot render scene with specified lens.\n"; 01279 01280 } else { 01281 DrawCullHandler cull_handler(gsg); 01282 if (gsg->begin_scene()) { 01283 delete dr_reader; 01284 dr_reader = NULL; 01285 01286 CallbackObject *cbobj = dr->get_cull_callback(); 01287 if (cbobj != (CallbackObject *)NULL) { 01288 // Issue the cull callback on this DisplayRegion. 01289 DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup); 01290 cbobj->do_callback(&cbdata); 01291 01292 // The callback has taken care of the culling. 01293 01294 } else { 01295 // Perform the cull normally. 01296 do_cull(&cull_handler, scene_setup, gsg, current_thread); 01297 } 01298 01299 gsg->end_scene(); 01300 } 01301 } 01302 01303 if (dr_reader != (DisplayRegionPipelineReader *)NULL) { 01304 delete dr_reader; 01305 } 01306 } 01307 01308 //////////////////////////////////////////////////////////////////// 01309 // Function: GraphicsEngine::cull_to_bins 01310 // Access: Private 01311 // Description: This is called in the cull thread by individual 01312 // RenderThread objects during the frame rendering. It 01313 // collects the geometry into bins in preparation for 01314 // drawing. 01315 //////////////////////////////////////////////////////////////////// 01316 void GraphicsEngine:: 01317 cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 01318 PStatTimer timer(_cull_pcollector, current_thread); 01319 01320 _singular_warning_last_frame = _singular_warning_this_frame; 01321 _singular_warning_this_frame = false; 01322 01323 // Keep track of the cameras we have already used in this thread to 01324 // render DisplayRegions. 01325 typedef pmap<NodePath, DisplayRegion *> AlreadyCulled; 01326 AlreadyCulled already_culled; 01327 01328 Windows::const_iterator wi; 01329 for (wi = wlist.begin(); wi != wlist.end(); ++wi) { 01330 GraphicsOutput *win = (*wi); 01331 if (win->is_active() && win->get_gsg()->is_active()) { 01332 PStatTimer timer(win->get_cull_window_pcollector(), current_thread); 01333 int num_display_regions = win->get_num_active_display_regions(); 01334 for (int i = 0; i < num_display_regions; ++i) { 01335 DisplayRegion *dr = win->get_active_display_region(i); 01336 if (dr != (DisplayRegion *)NULL) { 01337 DisplayRegionPipelineReader *dr_reader = 01338 new DisplayRegionPipelineReader(dr, current_thread); 01339 NodePath camera = dr_reader->get_camera(); 01340 AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(camera, NULL)).first; 01341 if ((*aci).second == NULL) { 01342 // We have not used this camera already in this thread. 01343 // Perform the cull operation. 01344 delete dr_reader; 01345 dr_reader = NULL; 01346 (*aci).second = dr; 01347 cull_to_bins(win, dr, current_thread); 01348 01349 } else { 01350 // We have already culled a scene using this camera in 01351 // this thread, and now we're being asked to cull another 01352 // scene using the same camera. (Maybe this represents 01353 // two different DisplayRegions for the left and right 01354 // channels of a stereo image.) Of course, the cull 01355 // result will be the same, so just use the result from 01356 // the other DisplayRegion. 01357 DisplayRegion *other_dr = (*aci).second; 01358 dr->set_cull_result(other_dr->get_cull_result(current_thread), 01359 setup_scene(win->get_gsg(), dr_reader), 01360 current_thread); 01361 } 01362 01363 if (dr_reader != (DisplayRegionPipelineReader *)NULL) { 01364 delete dr_reader; 01365 } 01366 } 01367 } 01368 } 01369 } 01370 } 01371 01372 //////////////////////////////////////////////////////////////////// 01373 // Function: GraphicsEngine::cull_to_bins 01374 // Access: Private 01375 // Description: Called only within the inner loop of cull_to_bins(), 01376 // above. 01377 //////////////////////////////////////////////////////////////////// 01378 void GraphicsEngine:: 01379 cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { 01380 GraphicsStateGuardian *gsg = win->get_gsg(); 01381 nassertv(gsg != (GraphicsStateGuardian *)NULL); 01382 01383 PT(CullResult) cull_result; 01384 PT(SceneSetup) scene_setup; 01385 { 01386 PStatTimer timer(_cull_setup_pcollector, current_thread); 01387 DisplayRegionPipelineReader dr_reader(dr, current_thread); 01388 scene_setup = setup_scene(gsg, &dr_reader); 01389 cull_result = dr->get_cull_result(current_thread); 01390 01391 if (cull_result != (CullResult *)NULL) { 01392 cull_result = cull_result->make_next(); 01393 01394 } else { 01395 // This DisplayRegion has no cull results; draw it. 01396 cull_result = new CullResult(gsg, dr->get_draw_region_pcollector()); 01397 } 01398 } 01399 01400 if (scene_setup != (SceneSetup *)NULL) { 01401 BinCullHandler cull_handler(cull_result); 01402 CallbackObject *cbobj = dr->get_cull_callback(); 01403 if (cbobj != (CallbackObject *)NULL) { 01404 // Issue the cull callback on this DisplayRegion. 01405 DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup); 01406 cbobj->do_callback(&cbdata); 01407 01408 // The callback has taken care of the culling. 01409 01410 } else { 01411 // Perform the cull normally. 01412 do_cull(&cull_handler, scene_setup, gsg, current_thread); 01413 } 01414 01415 PStatTimer timer(_cull_sort_pcollector, current_thread); 01416 cull_result->finish_cull(scene_setup, current_thread); 01417 } 01418 01419 // Save the results for next frame. 01420 dr->set_cull_result(cull_result, scene_setup, current_thread); 01421 } 01422 01423 //////////////////////////////////////////////////////////////////// 01424 // Function: GraphicsEngine::draw_bins 01425 // Access: Private 01426 // Description: This is called in the draw thread by individual 01427 // RenderThread objects during the frame rendering. It 01428 // issues the graphics commands to draw the objects that 01429 // have been collected into bins by a previous call to 01430 // cull_to_bins(). 01431 //////////////////////////////////////////////////////////////////// 01432 void GraphicsEngine:: 01433 draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 01434 nassertv(wlist.verify_list()); 01435 01436 size_t wlist_size = wlist.size(); 01437 for (size_t wi = 0; wi < wlist_size; ++wi) { 01438 GraphicsOutput *win = wlist[wi]; 01439 if (win->is_active() && win->get_gsg()->is_active()) { 01440 if (win->flip_ready()) { 01441 { 01442 PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); 01443 win->begin_flip(); 01444 } 01445 { 01446 PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); 01447 win->end_flip(); 01448 } 01449 } 01450 01451 PStatTimer timer(win->get_draw_window_pcollector(), current_thread); 01452 if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { 01453 win->clear(current_thread); 01454 01455 if (display_cat.is_spam()) { 01456 display_cat.spam() 01457 << "Drawing window " << win->get_name() << "\n"; 01458 } 01459 int num_display_regions = win->get_num_active_display_regions(); 01460 for (int i = 0; i < num_display_regions; ++i) { 01461 DisplayRegion *dr = win->get_active_display_region(i); 01462 if (dr != (DisplayRegion *)NULL) { 01463 draw_bins(win, dr, current_thread); 01464 } 01465 } 01466 win->end_frame(GraphicsOutput::FM_render, current_thread); 01467 01468 if (_auto_flip) { 01469 if (win->flip_ready()) { 01470 { 01471 PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); 01472 win->begin_flip(); 01473 } 01474 { 01475 PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); 01476 win->end_flip(); 01477 } 01478 } 01479 } 01480 } else { 01481 if (display_cat.is_spam()) { 01482 display_cat.spam() 01483 << "Not drawing window " << win->get_name() << "\n"; 01484 } 01485 } 01486 } else { 01487 if (display_cat.is_spam()) { 01488 display_cat.spam() 01489 << "Window " << win->get_name() << " is inactive\n"; 01490 } 01491 } 01492 } 01493 } 01494 01495 //////////////////////////////////////////////////////////////////// 01496 // Function: GraphicsEngine::draw_bins 01497 // Access: Private 01498 // Description: This variant on draw_bins() is only called from 01499 // draw_bins(), above. It draws the cull result for a 01500 // particular DisplayRegion. 01501 //////////////////////////////////////////////////////////////////// 01502 void GraphicsEngine:: 01503 draw_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { 01504 GraphicsStateGuardian *gsg = win->get_gsg(); 01505 nassertv(gsg != (GraphicsStateGuardian *)NULL); 01506 01507 PT(CullResult) cull_result = dr->get_cull_result(current_thread); 01508 PT(SceneSetup) scene_setup = dr->get_scene_setup(current_thread); 01509 do_draw(cull_result, scene_setup, win, dr, current_thread); 01510 } 01511 01512 //////////////////////////////////////////////////////////////////// 01513 // Function: GraphicsEngine::make_contexts 01514 // Access: Private 01515 // Description: Called in the draw thread, this calls make_context() 01516 // on each window on the list to guarantee its gsg and 01517 // graphics context both get created. 01518 //////////////////////////////////////////////////////////////////// 01519 void GraphicsEngine:: 01520 make_contexts(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 01521 Windows::const_iterator wi; 01522 for (wi = wlist.begin(); wi != wlist.end(); ++wi) { 01523 GraphicsOutput *win = (*wi); 01524 if (win->begin_frame(GraphicsOutput::FM_refresh, current_thread)) { 01525 win->end_frame(GraphicsOutput::FM_refresh, current_thread); 01526 } 01527 } 01528 } 01529 01530 //////////////////////////////////////////////////////////////////// 01531 // Function: GraphicsEngine::process_events 01532 // Access: Private 01533 // Description: This is called by the RenderThread object to process 01534 // all the windows events (resize, etc.) for the given 01535 // list of windows. This is run in the window thread. 01536 //////////////////////////////////////////////////////////////////// 01537 void GraphicsEngine:: 01538 process_events(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 01539 // We're not using a vector iterator here, since it's possible that 01540 // the window list changes in an event, which would invalidate the 01541 // iterator and cause a crash. 01542 for (int i = 0; i < wlist.size(); ++i) { 01543 wlist[i]->process_events(); 01544 } 01545 } 01546 01547 //////////////////////////////////////////////////////////////////// 01548 // Function: GraphicsEngine::flip_windows 01549 // Access: Private 01550 // Description: This is called by the RenderThread object to flip the 01551 // buffers for all of the non-single-buffered windows in 01552 // the given list. This is run in the draw thread. 01553 //////////////////////////////////////////////////////////////////// 01554 void GraphicsEngine:: 01555 flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 01556 Windows::const_iterator wi; 01557 for (wi = wlist.begin(); wi != wlist.end(); ++wi) { 01558 GraphicsOutput *win = (*wi); 01559 if (win->flip_ready()) { 01560 PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); 01561 win->begin_flip(); 01562 } 01563 } 01564 for (wi = wlist.begin(); wi != wlist.end(); ++wi) { 01565 GraphicsOutput *win = (*wi); 01566 if (win->flip_ready()) { 01567 PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); 01568 win->end_flip(); 01569 } 01570 } 01571 } 01572 01573 //////////////////////////////////////////////////////////////////// 01574 // Function: GraphicsEngine::do_sync_frame 01575 // Access: Private 01576 // Description: The implementation of sync_frame(). We assume _lock 01577 // is already held before this method is called. 01578 //////////////////////////////////////////////////////////////////// 01579 void GraphicsEngine:: 01580 do_sync_frame(Thread *current_thread) { 01581 nassertv(_lock.debug_is_locked()); 01582 01583 // Statistics 01584 PStatTimer timer(_sync_pcollector, current_thread); 01585 01586 nassertv(_flip_state == FS_draw); 01587 01588 // Wait for all the threads to finish their current frame. Grabbing 01589 // and releasing the mutex should achieve that. 01590 Threads::const_iterator ti; 01591 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 01592 RenderThread *thread = (*ti).second; 01593 thread->_cv_mutex.acquire(); 01594 thread->_cv_mutex.release(); 01595 } 01596 01597 _flip_state = FS_sync; 01598 } 01599 01600 //////////////////////////////////////////////////////////////////// 01601 // Function: GraphicsEngine::do_flip_frame 01602 // Access: Private 01603 // Description: The implementation of flip_frame(). We assume _lock 01604 // is already held before this method is called. 01605 //////////////////////////////////////////////////////////////////// 01606 void GraphicsEngine:: 01607 do_flip_frame(Thread *current_thread) { 01608 nassertv(_lock.debug_is_locked()); 01609 01610 // Statistics 01611 PStatTimer timer(_flip_pcollector, current_thread); 01612 01613 nassertv(_flip_state == FS_draw || _flip_state == FS_sync); 01614 01615 // First, wait for all the threads to finish their current frame, if 01616 // necessary. Grabbing the mutex (and waiting for TS_wait) should 01617 // achieve that. 01618 { 01619 PStatTimer timer(_wait_pcollector, current_thread); 01620 Threads::const_iterator ti; 01621 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 01622 RenderThread *thread = (*ti).second; 01623 thread->_cv_mutex.acquire(); 01624 01625 while (thread->_thread_state != TS_wait) { 01626 thread->_cv_done.wait(); 01627 } 01628 } 01629 } 01630 01631 // Now signal all of our threads to flip the windows. 01632 _app.do_flip(this, current_thread); 01633 01634 { 01635 Threads::const_iterator ti; 01636 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 01637 RenderThread *thread = (*ti).second; 01638 nassertv(thread->_thread_state == TS_wait); 01639 thread->_thread_state = TS_do_flip; 01640 thread->_cv_start.notify(); 01641 thread->_cv_mutex.release(); 01642 } 01643 } 01644 01645 _flip_state = FS_flip; 01646 } 01647 01648 //////////////////////////////////////////////////////////////////// 01649 // Function: GraphicsEngine::setup_scene 01650 // Access: Private 01651 // Description: Returns a new SceneSetup object appropriate for 01652 // rendering the scene from the indicated camera, or 01653 // NULL if the scene should not be rendered for some 01654 // reason. 01655 //////////////////////////////////////////////////////////////////// 01656 PT(SceneSetup) GraphicsEngine:: 01657 setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { 01658 Thread *current_thread = dr->get_current_thread(); 01659 PStatTimer timer(_cull_setup_pcollector, current_thread); 01660 01661 GraphicsOutput *window = dr->get_window(); 01662 // The window pointer shouldn't be NULL, since we presumably got to 01663 // this particular DisplayRegion by walking through a list on a 01664 // window. 01665 nassertr(window != (GraphicsOutput *)NULL, NULL); 01666 01667 NodePath camera = dr->get_camera(); 01668 if (camera.is_empty()) { 01669 // No camera, no draw. 01670 return NULL; 01671 } 01672 01673 Camera *camera_node; 01674 DCAST_INTO_R(camera_node, camera.node(), NULL); 01675 01676 if (!camera_node->is_active()) { 01677 // Camera inactive, no draw. 01678 return NULL; 01679 } 01680 camera_node->cleanup_aux_scene_data(current_thread); 01681 01682 int lens_index = dr->get_lens_index(); 01683 Lens *lens = camera_node->get_lens(lens_index); 01684 if (lens == (Lens *)NULL || !camera_node->get_lens_active(lens_index)) { 01685 // No lens, no draw. 01686 return NULL; 01687 } 01688 01689 NodePath scene_root = camera_node->get_scene(); 01690 if (scene_root.is_empty()) { 01691 // If there's no explicit scene specified, use whatever scene the 01692 // camera is parented within. This is the normal and preferred 01693 // case; the use of an explicit scene is now deprecated. 01694 scene_root = camera.get_top(current_thread); 01695 } 01696 01697 PT(SceneSetup) scene_setup = new SceneSetup; 01698 01699 // We will need both the camera transform (the net transform to the 01700 // camera from the scene) and the world transform (the camera 01701 // transform inverse, or the net transform to the scene from the 01702 // camera). These are actually defined from the parent of the 01703 // scene_root, because the scene_root's own transform is immediately 01704 // applied to these during rendering. (Normally, the parent of the 01705 // scene_root is the empty NodePath, although it need not be.) 01706 NodePath scene_parent = scene_root.get_parent(current_thread); 01707 CPT(TransformState) camera_transform = camera.get_transform(scene_parent, current_thread); 01708 CPT(TransformState) world_transform = scene_parent.get_transform(camera, current_thread); 01709 01710 if (camera_transform->is_invalid()) { 01711 // There must be a singular transform over the scene. 01712 if (!_singular_warning_last_frame) { 01713 display_cat.warning() 01714 << "Scene " << scene_root << " has net scale (" 01715 << scene_root.get_scale(NodePath()) << "); cannot render.\n"; 01716 _singular_warning_this_frame = true; 01717 } 01718 return NULL; 01719 } 01720 01721 if (world_transform->is_invalid()) { 01722 // There must be a singular transform over the camera. 01723 if (!_singular_warning_last_frame) { 01724 display_cat.warning() 01725 << "Camera " << camera << " has net scale (" 01726 << camera.get_scale(NodePath()) << "); cannot render.\n"; 01727 } 01728 _singular_warning_this_frame = true; 01729 return NULL; 01730 } 01731 01732 CPT(RenderState) initial_state = camera_node->get_initial_state(); 01733 01734 if (window->get_inverted()) { 01735 // If the window is to be inverted, we must set the inverted flag 01736 // on the SceneSetup object, so that the GSG will be able to 01737 // invert the projection matrix at the last minute. 01738 scene_setup->set_inverted(true); 01739 01740 // This also means we need to globally invert the sense of polygon 01741 // vertex ordering. 01742 initial_state = initial_state->compose(get_invert_polygon_state()); 01743 } 01744 01745 scene_setup->set_display_region(dr->get_object()); 01746 scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height()); 01747 scene_setup->set_scene_root(scene_root); 01748 scene_setup->set_camera_path(camera); 01749 scene_setup->set_camera_node(camera_node); 01750 scene_setup->set_lens(lens); 01751 scene_setup->set_initial_state(initial_state); 01752 scene_setup->set_camera_transform(camera_transform); 01753 scene_setup->set_world_transform(world_transform); 01754 01755 return scene_setup; 01756 } 01757 01758 //////////////////////////////////////////////////////////////////// 01759 // Function: GraphicsEngine::do_draw 01760 // Access: Private 01761 // Description: Draws the previously-culled scene. 01762 //////////////////////////////////////////////////////////////////// 01763 void GraphicsEngine:: 01764 do_draw(CullResult *cull_result, SceneSetup *scene_setup, 01765 GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { 01766 // Statistics 01767 PStatTimer timer(dr->get_draw_region_pcollector(), current_thread); 01768 01769 GraphicsStateGuardian *gsg = win->get_gsg(); 01770 CallbackObject *cbobj; 01771 01772 { 01773 DisplayRegionPipelineReader dr_reader(dr, current_thread); 01774 win->change_scenes(&dr_reader); 01775 gsg->prepare_display_region(&dr_reader, dr_reader.get_stereo_channel()); 01776 if (dr_reader.is_any_clear_active()) { 01777 gsg->clear(dr_reader.get_object()); 01778 } 01779 01780 cbobj = dr_reader.get_draw_callback(); 01781 } 01782 01783 if (cbobj != (CallbackObject *)NULL) { 01784 // Issue the draw callback on this DisplayRegion. 01785 01786 // Set the GSG to the initial state. 01787 gsg->clear_before_callback(); 01788 gsg->set_state_and_transform(RenderState::make_empty(), TransformState::make_identity()); 01789 01790 DisplayRegionDrawCallbackData cbdata(cull_result, scene_setup); 01791 cbobj->do_callback(&cbdata); 01792 01793 // We don't trust the state the callback may have left us in. 01794 gsg->clear_state_and_transform(); 01795 01796 // The callback has taken care of the drawing. 01797 return; 01798 } 01799 01800 if (cull_result == NULL || scene_setup == NULL) { 01801 // Nothing to see here. 01802 01803 } else if (dr->is_stereo()) { 01804 // We don't actually draw the stereo DisplayRegions. These are 01805 // just placeholders; we draw the individual left and right eyes 01806 // instead. (We might still clear the stereo DisplayRegions, 01807 // though, since it's probably faster to clear right and left 01808 // channels in one pass, than to clear them in two separate 01809 // passes.) 01810 01811 } else if (!gsg->set_scene(scene_setup)) { 01812 // The scene or lens is inappropriate somehow. 01813 display_cat.error() 01814 << gsg->get_type() << " cannot render scene with specified lens.\n"; 01815 01816 } else { 01817 if (gsg->begin_scene()) { 01818 cull_result->draw(current_thread); 01819 gsg->end_scene(); 01820 } 01821 } 01822 } 01823 01824 //////////////////////////////////////////////////////////////////// 01825 // Function: GraphicsEngine::do_add_window 01826 // Access: Private 01827 // Description: An internal function called by make_window() and 01828 // make_buffer() and similar functions to add the 01829 // newly-created GraphicsOutput object to the engine's 01830 // list of windows, and to request that the window be 01831 // opened. 01832 //////////////////////////////////////////////////////////////////// 01833 void GraphicsEngine:: 01834 do_add_window(GraphicsOutput *window, 01835 const GraphicsThreadingModel &threading_model) { 01836 nassertv(window != NULL); 01837 LightReMutexHolder holder(_lock); 01838 nassertv(window->get_engine() == this); 01839 01840 // We have a special counter that is unique per window that allows 01841 // us to assure that recently-added windows end up on the end of the 01842 // list. 01843 window->_internal_sort_index = _window_sort_index; 01844 ++_window_sort_index; 01845 01846 _windows_sorted = false; 01847 _windows.push_back(window); 01848 01849 WindowRenderer *cull = 01850 get_window_renderer(threading_model.get_cull_name(), 01851 threading_model.get_cull_stage()); 01852 WindowRenderer *draw = 01853 get_window_renderer(threading_model.get_draw_name(), 01854 threading_model.get_draw_stage()); 01855 01856 if (threading_model.get_cull_sorting()) { 01857 cull->add_window(cull->_cull, window); 01858 draw->add_window(draw->_draw, window); 01859 } else { 01860 cull->add_window(cull->_cdraw, window); 01861 } 01862 01863 // Ask the pipe which thread it prefers to run its windowing 01864 // commands in (the "window thread"). This is the thread that 01865 // handles the commands to open, resize, etc. the window. X 01866 // requires this to be done in the app thread (along with all the 01867 // other windows, since X is strictly single-threaded), but Windows 01868 // requires this to be done in draw (because once an OpenGL context 01869 // has been bound in a given thread, it cannot subsequently be bound 01870 // in any other thread, and we have to bind a context in 01871 // open_window()). 01872 01873 switch (window->get_pipe()->get_preferred_window_thread()) { 01874 case GraphicsPipe::PWT_app: 01875 _app.add_window(_app._window, window); 01876 break; 01877 01878 case GraphicsPipe::PWT_draw: 01879 draw->add_window(draw->_window, window); 01880 break; 01881 } 01882 01883 if (display_cat.is_debug()) { 01884 display_cat.debug() 01885 << "Created " << window->get_type() << " " << (void *)window << "\n"; 01886 } 01887 01888 window->request_open(); 01889 _needs_open_windows = true; 01890 } 01891 01892 //////////////////////////////////////////////////////////////////// 01893 // Function: GraphicsEngine::do_add_gsg 01894 // Access: Private 01895 // Description: An internal function called by make_output to add 01896 // the newly-created gsg object to the engine's 01897 // list of gsg's. It also adjusts various config 01898 // variables based on the gsg's capabilities. 01899 //////////////////////////////////////////////////////////////////// 01900 void GraphicsEngine:: 01901 do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, 01902 const GraphicsThreadingModel &threading_model) { 01903 LightReMutexHolder holder(_lock); 01904 01905 nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this); 01906 gsg->_threading_model = threading_model; 01907 if (!_default_loader.is_null()) { 01908 gsg->set_loader(_default_loader); 01909 } 01910 01911 auto_adjust_capabilities(gsg); 01912 01913 WindowRenderer *draw = 01914 get_window_renderer(threading_model.get_draw_name(), 01915 threading_model.get_draw_stage()); 01916 01917 draw->add_gsg(gsg); 01918 } 01919 01920 //////////////////////////////////////////////////////////////////// 01921 // Function: GraphicsEngine::do_remove_window 01922 // Access: Private 01923 // Description: An internal function called by remove_window() and 01924 // remove_all_windows() to actually remove the indicated 01925 // window from all relevant structures, except the 01926 // _windows list itself. 01927 //////////////////////////////////////////////////////////////////// 01928 void GraphicsEngine:: 01929 do_remove_window(GraphicsOutput *window, Thread *current_thread) { 01930 nassertv(window != NULL); 01931 PT(GraphicsPipe) pipe = window->get_pipe(); 01932 window->clear_pipe(); 01933 01934 if (!_windows_sorted) { 01935 do_resort_windows(); 01936 } 01937 01938 // Now remove the window from all threads that know about it. 01939 _app.remove_window(window); 01940 Threads::const_iterator ti; 01941 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 01942 RenderThread *thread = (*ti).second; 01943 thread->remove_window(window); 01944 } 01945 01946 // If the window happened to be controlled by the app thread, we 01947 // might as well close it now rather than waiting for next frame. 01948 _app.do_pending(this, current_thread); 01949 01950 if (display_cat.is_debug()) { 01951 display_cat.debug() 01952 << "Removed " << window->get_type() << " " << (void *)window << "\n"; 01953 } 01954 } 01955 01956 //////////////////////////////////////////////////////////////////// 01957 // Function: GraphicsEngine::do_resort_windows 01958 // Access: Private 01959 // Description: Resorts all of the Windows lists. This may need to 01960 // be done if one or more of the windows' sort 01961 // properties has changed. 01962 //////////////////////////////////////////////////////////////////// 01963 void GraphicsEngine:: 01964 do_resort_windows() { 01965 _windows_sorted = true; 01966 01967 _app.resort_windows(); 01968 Threads::const_iterator ti; 01969 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 01970 RenderThread *thread = (*ti).second; 01971 thread->resort_windows(); 01972 } 01973 01974 _windows.sort(); 01975 } 01976 01977 //////////////////////////////////////////////////////////////////// 01978 // Function: GraphicsEngine::auto_adjust_capabilities 01979 // Access: Private 01980 // Description: Video card capability flags are stored on a 01981 // per-gsg basis. However, there are a few cases 01982 // where panda needs to know not the capabilities 01983 // of an individual GSG, but rather, the 01984 // collective capabilities of all the GSGs. 01985 // 01986 // Non-power-of-two (NPOT) texture support is the 01987 // classic example. Panda makes a single global 01988 // decision to either create NPOT textures, or not. 01989 // Therefore, it doesn't need to know whether one GSG 01990 // supports NPOT textures. It needs to know whether ALL 01991 // the GSGs support NPOT textures. 01992 // 01993 // The purpose of this routine is to maintain global 01994 // capability flags that summarize the collective 01995 // capabilities of the computer as a whole. 01996 // 01997 // These global capability flags are initialized from 01998 // config variables. Then, they can be auto-reconfigured 01999 // using built-in heuristic mechanisms if the user so 02000 // desires. Whether auto-reconfiguration is enabled or 02001 // not, the configured values are checked against 02002 // the actual capabilities of the machine and error 02003 // messages will be printed if there is a mismatch. 02004 // 02005 //////////////////////////////////////////////////////////////////// 02006 void GraphicsEngine:: 02007 auto_adjust_capabilities(GraphicsStateGuardian *gsg) { 02008 02009 // The rule we use when auto-reconfiguring is as follows. The 02010 // global capabilities must initially be set to conservative 02011 // values. When the first GSG comes into existence, its 02012 // capabilities will be checked, and the global capabilities 02013 // may be elevated to more aggressive values. 02014 // 02015 // At first glance, this might seem backward, and it might seem 02016 // better to do it the other way: start with all global capabilities 02017 // aggressively set, and then disable capabilities when you discover 02018 // a gsg that doesn't support them. 02019 // 02020 // However, that approach doesn't work, because once a global 02021 // capability is enabled, there is no going back. If 02022 // textures_power_2 has ever been set to 'none', there may be NPOT 02023 // textures already floating about the system. Ie, it's too late: 02024 // you can't turn these global capability flags off, once they've 02025 // been turned on. 02026 // 02027 // That's why we have to start with conservative settings, and then 02028 // elevate those settings to more aggressive values later when 02029 // we're fairly sure it's OK to do so. 02030 // 02031 // For each global capability, we must: 02032 // 1. Make sure the initial setting is conservative. 02033 // 2. Possibly elevate to a more aggressive value. 02034 // 3. Check that we haven't over-elevated. 02035 // 02036 02037 if (textures_auto_power_2 && (textures_power_2 == ATS_none)) { 02038 display_cat.error() 02039 << "Invalid panda config file: if you set the config-variable\n" 02040 << "textures_auto_power_2 to true, you must set the config-variable" 02041 << "textures_power_2 to 'up' or 'down'.\n"; 02042 textures_power_2 = ATS_down; // Not a fix. Just suppresses further error messages. 02043 } 02044 02045 if (textures_auto_power_2 && !Texture::have_textures_power_2()) { 02046 if (gsg->get_supports_tex_non_pow2()) { 02047 Texture::set_textures_power_2(ATS_none); 02048 } else { 02049 Texture::set_textures_power_2(textures_power_2); 02050 } 02051 } 02052 02053 if ((Texture::get_textures_power_2() == ATS_none) && 02054 (!gsg->get_supports_tex_non_pow2())) { 02055 02056 // Overaggressive configuration detected 02057 02058 display_cat.error() 02059 << "The 'textures_power_2' configuration is set to 'none', meaning \n" 02060 << "that non-power-of-two texture support is required, but the video \n" 02061 << "driver I'm trying to use does not support non-power-of-two textures.\n"; 02062 02063 if (textures_power_2 != ATS_none) { 02064 display_cat.error() 02065 << "The 'none' did not come from the config file. In other words,\n" 02066 << "the variable 'textures_power_2' was altered procedurally.\n"; 02067 02068 if (textures_auto_power_2) { 02069 display_cat.error() 02070 << "It is possible that it was set by panda's automatic mechanisms,\n" 02071 << "which are currently enabled, because 'textures_auto_power_2' is\n" 02072 << "true. Panda's automatic mechanisms assume that if one\n" 02073 << "window supports non-power-of-two textures, then they all will.\n" 02074 << "This assumption works for most games, but not all.\n" 02075 << "In particular, it can fail if the game creates multiple windows\n" 02076 << "on multiple displays with different video cards.\n"; 02077 } 02078 } 02079 } 02080 02081 if (shader_auto_utilization && (shader_utilization != SUT_none)) { 02082 display_cat.error() 02083 << "Invalid panda config file: if you set the config-variable\n" 02084 << "shader_auto_utilization to true, you must set the config-variable" 02085 << "shader_utilization to 'none'.\n"; 02086 shader_utilization = SUT_none; // Not a fix. Just suppresses further error messages. 02087 } 02088 02089 if (shader_auto_utilization && !Shader::have_shader_utilization()) { 02090 if (gsg->get_supports_basic_shaders()) { 02091 Shader::set_shader_utilization(SUT_basic); 02092 } else { 02093 Shader::set_shader_utilization(SUT_none); 02094 } 02095 } 02096 02097 if ((Shader::get_shader_utilization() != SUT_none) && 02098 (!gsg->get_supports_basic_shaders())) { 02099 02100 // Overaggressive configuration detected 02101 02102 display_cat.error() 02103 << "The 'shader_utilization' config variable is set, meaning\n" 02104 << "that panda may try to generate shaders. However, the video \n" 02105 << "driver I'm trying to use does not support shaders.\n"; 02106 02107 if (shader_utilization == SUT_none) { 02108 display_cat.error() 02109 << "The 'shader_utilization' setting did not come from the config\n" 02110 << "file. In other words, it was altered procedurally.\n"; 02111 02112 if (shader_auto_utilization) { 02113 display_cat.error() 02114 << "It is possible that it was set by panda's automatic mechanisms,\n" 02115 << "which are currently enabled, because 'shader_auto_utilization' is\n" 02116 << "true. Panda's automatic mechanisms assume that if one\n" 02117 << "window supports shaders, then they all will.\n" 02118 << "This assumption works for most games, but not all.\n" 02119 << "In particular, it can fail if the game creates multiple windows\n" 02120 << "on multiple displays with different video cards.\n"; 02121 } 02122 } 02123 } 02124 } 02125 02126 //////////////////////////////////////////////////////////////////// 02127 // Function: GraphicsEngine::terminate_threads 02128 // Access: Private 02129 // Description: Signals our child threads to terminate and waits for 02130 // them to clean up. 02131 //////////////////////////////////////////////////////////////////// 02132 void GraphicsEngine:: 02133 terminate_threads(Thread *current_thread) { 02134 LightReMutexHolder holder(_lock, current_thread); 02135 02136 // We spend almost our entire time in this method just waiting for 02137 // threads. Time it appropriately. 02138 PStatTimer timer(_wait_pcollector, current_thread); 02139 02140 // First, wait for all the threads to finish their current frame. 02141 // Grabbing the mutex should achieve that. 02142 Threads::const_iterator ti; 02143 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 02144 RenderThread *thread = (*ti).second; 02145 thread->_cv_mutex.acquire(); 02146 } 02147 02148 // Now tell them to close their windows and terminate. 02149 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 02150 RenderThread *thread = (*ti).second; 02151 thread->_thread_state = TS_terminate; 02152 thread->_cv_start.notify(); 02153 thread->_cv_mutex.release(); 02154 } 02155 02156 // Finally, wait for them all to finish cleaning up. 02157 for (ti = _threads.begin(); ti != _threads.end(); ++ti) { 02158 RenderThread *thread = (*ti).second; 02159 thread->join(); 02160 } 02161 02162 _threads.clear(); 02163 } 02164 02165 02166 #ifdef DO_PSTATS 02167 //////////////////////////////////////////////////////////////////// 02168 // Function: GraphicsEngine::pstats_count_cycler_type 02169 // Access: Private, Static 02170 // Description: A callback function for 02171 // Pipeline::iterate_all_cycler_types() to report the 02172 // cycler types to PStats. 02173 //////////////////////////////////////////////////////////////////// 02174 void GraphicsEngine:: 02175 pstats_count_cycler_type(TypeHandle type, int count, void *data) { 02176 GraphicsEngine *self = (GraphicsEngine *)data; 02177 CyclerTypeCounters::iterator ci = self->_all_cycler_types.find(type); 02178 if (ci == self->_all_cycler_types.end()) { 02179 PStatCollector collector(_cyclers_pcollector, type.get_name()); 02180 ci = self->_all_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first; 02181 } 02182 (*ci).second.set_level(count); 02183 } 02184 #endif // DO_PSTATS 02185 02186 #ifdef DO_PSTATS 02187 //////////////////////////////////////////////////////////////////// 02188 // Function: GraphicsEngine::pstats_count_dirty_cycler_type 02189 // Access: Private, Static 02190 // Description: A callback function for 02191 // Pipeline::iterate_dirty_cycler_types() to report the 02192 // cycler types to PStats. 02193 //////////////////////////////////////////////////////////////////// 02194 void GraphicsEngine:: 02195 pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data) { 02196 GraphicsEngine *self = (GraphicsEngine *)data; 02197 CyclerTypeCounters::iterator ci = self->_dirty_cycler_types.find(type); 02198 if (ci == self->_dirty_cycler_types.end()) { 02199 PStatCollector collector(_dirty_cyclers_pcollector, type.get_name()); 02200 ci = self->_dirty_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first; 02201 } 02202 (*ci).second.set_level(count); 02203 } 02204 #endif // DO_PSTATS 02205 02206 //////////////////////////////////////////////////////////////////// 02207 // Function: GraphicsEngine::get_invert_polygon_state 02208 // Access: Protected, Static 02209 // Description: Returns a RenderState for inverting the sense of 02210 // polygon vertex ordering: if the scene graph specifies 02211 // a clockwise ordering, this changes it to 02212 // counterclockwise, and vice-versa. 02213 //////////////////////////////////////////////////////////////////// 02214 const RenderState *GraphicsEngine:: 02215 get_invert_polygon_state() { 02216 // Once someone asks for this pointer, we hold its reference count 02217 // and never free it. 02218 static CPT(RenderState) state = (const RenderState *)NULL; 02219 if (state == (const RenderState *)NULL) { 02220 state = RenderState::make(CullFaceAttrib::make_reverse()); 02221 } 02222 02223 return state; 02224 } 02225 02226 //////////////////////////////////////////////////////////////////// 02227 // Function: GraphicsEngine::get_window_renderer 02228 // Access: Private 02229 // Description: Returns the WindowRenderer with the given name. 02230 // Creates a new RenderThread if there is no such thread 02231 // already. The pipeline_stage number specifies the 02232 // pipeline stage that will be assigned to the thread 02233 // (unless was previously given a higher stage). 02234 // 02235 // You must already be holding the lock before calling 02236 // this method. 02237 //////////////////////////////////////////////////////////////////// 02238 GraphicsEngine::WindowRenderer *GraphicsEngine:: 02239 get_window_renderer(const string &name, int pipeline_stage) { 02240 nassertr(_lock.debug_is_locked(), NULL); 02241 02242 if (name.empty()) { 02243 return &_app; 02244 } 02245 02246 Threads::iterator ti = _threads.find(name); 02247 if (ti != _threads.end()) { 02248 return (*ti).second.p(); 02249 } 02250 02251 PT(RenderThread) thread = new RenderThread(name, this); 02252 thread->set_min_pipeline_stage(pipeline_stage); 02253 _pipeline->set_min_stages(pipeline_stage + 1); 02254 02255 bool started = thread->start(TP_normal, true); 02256 nassertr(started, thread.p()); 02257 _threads[name] = thread; 02258 02259 nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p()); 02260 02261 return thread.p(); 02262 } 02263 02264 //////////////////////////////////////////////////////////////////// 02265 // Function: GraphicsEngine::WindowRenderer::Constructor 02266 // Access: Public 02267 // Description: 02268 //////////////////////////////////////////////////////////////////// 02269 GraphicsEngine::WindowRenderer:: 02270 WindowRenderer(const string &name) : 02271 _wl_lock(string("GraphicsEngine::WindowRenderer::_wl_lock ") + name) 02272 { 02273 } 02274 02275 //////////////////////////////////////////////////////////////////// 02276 // Function: GraphicsEngine::WindowRenderer::add_gsg 02277 // Access: Public 02278 // Description: Adds a new GSG to the _gsg list, if it is not already 02279 // there. 02280 //////////////////////////////////////////////////////////////////// 02281 void GraphicsEngine::WindowRenderer:: 02282 add_gsg(GraphicsStateGuardian *gsg) { 02283 LightReMutexHolder holder(_wl_lock); 02284 _gsgs.insert(gsg); 02285 } 02286 02287 //////////////////////////////////////////////////////////////////// 02288 // Function: GraphicsEngine::WindowRenderer::add_window 02289 // Access: Public 02290 // Description: Adds a new window to the indicated list, which should 02291 // be a member of the WindowRenderer. 02292 //////////////////////////////////////////////////////////////////// 02293 void GraphicsEngine::WindowRenderer:: 02294 add_window(Windows &wlist, GraphicsOutput *window) { 02295 LightReMutexHolder holder(_wl_lock); 02296 wlist.insert(window); 02297 } 02298 02299 //////////////////////////////////////////////////////////////////// 02300 // Function: GraphicsEngine::WindowRenderer::remove_window 02301 // Access: Public 02302 // Description: Immediately removes the indicated window from all 02303 // lists. If the window is currently open and is 02304 // already on the _window list, moves it to the _pending_close 02305 // list for later closure. 02306 //////////////////////////////////////////////////////////////////// 02307 void GraphicsEngine::WindowRenderer:: 02308 remove_window(GraphicsOutput *window) { 02309 nassertv(window != NULL); 02310 LightReMutexHolder holder(_wl_lock); 02311 PT(GraphicsOutput) ptwin = window; 02312 02313 _cull.erase(ptwin); 02314 _cdraw.erase(ptwin); 02315 _draw.erase(ptwin); 02316 02317 Windows::iterator wi; 02318 02319 wi = _window.find(ptwin); 02320 if (wi != _window.end()) { 02321 // The window is on our _window list, meaning its open/close 02322 // operations (among other window ops) are serviced by this 02323 // thread. 02324 02325 // Make sure the window isn't about to request itself open. 02326 ptwin->request_close(); 02327 02328 // If the window is already open, move it to the _pending_close list so 02329 // it can be closed later. We can't close it immediately, because 02330 // we might not have been called from the subthread. 02331 if (ptwin->is_valid()) { 02332 _pending_close.push_back(ptwin); 02333 } 02334 02335 _window.erase(wi); 02336 } 02337 } 02338 02339 //////////////////////////////////////////////////////////////////// 02340 // Function: GraphicsEngine::WindowRenderer::resort_windows 02341 // Access: Public 02342 // Description: Resorts all the lists of windows, assuming they may 02343 // have become unsorted. 02344 //////////////////////////////////////////////////////////////////// 02345 void GraphicsEngine::WindowRenderer:: 02346 resort_windows() { 02347 LightReMutexHolder holder(_wl_lock); 02348 02349 _cull.sort(); 02350 _cdraw.sort(); 02351 _draw.sort(); 02352 _window.sort(); 02353 02354 if (display_cat.is_debug()) { 02355 display_cat.debug() 02356 << "Windows resorted:"; 02357 Windows::const_iterator wi; 02358 for (wi = _window.begin(); wi != _window.end(); ++wi) { 02359 GraphicsOutput *win = (*wi); 02360 display_cat.debug(false) 02361 << " " << win->get_name() << "(" << win->get_sort() << ")"; 02362 } 02363 display_cat.debug(false) 02364 << "\n"; 02365 02366 for (wi = _draw.begin(); wi != _draw.end(); ++wi) { 02367 GraphicsOutput *win = (*wi); 02368 display_cat.debug(false) 02369 << " " << win->get_name() << "(" << win->get_sort() << ")"; 02370 } 02371 display_cat.debug(false) 02372 << "\n"; 02373 } 02374 } 02375 02376 //////////////////////////////////////////////////////////////////// 02377 // Function: GraphicsEngine::WindowRenderer::do_frame 02378 // Access: Public 02379 // Description: Executes one stage of the pipeline for the current 02380 // thread: calls cull on all windows that are on the 02381 // cull list for this thread, draw on all the windows on 02382 // the draw list, etc. 02383 //////////////////////////////////////////////////////////////////// 02384 void GraphicsEngine::WindowRenderer:: 02385 do_frame(GraphicsEngine *engine, Thread *current_thread) { 02386 PStatTimer timer(engine->_do_frame_pcollector, current_thread); 02387 LightReMutexHolder holder(_wl_lock); 02388 02389 engine->cull_to_bins(_cull, current_thread); 02390 engine->cull_and_draw_together(_cdraw, current_thread); 02391 engine->draw_bins(_draw, current_thread); 02392 engine->process_events(_window, current_thread); 02393 02394 // If any GSG's on the list have no more outstanding pointers, clean 02395 // them up. (We are in the draw thread for all of these GSG's.) 02396 if (any_done_gsgs()) { 02397 GSGs new_gsgs; 02398 GSGs::iterator gi; 02399 for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) { 02400 GraphicsStateGuardian *gsg = (*gi); 02401 if (gsg->get_ref_count() == 1) { 02402 // This one has no outstanding pointers; clean it up. 02403 GraphicsPipe *pipe = gsg->get_pipe(); 02404 engine->close_gsg(pipe, gsg); 02405 } else { 02406 // This one is ok; preserve it. 02407 new_gsgs.insert(gsg); 02408 } 02409 } 02410 02411 _gsgs.swap(new_gsgs); 02412 } 02413 } 02414 02415 //////////////////////////////////////////////////////////////////// 02416 // Function: GraphicsEngine::WindowRenderer::do_windows 02417 // Access: Public 02418 // Description: Attempts to fully open or close any windows or 02419 // buffers associated with this thread, but does not 02420 // otherwise perform any rendering. (Normally, this 02421 // step is handled during do_frame(); call this method 02422 // only if you want these things to open immediately.) 02423 //////////////////////////////////////////////////////////////////// 02424 void GraphicsEngine::WindowRenderer:: 02425 do_windows(GraphicsEngine *engine, Thread *current_thread) { 02426 LightReMutexHolder holder(_wl_lock); 02427 02428 engine->process_events(_window, current_thread); 02429 02430 engine->make_contexts(_cdraw, current_thread); 02431 engine->make_contexts(_draw, current_thread); 02432 } 02433 02434 //////////////////////////////////////////////////////////////////// 02435 // Function: GraphicsEngine::WindowRenderer::do_flip 02436 // Access: Public 02437 // Description: Flips the windows as appropriate for the current 02438 // thread. 02439 //////////////////////////////////////////////////////////////////// 02440 void GraphicsEngine::WindowRenderer:: 02441 do_flip(GraphicsEngine *engine, Thread *current_thread) { 02442 LightReMutexHolder holder(_wl_lock); 02443 engine->flip_windows(_cdraw, current_thread); 02444 engine->flip_windows(_draw, current_thread); 02445 } 02446 02447 //////////////////////////////////////////////////////////////////// 02448 // Function: GraphicsEngine::WindowRenderer::do_close 02449 // Access: Public 02450 // Description: Closes all the windows on the _window list. 02451 //////////////////////////////////////////////////////////////////// 02452 void GraphicsEngine::WindowRenderer:: 02453 do_close(GraphicsEngine *engine, Thread *current_thread) { 02454 LightReMutexHolder holder(_wl_lock); 02455 Windows::iterator wi; 02456 for (wi = _window.begin(); wi != _window.end(); ++wi) { 02457 GraphicsOutput *win = (*wi); 02458 win->set_close_now(); 02459 } 02460 02461 // Also close all of the GSG's. 02462 GSGs new_gsgs; 02463 GSGs::iterator gi; 02464 for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) { 02465 GraphicsStateGuardian *gsg = (*gi); 02466 if (gsg->get_ref_count() == 1) { 02467 // This one has no outstanding pointers; clean it up. 02468 GraphicsPipe *pipe = gsg->get_pipe(); 02469 engine->close_gsg(pipe, gsg); 02470 } else { 02471 // This one is ok; preserve it. 02472 new_gsgs.insert(gsg); 02473 } 02474 } 02475 02476 _gsgs.swap(new_gsgs); 02477 } 02478 02479 //////////////////////////////////////////////////////////////////// 02480 // Function: GraphicsEngine::WindowRenderer::do_pending 02481 // Access: Public 02482 // Description: Actually closes any windows that were recently 02483 // removed from the WindowRenderer. 02484 //////////////////////////////////////////////////////////////////// 02485 void GraphicsEngine::WindowRenderer:: 02486 do_pending(GraphicsEngine *engine, Thread *current_thread) { 02487 LightReMutexHolder holder(_wl_lock); 02488 02489 if (!_pending_close.empty()) { 02490 if (display_cat.is_debug()) { 02491 display_cat.debug() 02492 << "_pending_close.size() = " << _pending_close.size() << "\n"; 02493 } 02494 02495 // Close any windows that were pending closure. Carefully protect 02496 // against recursive entry to this function by swapping the vector 02497 // to a local copy first. 02498 Windows::iterator wi; 02499 Windows pending_close; 02500 _pending_close.swap(pending_close); 02501 for (wi = pending_close.begin(); wi != pending_close.end(); ++wi) { 02502 GraphicsOutput *win = (*wi); 02503 win->set_close_now(); 02504 } 02505 } 02506 } 02507 02508 //////////////////////////////////////////////////////////////////// 02509 // Function: GraphicsEngine::WindowRenderer::any_done_gsgs 02510 // Access: Public 02511 // Description: Returns true if any of the GSG's on this thread's 02512 // draw list are done (they have no outstanding pointers 02513 // other than this one), or false if all of them are 02514 // still good. 02515 //////////////////////////////////////////////////////////////////// 02516 bool GraphicsEngine::WindowRenderer:: 02517 any_done_gsgs() const { 02518 GSGs::const_iterator gi; 02519 for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) { 02520 if ((*gi)->get_ref_count() == 1) { 02521 return true; 02522 } 02523 } 02524 02525 return false; 02526 } 02527 02528 //////////////////////////////////////////////////////////////////// 02529 // Function: GraphicsEngine::RenderThread::Constructor 02530 // Access: Public 02531 // Description: 02532 //////////////////////////////////////////////////////////////////// 02533 GraphicsEngine::RenderThread:: 02534 RenderThread(const string &name, GraphicsEngine *engine) : 02535 Thread(name, "Main"), 02536 WindowRenderer(name), 02537 _engine(engine), 02538 _cv_mutex(string("GraphicsEngine::RenderThread ") + name), 02539 _cv_start(_cv_mutex), 02540 _cv_done(_cv_mutex) 02541 { 02542 _thread_state = TS_wait; 02543 } 02544 02545 //////////////////////////////////////////////////////////////////// 02546 // Function: GraphicsEngine::RenderThread::thread_main 02547 // Access: Public, Virtual 02548 // Description: The main loop for a particular render thread. The 02549 // thread will process whatever cull or draw windows it 02550 // has assigned to it. 02551 //////////////////////////////////////////////////////////////////// 02552 void GraphicsEngine::RenderThread:: 02553 thread_main() { 02554 Thread *current_thread = Thread::get_current_thread(); 02555 02556 MutexHolder holder(_cv_mutex); 02557 while (true) { 02558 nassertv(_cv_mutex.debug_is_locked()); 02559 02560 switch (_thread_state) { 02561 case TS_wait: 02562 break; 02563 02564 case TS_do_frame: 02565 do_pending(_engine, current_thread); 02566 do_frame(_engine, current_thread); 02567 break; 02568 02569 case TS_do_flip: 02570 do_flip(_engine, current_thread); 02571 break; 02572 02573 case TS_do_release: 02574 do_pending(_engine, current_thread); 02575 break; 02576 02577 case TS_do_windows: 02578 do_windows(_engine, current_thread); 02579 do_pending(_engine, current_thread); 02580 break; 02581 02582 case TS_terminate: 02583 do_pending(_engine, current_thread); 02584 do_close(_engine, current_thread); 02585 _thread_state = TS_done; 02586 _cv_done.notify(); 02587 return; 02588 02589 case TS_done: 02590 // Shouldn't be possible to get here. 02591 nassertv(false); 02592 return; 02593 } 02594 02595 _thread_state = TS_wait; 02596 _cv_done.notify(); 02597 02598 { 02599 PStatTimer timer(_wait_pcollector, current_thread); 02600 _cv_start.wait(); 02601 } 02602 } 02603 }