Panda3D
 All Classes Functions Variables Enumerations
graphicsEngine.cxx
1 // Filename: graphicsEngine.cxx
2 // Created by: drose (24Feb02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "graphicsEngine.h"
16 #include "graphicsPipe.h"
17 #include "parasiteBuffer.h"
18 #include "config_gobj.h"
19 #include "config_display.h"
20 #include "pipeline.h"
21 #include "drawCullHandler.h"
22 #include "binCullHandler.h"
23 #include "cullResult.h"
24 #include "cullTraverser.h"
25 #include "clockObject.h"
26 #include "pStatTimer.h"
27 #include "pStatGPUTimer.h"
28 #include "pStatClient.h"
29 #include "pStatCollector.h"
30 #include "mutexHolder.h"
31 #include "reMutexHolder.h"
32 #include "lightReMutexHolder.h"
33 #include "cullFaceAttrib.h"
34 #include "string_utils.h"
35 #include "geomCacheManager.h"
36 #include "renderState.h"
37 #include "transformState.h"
38 #include "thread.h"
39 #include "pipeline.h"
40 #include "throw_event.h"
41 #include "bamCache.h"
42 #include "cullableObject.h"
43 #include "geomVertexArrayData.h"
44 #include "omniBoundingVolume.h"
45 #include "vertexDataSaveFile.h"
46 #include "vertexDataBook.h"
47 #include "vertexDataPage.h"
48 #include "config_pgraph.h"
49 #include "displayRegionCullCallbackData.h"
50 #include "displayRegionDrawCallbackData.h"
51 #include "callbackGraphicsWindow.h"
52 
53 #if defined(WIN32)
54  #define WINDOWS_LEAN_AND_MEAN
55  #include <WinSock2.h>
56  #include <wtypes.h>
57  #undef WINDOWS_LEAN_AND_MEAN
58 #else
59  #include <sys/time.h>
60 #endif
61 
62 PT(GraphicsEngine) GraphicsEngine::_global_ptr;
63 
64 PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync");
65 PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle");
66 PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General");
67 PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame");
68 PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame");
69 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
70 PStatCollector GraphicsEngine::_cull_pcollector("Cull");
71 PStatCollector GraphicsEngine::_cull_setup_pcollector("Cull:Setup");
72 PStatCollector GraphicsEngine::_cull_sort_pcollector("Cull:Sort");
73 PStatCollector GraphicsEngine::_draw_pcollector("Draw");
74 PStatCollector GraphicsEngine::_sync_pcollector("Draw:Sync");
75 PStatCollector GraphicsEngine::_flip_pcollector("Wait:Flip");
76 PStatCollector GraphicsEngine::_flip_begin_pcollector("Wait:Flip:Begin");
77 PStatCollector GraphicsEngine::_flip_end_pcollector("Wait:Flip:End");
78 PStatCollector GraphicsEngine::_transform_states_pcollector("TransformStates");
79 PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformStates:Unused");
80 PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates");
81 PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused");
82 PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers");
83 PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers");
84 PStatCollector GraphicsEngine::_delete_pcollector("App:Delete");
85 
86 
87 PStatCollector GraphicsEngine::_sw_sprites_pcollector("SW Sprites");
88 PStatCollector GraphicsEngine::_vertex_data_small_pcollector("Vertex Data:Small");
89 PStatCollector GraphicsEngine::_vertex_data_independent_pcollector("Vertex Data:Independent");
90 PStatCollector GraphicsEngine::_vertex_data_pending_pcollector("Vertex Data:Pending");
91 PStatCollector GraphicsEngine::_vertex_data_resident_pcollector("Vertex Data:Resident");
92 PStatCollector GraphicsEngine::_vertex_data_compressed_pcollector("Vertex Data:Compressed");
93 PStatCollector GraphicsEngine::_vertex_data_unused_disk_pcollector("Vertex Data:Disk:Unused");
94 PStatCollector GraphicsEngine::_vertex_data_used_disk_pcollector("Vertex Data:Disk:Used");
95 
96 // These are counted independently by the collision system; we
97 // redefine them here so we can reset them at each frame.
98 PStatCollector GraphicsEngine::_cnode_volume_pcollector("Collision Volumes:CollisionNode");
99 PStatCollector GraphicsEngine::_gnode_volume_pcollector("Collision Volumes:GeomNode");
100 PStatCollector GraphicsEngine::_geom_volume_pcollector("Collision Volumes:Geom");
101 PStatCollector GraphicsEngine::_node_volume_pcollector("Collision Volumes:PandaNode");
102 PStatCollector GraphicsEngine::_volume_pcollector("Collision Volumes:CollisionSolid");
103 PStatCollector GraphicsEngine::_test_pcollector("Collision Tests:CollisionSolid");
104 PStatCollector GraphicsEngine::_volume_polygon_pcollector("Collision Volumes:CollisionPolygon");
105 PStatCollector GraphicsEngine::_test_polygon_pcollector("Collision Tests:CollisionPolygon");
106 PStatCollector GraphicsEngine::_volume_plane_pcollector("Collision Volumes:CollisionPlane");
107 PStatCollector GraphicsEngine::_test_plane_pcollector("Collision Tests:CollisionPlane");
108 PStatCollector GraphicsEngine::_volume_sphere_pcollector("Collision Volumes:CollisionSphere");
109 PStatCollector GraphicsEngine::_test_sphere_pcollector("Collision Tests:CollisionSphere");
110 PStatCollector GraphicsEngine::_volume_box_pcollector("Collision Volumes:CollisionBox");
111 PStatCollector GraphicsEngine::_test_box_pcollector("Collision Tests:CollisionBox");
112 PStatCollector GraphicsEngine::_volume_tube_pcollector("Collision Volumes:CollisionTube");
113 PStatCollector GraphicsEngine::_test_tube_pcollector("Collision Tests:CollisionTube");
114 PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:CollisionInvSphere");
115 PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere");
116 PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom");
117 PStatCollector GraphicsEngine::_test_geom_pcollector("Collision Tests:CollisionGeom");
118 PStatCollector GraphicsEngine::_occlusion_untested_pcollector("Occlusion results:Not tested");
119 PStatCollector GraphicsEngine::_occlusion_passed_pcollector("Occlusion results:Visible");
120 PStatCollector GraphicsEngine::_occlusion_failed_pcollector("Occlusion results:Occluded");
121 PStatCollector GraphicsEngine::_occlusion_tests_pcollector("Occlusion tests");
122 
123 ////////////////////////////////////////////////////////////////////
124 // Function: GraphicsEngine::Constructor
125 // Access: Published
126 // Description: Creates a new GraphicsEngine object. The Pipeline is
127 // normally left to default to NULL, which indicates the
128 // global render pipeline, but it may be any Pipeline
129 // you choose.
130 ////////////////////////////////////////////////////////////////////
133  _pipeline(pipeline),
134  _app("app"),
135  _lock("GraphicsEngine::_lock"),
136  _loaded_textures_lock("GraphicsEngine::_loaded_textures_lock")
137 {
138  if (_pipeline == (Pipeline *)NULL) {
139  _pipeline = Pipeline::get_render_pipeline();
140  }
141 
142  _windows_sorted = true;
143  _window_sort_index = 0;
144  _needs_open_windows = false;
145 
147  if (!_threading_model.is_default()) {
148  display_cat.info()
149  << "Using threading model " << _threading_model << "\n";
150  }
151  _auto_flip = auto_flip;
152  _portal_enabled = false;
153  _flip_state = FS_flip;
154 
155  _singular_warning_last_frame = false;
156  _singular_warning_this_frame = false;
157 }
158 
159 ////////////////////////////////////////////////////////////////////
160 // Function: GraphicsEngine::Destructor
161 // Access: Published
162 // Description: Gracefully cleans up the graphics engine and its
163 // related threads and windows.
164 ////////////////////////////////////////////////////////////////////
167 #ifdef DO_PSTATS
168  if (_app_pcollector.is_started()) {
169  _app_pcollector.stop();
170  }
171 #endif
172 
174 }
175 
176 ////////////////////////////////////////////////////////////////////
177 // Function: GraphicsEngine::set_threading_model
178 // Access: Published
179 // Description: Specifies how future objects created via make_gsg(),
180 // make_buffer(), and make_window() will be threaded.
181 // This does not affect any already-created objects.
182 ////////////////////////////////////////////////////////////////////
183 void GraphicsEngine::
186  if (!threading_model.is_single_threaded()) {
187  display_cat.warning()
188  << "Threading model " << threading_model
189  << " requested but threading is not available. Ignoring.\n";
190  return;
191  }
192  }
193 
194 #ifndef THREADED_PIPELINE
195  if (!threading_model.is_single_threaded()) {
196  display_cat.warning()
197  << "Threading model " << threading_model
198  << " requested but multithreaded render pipelines not enabled in build.\n";
199  if (!allow_nonpipeline_threads) {
200  display_cat.warning()
201  << "Ignoring requested threading model.\n";
202  return;
203  }
204  display_cat.warning()
205  << "Danger! Creating requested render threads anyway!\n";
206  }
207 #endif // THREADED_PIPELINE
208  ReMutexHolder holder(_lock);
209  _threading_model = threading_model;
210 }
211 
212 ////////////////////////////////////////////////////////////////////
213 // Function: GraphicsEngine::get_threading_model
214 // Access: Published
215 // Description: Returns the threading model that will be applied to
216 // future objects. See set_threading_model().
217 ////////////////////////////////////////////////////////////////////
220  GraphicsThreadingModel result;
221  {
222  ReMutexHolder holder(_lock);
223  result = _threading_model;
224  }
225  return result;
226 }
227 
228 // THIS IS THE OLD CODE FOR make_gsg
229 // PT(GraphicsStateGuardian) gsg = pipe->make_gsg(properties, share_with);
230 
231 
232 
233 ////////////////////////////////////////////////////////////////////
234 // Function: GraphicsEngine::make_output
235 // Access: Published
236 // Description: Creates a new window (or buffer) and returns it.
237 // The GraphicsEngine becomes the owner of the window,
238 // it will persist at least until remove_window() is
239 // called later.
240 //
241 // If a null pointer is supplied for the gsg, then this
242 // routine will create a new gsg.
243 //
244 // This routine is only called from the app thread.
245 ////////////////////////////////////////////////////////////////////
246 
249  const string &name, int sort,
250  const FrameBufferProperties &fb_prop,
251  const WindowProperties &win_prop,
252  int flags,
254  GraphicsOutput *host) {
255 
256  // The code here is tricky because the gsg that is passed in
257  // might be in the uninitialized state. As a result,
258  // pipe::make_output may not be able to tell which DirectX
259  // capabilities or OpenGL extensions are supported and which
260  // are not. Worse yet, it can't query the API, because that
261  // can only be done from the draw thread, and this is the app
262  // thread.
263  //
264  // So here's the workaround: this routine calls pipe::make_output,
265  // which returns a "non-certified" window. That means that
266  // the pipe doesn't promise that the draw thread will actually
267  // succeed in initializing the window. This routine then calls
268  // open_windows, which attempts to initialize the window.
269  //
270  // If open_windows fails to initialize the window, then
271  // this routine will ask pipe::make_output to try again, this
272  // time using a different set of OpenGL extensions or DirectX
273  // capabilities. This is what the "retry" parameter to
274  // pipe::make_output is for - it specifies, in an abstract
275  // manner, which set of capabilties/extensions to try.
276  //
277  // The only problem with this design is that it requires the
278  // engine to call open_windows, which is slow. To make
279  // things faster, the pipe can choose to "precertify"
280  // its creations. If it chooses to do so, this is a guarantee
281  // that the windows it returns will not fail in open_windows.
282  // However, most graphics pipes will only precertify if you
283  // pass them an already-initialized gsg. Long story short,
284  // if you want make_output to be fast, use an
285  // already-initialized gsg.
286 
287  // Simplify the input parameters.
288 
289  int x_size = 0, y_size = 0;
290  if (win_prop.has_size()) {
291  x_size = win_prop.get_x_size();
292  y_size = win_prop.get_y_size();
293  }
294  if ((x_size == 0)&&(y_size == 0)) {
295  flags |= GraphicsPipe::BF_size_track_host;
296  }
297  if (host != 0) {
298  host = host->get_host();
299  }
300 
301  // If a gsg or host was supplied, and either is not yet initialized,
302  // then call open_windows to get both ready. If that fails,
303  // give up on using the supplied gsg and host.
304 
305  if (host == (GraphicsOutput *)NULL) {
306  if (gsg != (GraphicsStateGuardian*)NULL) {
307  if ((!gsg->is_valid())||(gsg->needs_reset())) {
308  open_windows();
309  }
310  if ((!gsg->is_valid())||(gsg->needs_reset())) {
311  gsg = NULL;
312  }
313  }
314  } else {
315  if ((host->get_gsg()==0)||
316  (!host->is_valid())||
317  (!host->get_gsg()->is_valid())||
318  (host->get_gsg()->needs_reset())) {
319  open_windows();
320  }
321  if ((host->get_gsg()==0)||
322  (!host->is_valid())||
323  (!host->get_gsg()->is_valid())||
324  (host->get_gsg()->needs_reset())) {
325  host = NULL;
326  gsg = NULL;
327  } else {
328  gsg = host->get_gsg();
329  }
330  }
331 
332  // Sanity check everything.
333 
334  GraphicsThreadingModel threading_model = get_threading_model();
335  nassertr(pipe != (GraphicsPipe *)NULL, NULL);
336  if (gsg != (GraphicsStateGuardian *)NULL) {
337  nassertr(pipe == gsg->get_pipe(), NULL);
338  nassertr(this == gsg->get_engine(), NULL);
339  nassertr(threading_model.get_draw_name() ==
340  gsg->get_threading_model().get_draw_name(), NULL);
341  }
342 
343  // Are we really asking for a callback window?
344  if ((flags & GraphicsPipe::BF_require_callback_window)!=0) {
345  PT(GraphicsStateGuardian) this_gsg = gsg;
346  if (this_gsg == (GraphicsStateGuardian *)NULL) {
347  // If we don't already have a GSG, we have to ask the pipe to
348  // make a new one, unencumbered by window dressing.
349  this_gsg = pipe->make_callback_gsg(this);
350  }
351  if (this_gsg != (GraphicsStateGuardian *)NULL) {
352  CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg);
353  window->_sort = sort;
354  do_add_window(window, threading_model);
355  do_add_gsg(window->get_gsg(), pipe, threading_model);
356  display_cat.info() << "Created output of type CallbackGraphicsWindow\n";
357  return window;
358  }
359 
360  // Couldn't make a callback window, because the pipe wouldn't make
361  // an unencumbered GSG.
362  return NULL;
363  }
364 
365  // Determine if a parasite buffer meets the user's specs.
366 
367  bool can_use_parasite = false;
368  if ((host != 0)&&
369  ((flags&GraphicsPipe::BF_require_window)==0)&&
370  ((flags&GraphicsPipe::BF_require_callback_window)==0)&&
371  ((flags&GraphicsPipe::BF_refuse_parasite)==0)&&
372  ((flags&GraphicsPipe::BF_can_bind_color)==0)&&
373  ((flags&GraphicsPipe::BF_can_bind_every)==0)&&
374  ((flags&GraphicsPipe::BF_rtt_cumulative)==0)&&
375  ((flags&GraphicsPipe::BF_can_bind_layered)==0)) {
376  if ((flags&GraphicsPipe::BF_fb_props_optional) ||
377  (host->get_fb_properties().subsumes(fb_prop))) {
378  can_use_parasite = true;
379  }
380  }
381 
382  // If parasite buffers are preferred, then try a parasite first.
383  // Even if prefer-parasite-buffer is set, parasites are not preferred
384  // if the host window is too small, or if the host window does not
385  // have the requested properties.
386 
387  if ((prefer_parasite_buffer) &&
388  (can_use_parasite) &&
389  (x_size <= host->get_x_size())&&
390  (y_size <= host->get_y_size())&&
391  (host->get_fb_properties().subsumes(fb_prop))) {
392  ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
393  buffer->_sort = sort;
394  do_add_window(buffer, threading_model);
395  do_add_gsg(host->get_gsg(), pipe, threading_model);
396  display_cat.info() << "Created output of type ParasiteBuffer\n";
397  return buffer;
398  }
399 
400  // If force-parasite-buffer is set, we create a parasite buffer even
401  // if it's less than ideal. You might set this if you really don't
402  // trust your graphics driver's support for offscreen buffers.
403  if (force_parasite_buffer && can_use_parasite) {
404  ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
405  buffer->_sort = sort;
406  do_add_window(buffer, threading_model);
407  do_add_gsg(host->get_gsg(), pipe, threading_model);
408  display_cat.info() << "Created output of type ParasiteBuffer\n";
409  return buffer;
410  }
411 
412  // Ask the pipe to create a window.
413 
414  for (int retry=0; retry<10; retry++) {
415  bool precertify = false;
416  PT(GraphicsOutput) window =
417  pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify);
418  if (window != (GraphicsOutput *)NULL) {
419  window->_sort = sort;
420  if ((precertify) && (gsg != 0) && (window->get_gsg()==gsg)) {
421  do_add_window(window, threading_model);
422  do_add_gsg(window->get_gsg(), pipe, threading_model);
423  display_cat.info()
424  << "Created output of type " << window->get_type() << "\n";
425  return window;
426  }
427  do_add_window(window, threading_model);
428  open_windows();
429  if (window->is_valid()) {
430  do_add_gsg(window->get_gsg(), pipe, threading_model);
431  display_cat.info()
432  << "Created output of type " << window->get_type() << "\n";
433 
434  if (window->get_fb_properties().subsumes(fb_prop)) {
435  return window;
436  } else {
437  if (flags & GraphicsPipe::BF_fb_props_optional) {
438  display_cat.warning()
439  << "FrameBufferProperties available less than requested.\n";
440  return window;
441  }
442  display_cat.error()
443  << "Could not get requested FrameBufferProperties; abandoning window.\n";
444  display_cat.error(false)
445  << " requested: " << fb_prop << "\n"
446  << " got: " << window->get_fb_properties() << "\n";
447  }
448  } else {
449  display_cat.info()
450  << window->get_type() << " wouldn't open; abandoning.\n";
451  display_cat.debug(false)
452  << " requested: " << fb_prop << "\n";
453  }
454 
455  // No good; delete the window and keep trying.
456  bool removed = remove_window(window);
457  nassertr(removed, NULL);
458  }
459  }
460 
461  // Parasite buffers were not preferred, but the pipe could not
462  // create a window to the user's specs. Try a parasite as a
463  // last hope.
464 
465  if (can_use_parasite) {
466  ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
467  buffer->_sort = sort;
468  do_add_window(buffer, threading_model);
469  do_add_gsg(host->get_gsg(), pipe, threading_model);
470  display_cat.info() << "Created output of type ParasiteBuffer\n";
471  return buffer;
472  }
473 
474  // Could not create a window to the user's specs.
475 
476  return NULL;
477 }
478 
479 ////////////////////////////////////////////////////////////////////
480 // Function: GraphicsEngine::add_window
481 // Access: Published
482 // Description: This can be used to add a newly-created
483 // GraphicsOutput object (and its GSG) to the engine's
484 // list of windows, and requests that it be opened.
485 // This shouldn't be called by user code as
486 // make_output normally does this under the hood; it
487 // may be useful in esoteric cases in which a custom
488 // window object is used.
489 ////////////////////////////////////////////////////////////////////
490 bool GraphicsEngine::
491 add_window(GraphicsOutput *window, int sort) {
492  nassertr(window != NULL, false);
493 
494  GraphicsThreadingModel threading_model = get_threading_model();
495  nassertr(this == window->get_engine(), false);
496 
497  window->_sort = sort;
498  do_add_window(window, threading_model);
499 
500  open_windows();
501  if (window->is_valid()) {
502  do_add_gsg(window->get_gsg(), window->get_pipe(), threading_model);
503 
504  display_cat.info()
505  << "Added output of type " << window->get_type() << "\n";
506 
507  return true;
508 
509  } else {
510  remove_window(window);
511  return false;
512  }
513 }
514 
515 ////////////////////////////////////////////////////////////////////
516 // Function: GraphicsEngine::remove_window
517 // Access: Published
518 // Description: Removes the indicated window or offscreen buffer from
519 // the set of windows that will be processed when
520 // render_frame() is called. This also closes the
521 // window if it is open, and removes the window from its
522 // GraphicsPipe, allowing the window to be destructed if
523 // there are no other references to it. (However, the
524 // window may not be actually closed until next frame,
525 // if it is controlled by a sub-thread.)
526 //
527 // The return value is true if the window was removed,
528 // false if it was not found.
529 //
530 // Unlike remove_all_windows(), this function does not
531 // terminate any of the threads that may have been
532 // started to service this window; they are left running
533 // (since you might open a new window later on these
534 // threads). If your intention is to clean up before
535 // shutting down, it is better to call
536 // remove_all_windows() then to call remove_window() one
537 // at a time.
538 ////////////////////////////////////////////////////////////////////
539 bool GraphicsEngine::
541  nassertr(window != NULL, false);
542  Thread *current_thread = Thread::get_current_thread();
543 
544  // First, make sure we know what this window is.
545  PT(GraphicsOutput) ptwin = window;
546  size_t count;
547  {
548  ReMutexHolder holder(_lock, current_thread);
549  if (!_windows_sorted) {
550  do_resort_windows();
551  }
552  count = _windows.erase(ptwin);
553  }
554  if (count == 0) {
555  // Never heard of this window. Do nothing.
556  return false;
557  }
558 
559  do_remove_window(window, current_thread);
560 
561  GraphicsStateGuardian *gsg = window->get_gsg();
562  if (gsg != (GraphicsStateGuardian *)NULL) {
564  if (pgo != (PreparedGraphicsObjects *)NULL) {
565  // Check to see if any other still-active windows share this
566  // context.
567  bool any_common = false;
568  {
569  ReMutexHolder holder(_lock, current_thread);
570  Windows::iterator wi;
571  for (wi = _windows.begin(); wi != _windows.end() && !any_common; ++wi) {
572  GraphicsStateGuardian *gsg2 = (*wi)->get_gsg();
573  if (gsg2 != (GraphicsStateGuardian *)NULL &&
574  gsg2->get_prepared_objects() == pgo) {
575  any_common = true;
576  }
577  }
578  }
579  if (!any_common) {
580  // If no windows still use this context, release all textures,
581  // etc. We do this in case there is a floating pointer
582  // somewhere keeping the GSG from destructing when its window
583  // goes away. A leaked GSG pointer is bad enough, but there's
584  // no reason we also need to keep around all of the objects
585  // allocated on graphics memory.
586  pgo->release_all();
587  }
588  }
589  }
590 
591  nassertr(count == 1, true);
592  return true;
593 }
594 
595 ////////////////////////////////////////////////////////////////////
596 // Function: GraphicsEngine::remove_all_windows
597 // Access: Published
598 // Description: Removes and closes all windows from the engine. This
599 // also cleans up and terminates any threads that have
600 // been started to service those windows.
601 ////////////////////////////////////////////////////////////////////
602 void GraphicsEngine::
604  Thread *current_thread = Thread::get_current_thread();
605 
606  // Let's move the _windows vector into a local copy first, and walk
607  // through that local copy, just in case someone we call during the
608  // loop attempts to modify _windows. I don't know what code would
609  // be doing this, but it appeared to be happening, and this worked
610  // around it.
611  Windows old_windows;
612  old_windows.swap(_windows);
613  Windows::iterator wi;
614  for (wi = old_windows.begin(); wi != old_windows.end(); ++wi) {
615  GraphicsOutput *win = (*wi);
616  nassertv(win != NULL);
617  do_remove_window(win, current_thread);
618  GraphicsStateGuardian *gsg = win->get_gsg();
619  if (gsg != (GraphicsStateGuardian *)NULL) {
620  gsg->release_all();
621  }
622  }
623 
624  _app.do_close(this, current_thread);
625  _app.do_pending(this, current_thread);
626  terminate_threads(current_thread);
627 
628  // It seems a safe assumption that we're about to exit the
629  // application or otherwise shut down Panda. Although it's a bit of
630  // a hack, since it's not really related to removing windows, this
631  // would nevertheless be a fine time to ensure the model cache (if
632  // any) has been flushed to disk.
634 
635  // And, hey, let's stop the vertex paging threads, if any.
637 
639 
640 #ifdef DO_PSTATS
641  PStatClient::get_global_pstats()->disconnect();
642 #endif
643 
644  // Well, and why not clean up all threads here?
646 }
647 
648 ////////////////////////////////////////////////////////////////////
649 // Function: GraphicsEngine::reset_all_windows
650 // Access: Published
651 // Description: Resets the framebuffer of the current window. This
652 // is currently used by DirectX 8 only. It calls a
653 // reset_window function on each active window to
654 // release/create old/new framebuffer
655 ////////////////////////////////////////////////////////////////////
656 void GraphicsEngine::
657 reset_all_windows(bool swapchain) {
658  Windows::iterator wi;
659  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
660  GraphicsOutput *win = (*wi);
661  // if (win->is_active())
662  win->reset_window(swapchain);
663  }
664 }
665 
666 ////////////////////////////////////////////////////////////////////
667 // Function: GraphicsEngine::is_empty
668 // Access: Published
669 // Description: Returns true if there are no windows or buffers
670 // managed by the engine, false if there is at least
671 // one.
672 ////////////////////////////////////////////////////////////////////
673 bool GraphicsEngine::
674 is_empty() const {
675  return _windows.empty();
676 }
677 
678 ////////////////////////////////////////////////////////////////////
679 // Function: GraphicsEngine::get_num_windows
680 // Access: Published
681 // Description: Returns the number of windows (or buffers) managed by
682 // the engine.
683 ////////////////////////////////////////////////////////////////////
686  return _windows.size();
687 }
688 
689 ////////////////////////////////////////////////////////////////////
690 // Function: GraphicsEngine::get_window
691 // Access: Published
692 // Description: Returns the nth window or buffers managed by the
693 // engine, in sorted order.
694 ////////////////////////////////////////////////////////////////////
696 get_window(int n) const {
697  nassertr(n >= 0 && n < (int)_windows.size(), NULL);
698 
699  if (!_windows_sorted) {
700  ((GraphicsEngine *)this)->do_resort_windows();
701  }
702  return _windows[n];
703 }
704 
705 ////////////////////////////////////////////////////////////////////
706 // Function: GraphicsEngine::render_frame
707 // Access: Published
708 // Description: Renders the next frame in all the registered windows,
709 // and flips all of the frame buffers.
710 ////////////////////////////////////////////////////////////////////
711 void GraphicsEngine::
713  Thread *current_thread = Thread::get_current_thread();
714  ReMutexHolder public_holder(_public_lock);
715 
716  // Since this gets called every frame, we should take advantage of
717  // the opportunity to flush the cache if necessary.
719 
720  // Anything that happens outside of GraphicsEngine::render_frame()
721  // is deemed to be App.
722 #ifdef DO_PSTATS
723  _render_frame_pcollector.start();
724  if (_app_pcollector.is_started()) {
725  _app_pcollector.stop();
726  }
727 #endif
728 
729  if (_needs_open_windows) {
730  // Make sure our buffers and windows are fully realized before we
731  // render a frame. We do this particularly to realize our
732  // offscreen buffers, so that we don't render a frame before the
733  // offscreen buffers are ready (which might result in a frame
734  // going by without some textures having been rendered).
735  open_windows();
736  }
737 
738  ClockObject *global_clock = ClockObject::get_global_clock();
739 
740  if (display_cat.is_spam()) {
741  display_cat.spam()
742  << "render_frame() - frame " << global_clock->get_frame_count() << "\n";
743  }
744 
745  {
746  ReMutexHolder holder(_lock, current_thread);
747 
748  if (!_windows_sorted) {
749  do_resort_windows();
750  }
751 
752  if (sync_flip && _flip_state != FS_flip) {
753  do_flip_frame(current_thread);
754  }
755 
756  // Are any of the windows ready to be deleted?
757  Windows new_windows;
758  new_windows.reserve(_windows.size());
759  Windows::iterator wi;
760  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
761  GraphicsOutput *win = (*wi);
762  nassertv(win != NULL);
763  if (win->get_delete_flag()) {
764  do_remove_window(win, current_thread);
765 
766  } else {
767  new_windows.push_back(win);
768 
769  // Let's calculate each scene's bounding volume here in App,
770  // before we cycle the pipeline. The cull traversal will
771  // calculate it anyway, but if we calculate it in App first
772  // before it gets calculated in the Cull thread, it will be more
773  // likely to stick for subsequent frames, so we won't have to
774  // recompute it each frame.
775  int num_drs = win->get_num_active_display_regions();
776  for (int i = 0; i < num_drs; ++i) {
777  DisplayRegion *dr = win->get_active_display_region(i);
778  if (dr != (DisplayRegion *)NULL) {
779  NodePath camera_np = dr->get_camera(current_thread);
780  if (!camera_np.is_empty()) {
781  Camera *camera = DCAST(Camera, camera_np.node());
782  NodePath scene = camera->get_scene();
783  if (scene.is_empty()) {
784  scene = camera_np.get_top(current_thread);
785  }
786  if (!scene.is_empty()) {
787  scene.get_bounds(current_thread);
788  }
789  }
790  }
791  }
792  }
793  }
794  _windows.swap(new_windows);
795 
796  // Go ahead and release any textures' ram images for textures that
797  // were drawn in the previous frame.
798  {
799  MutexHolder holder2(_loaded_textures_lock);
800  LoadedTextures::iterator lti;
801  for (lti = _loaded_textures.begin(); lti != _loaded_textures.end(); ++lti) {
802  LoadedTexture &lt = (*lti);
803  if (lt._tex->get_image_modified() == lt._image_modified) {
804  lt._tex->texture_uploaded();
805  }
806  }
807  _loaded_textures.clear();
808  }
809 
810  // Now it's time to do any drawing from the main frame--after all of
811  // the App code has executed, but before we begin the next frame.
812  _app.do_frame(this, current_thread);
813 
814  // Grab each thread's mutex again after all windows have flipped,
815  // and wait for the thread to finish.
816  {
817  PStatTimer timer(_wait_pcollector, current_thread);
818  Threads::const_iterator ti;
819  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
820  RenderThread *thread = (*ti).second;
821  thread->_cv_mutex.acquire();
822 
823  while (thread->_thread_state != TS_wait) {
824  thread->_cv_done.wait();
825  }
826  }
827  }
828 
829 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS)
830  _cyclers_pcollector.set_level(_pipeline->get_num_cyclers());
831  _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers());
832 
833 #ifdef DEBUG_THREADS
835  _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this);
836  _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this);
837  }
838 #endif // DEBUG_THREADS
839 
840 #endif // THREADED_PIPELINE && DO_PSTATS
841 
845  TransformState::flush_level();
847 
848  // Now cycle the pipeline and officially begin the next frame.
849 #ifdef THREADED_PIPELINE
850  {
851  PStatTimer timer(_cycle_pcollector, current_thread);
852  _pipeline->cycle();
853  }
854 #endif // THREADED_PIPELINE
855 
856  global_clock->tick(current_thread);
857  if (global_clock->check_errors(current_thread)) {
858  throw_event("clock_error");
859  }
860 
861 #ifdef DO_PSTATS
862  PStatClient::main_tick();
863 
864  // Reset our pcollectors that track data across the frame.
865  CullTraverser::_nodes_pcollector.clear_level();
866  CullTraverser::_geom_nodes_pcollector.clear_level();
867  CullTraverser::_geoms_pcollector.clear_level();
868  GeomCacheManager::_geom_cache_active_pcollector.clear_level();
869  GeomCacheManager::_geom_cache_record_pcollector.clear_level();
870  GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
871  GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
872 
873  GraphicsStateGuardian::init_frame_pstats();
874 
875  _transform_states_pcollector.set_level(TransformState::get_num_states());
876  _render_states_pcollector.set_level(RenderState::get_num_states());
877  if (pstats_unused_states) {
878  _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states());
879  _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states());
880  }
881 
882  _sw_sprites_pcollector.clear_level();
883 
884  _cnode_volume_pcollector.clear_level();
885  _gnode_volume_pcollector.clear_level();
886  _geom_volume_pcollector.clear_level();
887  _node_volume_pcollector.clear_level();
888  _volume_pcollector.clear_level();
889  _test_pcollector.clear_level();
890  _volume_polygon_pcollector.clear_level();
891  _test_polygon_pcollector.clear_level();
892  _volume_plane_pcollector.clear_level();
893  _test_plane_pcollector.clear_level();
894  _volume_sphere_pcollector.clear_level();
895  _test_sphere_pcollector.clear_level();
896  _volume_box_pcollector.clear_level();
897  _test_box_pcollector.clear_level();
898  _volume_tube_pcollector.clear_level();
899  _test_tube_pcollector.clear_level();
900  _volume_inv_sphere_pcollector.clear_level();
901  _test_inv_sphere_pcollector.clear_level();
902  _volume_geom_pcollector.clear_level();
903  _test_geom_pcollector.clear_level();
904  _occlusion_untested_pcollector.clear_level();
905  _occlusion_passed_pcollector.clear_level();
906  _occlusion_failed_pcollector.clear_level();
907  _occlusion_tests_pcollector.clear_level();
908 
910  size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
912  size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size();
913  size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size();
914  size_t pending = VertexDataPage::get_pending_lru()->get_total_size();
915 
917  size_t total_disk = save_file->get_total_file_size();
918  size_t used_disk = save_file->get_used_file_size();
919 
920  _vertex_data_small_pcollector.set_level(small_buf);
921  _vertex_data_independent_pcollector.set_level(independent);
922  _vertex_data_pending_pcollector.set_level(pending);
923  _vertex_data_resident_pcollector.set_level(resident);
924  _vertex_data_compressed_pcollector.set_level(compressed);
925  _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk);
926  _vertex_data_used_disk_pcollector.set_level(used_disk);
927  }
928 
929 #endif // DO_PSTATS
930 
932 
933  // Now signal all of our threads to begin their next frame.
934  Threads::const_iterator ti;
935  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
936  RenderThread *thread = (*ti).second;
937  if (thread->_thread_state == TS_wait) {
938  thread->_thread_state = TS_do_frame;
939  thread->_cv_start.notify();
940  }
941  thread->_cv_mutex.release();
942  }
943 
944  // Some threads may still be drawing, so indicate that we have to
945  // wait for those threads before we can flip.
946  _flip_state = _auto_flip ? FS_flip : FS_draw;
947  }
948 
949  // Now the lock is released.
950 
951  if (yield_timeslice) {
952  // Nap for a moment to yield the timeslice, to be polite to other
953  // running applications.
954  PStatTimer timer(_yield_pcollector, current_thread);
956  } else if (!Thread::is_true_threads()) {
957  PStatTimer timer(_yield_pcollector, current_thread);
959  }
960 
961  // Anything that happens outside of GraphicsEngine::render_frame()
962  // is deemed to be App.
963  _app_pcollector.start();
964  _render_frame_pcollector.stop();
965 }
966 
967 
968 ////////////////////////////////////////////////////////////////////
969 // Function: GraphicsEngine::open_windows
970 // Access: Published
971 // Description: Fully opens (or closes) any windows that have
972 // recently been requested open or closed, without
973 // rendering any frames. It is not necessary to call
974 // this explicitly, since windows will be automatically
975 // opened or closed when the next frame is rendered, but
976 // you may call this if you want your windows now
977 // without seeing a frame go by.
978 ////////////////////////////////////////////////////////////////////
979 void GraphicsEngine::
981  Thread *current_thread = Thread::get_current_thread();
982 
983  ReMutexHolder holder(_lock, current_thread);
984 
985  if (!_windows_sorted) {
986  do_resort_windows();
987  }
988 
989  // We do it twice, to allow both cull and draw to process the
990  // window.
991  for (int i = 0; i < 2; ++i) {
992  _app.do_windows(this, current_thread);
993  _app.do_pending(this, current_thread);
994 
995  PStatTimer timer(_wait_pcollector, current_thread);
996  Threads::const_iterator ti;
997  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
998  RenderThread *thread = (*ti).second;
999  thread->_cv_mutex.acquire();
1000 
1001  while (thread->_thread_state != TS_wait) {
1002  thread->_cv_done.wait();
1003  }
1004 
1005  thread->_thread_state = TS_do_windows;
1006  thread->_cv_start.notify();
1007  thread->_cv_mutex.release();
1008  }
1009  }
1010 
1011  _needs_open_windows = false;
1012 }
1013 
1014 ////////////////////////////////////////////////////////////////////
1015 // Function: GraphicsEngine::sync_frame
1016 // Access: Published
1017 // Description: Waits for all the threads that started drawing their
1018 // last frame to finish drawing. The windows are not
1019 // yet flipped when this returns; see also flip_frame().
1020 // It is not usually necessary to call this explicitly,
1021 // unless you need to see the previous frame right away.
1022 ////////////////////////////////////////////////////////////////////
1023 void GraphicsEngine::
1025  Thread *current_thread = Thread::get_current_thread();
1026  ReMutexHolder holder(_lock, current_thread);
1027 
1028  if (_flip_state == FS_draw) {
1029  do_sync_frame(current_thread);
1030  }
1031 }
1032 
1033 
1034 ////////////////////////////////////////////////////////////////////
1035 // Function: GraphicsEngine::ready_flip
1036 // Access: Published
1037 // Description: Waits for all the threads that started drawing their
1038 // last frame to finish drawing. Returns when all threads have
1039 // actually finished drawing, as opposed to 'sync_frame'
1040 // we seems to return once all draw calls have been submitted.
1041 // Calling 'flip_frame' after this function should immediately
1042 // cause a buffer flip. This function will only work in
1043 // opengl right now, for all other graphics pipelines it will
1044 // simply return immediately. In opengl it's a bit of a hack:
1045 // it will attempt to read a single pixel from the frame buffer to
1046 // force the graphics card to finish drawing before it returns
1047 ////////////////////////////////////////////////////////////////////
1048 void GraphicsEngine::
1050  Thread *current_thread = Thread::get_current_thread();
1051  ReMutexHolder holder(_lock, current_thread);
1052 
1053  if (_flip_state == FS_draw) {
1054  do_ready_flip(current_thread);
1055  }
1056 }
1057 
1058 ////////////////////////////////////////////////////////////////////
1059 // Function: GraphicsEngine::flip_frame
1060 // Access: Published
1061 // Description: Waits for all the threads that started drawing their
1062 // last frame to finish drawing, and then flips all the
1063 // windows. It is not usually necessary to call this
1064 // explicitly, unless you need to see the previous frame
1065 // right away.
1066 ////////////////////////////////////////////////////////////////////
1067 void GraphicsEngine::
1069  Thread *current_thread = Thread::get_current_thread();
1070  ReMutexHolder holder(_lock, current_thread);
1071 
1072  if (_flip_state != FS_flip) {
1073  do_flip_frame(current_thread);
1074  }
1075 }
1076 
1077 ////////////////////////////////////////////////////////////////////
1078 // Function: GraphicsEngine::extract_texture_data
1079 // Access: Published
1080 // Description: Asks the indicated GraphicsStateGuardian to retrieve
1081 // the texture memory image of the indicated texture and
1082 // store it in the texture's ram_image field. The image
1083 // can then be written to disk via Texture::write(), or
1084 // otherwise manipulated on the CPU.
1085 //
1086 // This is useful for retrieving the contents of a
1087 // texture that has been somehow generated on the
1088 // graphics card, instead of having been loaded the
1089 // normal way via Texture::read() or Texture::load().
1090 // It is particularly useful for getting the data
1091 // associated with a compressed texture image.
1092 //
1093 // Since this requires a round-trip to the draw thread,
1094 // it may require waiting for the current thread to
1095 // finish rendering if it is called in a multithreaded
1096 // environment. However, you can call this several
1097 // consecutive times on different textures for little
1098 // additional cost.
1099 //
1100 // If the texture has not yet been loaded to the GSG in
1101 // question, it will be loaded immediately.
1102 //
1103 // The return value is true if the operation is
1104 // successful, false otherwise.
1105 ////////////////////////////////////////////////////////////////////
1106 bool GraphicsEngine::
1108  ReMutexHolder holder(_lock);
1109 
1110  string draw_name = gsg->get_threading_model().get_draw_name();
1111  if (draw_name.empty()) {
1112  // A single-threaded environment. No problem.
1113  return gsg->extract_texture_data(tex);
1114 
1115  } else {
1116  // A multi-threaded environment. We have to wait until the draw
1117  // thread has finished its current task.
1118  WindowRenderer *wr = get_window_renderer(draw_name, 0);
1119  RenderThread *thread = (RenderThread *)wr;
1120  MutexHolder holder2(thread->_cv_mutex);
1121 
1122  while (thread->_thread_state != TS_wait) {
1123  thread->_cv_done.wait();
1124  }
1125 
1126  // OK, now the draw thread is idle. That's really good enough for
1127  // our purposes; we don't *actually* need to make the draw thread
1128  // do the work--it's sufficient that it's not doing anything else
1129  // while we access the GSG.
1130  return gsg->extract_texture_data(tex);
1131  }
1132 }
1133 
1134 ////////////////////////////////////////////////////////////////////
1135 // Function: GraphicsEngine::dispatch_compute
1136 // Access: Published
1137 // Description: Asks the indicated GraphicsStateGuardian to dispatch
1138 // the compute shader in the given ShaderAttrib using
1139 // the given work group counts. This can act as an
1140 // interface for running a one-off compute shader,
1141 // without having to store it in the scene graph using
1142 // a ComputeNode.
1143 //
1144 // Since this requires a round-trip to the draw thread,
1145 // it may require waiting for the current thread to
1146 // finish rendering if it is called in a multithreaded
1147 // environment. However, you can call this several
1148 // consecutive times on different textures for little
1149 // additional cost.
1150 //
1151 // The return value is true if the operation is
1152 // successful, false otherwise.
1153 ////////////////////////////////////////////////////////////////////
1154 void GraphicsEngine::
1155 dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
1156  ReMutexHolder holder(_lock);
1157 
1158  CPT(RenderState) state = RenderState::make(sattr);
1159 
1160  string draw_name = gsg->get_threading_model().get_draw_name();
1161  if (draw_name.empty()) {
1162  // A single-threaded environment. No problem.
1163 
1164  } else {
1165  // A multi-threaded environment. We have to wait until the draw
1166  // thread has finished its current task.
1167  WindowRenderer *wr = get_window_renderer(draw_name, 0);
1168  RenderThread *thread = (RenderThread *)wr;
1169  MutexHolder holder2(thread->_cv_mutex);
1170 
1171  while (thread->_thread_state != TS_wait) {
1172  thread->_cv_done.wait();
1173  }
1174 
1175  // OK, now the draw thread is idle. That's really good enough for
1176  // our purposes; we don't *actually* need to make the draw thread
1177  // do the work--it's sufficient that it's not doing anything else
1178  // while we access the GSG.
1179  }
1180 
1181  gsg->set_state_and_transform(state, TransformState::make_identity());
1182  gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]);
1183 }
1184 
1185 ////////////////////////////////////////////////////////////////////
1186 // Function: GraphicsEngine::get_global_ptr
1187 // Access: Published, Static
1188 // Description:
1189 ////////////////////////////////////////////////////////////////////
1190 GraphicsEngine *GraphicsEngine::
1191 get_global_ptr() {
1192  if (_global_ptr == NULL) {
1193  _global_ptr = new GraphicsEngine;
1194  PandaNode::set_scene_root_func(&scene_root_func);
1195  }
1196  return _global_ptr;
1197 }
1198 
1199 ////////////////////////////////////////////////////////////////////
1200 // Function: GraphicsEngine::texture_uploaded
1201 // Access: Public
1202 // Description: This method is called by the GraphicsStateGuardian
1203 // after a texture has been successfully uploaded to
1204 // graphics memory. It is intended as a callback so the
1205 // texture can release its RAM image, if _keep_ram_image
1206 // is false.
1207 //
1208 // Normally, this is not called directly except by the
1209 // GraphicsStateGuardian. It will be called in the draw
1210 // thread.
1211 ////////////////////////////////////////////////////////////////////
1212 void GraphicsEngine::
1214  MutexHolder holder(_loaded_textures_lock);
1215  // We defer this until the end of the frame; multiple GSG's might be
1216  // rendering the texture within the same frame, and we don't want to
1217  // dump the texture image until they've all had a chance at it.
1218  _loaded_textures.push_back(LoadedTexture());
1219  LoadedTexture &lt = _loaded_textures.back();
1220  lt._tex = tex;
1221  lt._image_modified = tex->get_image_modified();
1222 // Usually only called by DisplayRegion::do_cull.
1223 }
1224 
1225 ////////////////////////////////////////////////////////////////////
1226 // Function: GraphicsEngine::do_cull
1227 // Access: Public, Static
1228 // Description: Fires off a cull traversal using the indicated camera.
1229 ////////////////////////////////////////////////////////////////////
1230 void GraphicsEngine::
1231 do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
1232  GraphicsStateGuardian *gsg, Thread *current_thread) {
1233  DisplayRegion *dr = scene_setup->get_display_region();
1234  PStatTimer timer(dr->get_cull_region_pcollector(), current_thread);
1235 
1236  CullTraverser *trav = dr->get_cull_traverser();
1237  trav->set_cull_handler(cull_handler);
1238  trav->set_scene(scene_setup, gsg, dr->get_incomplete_render());
1239 
1240  trav->set_view_frustum(NULL);
1241  if (view_frustum_cull) {
1242  // If we're to be performing view-frustum culling, determine the
1243  // bounding volume associated with the current viewing frustum.
1244 
1245  // First, we have to get the current viewing frustum, which comes
1246  // from the lens.
1247  PT(BoundingVolume) bv = scene_setup->get_cull_bounds();
1248 
1249  if (bv != (BoundingVolume *)NULL &&
1250  bv->is_of_type(GeometricBoundingVolume::get_class_type()) &&
1251  !bv->is_of_type(OmniBoundingVolume::get_class_type())) {
1252  // Transform it into the appropriate coordinate space.
1253  PT(GeometricBoundingVolume) local_frustum;
1254  local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
1255 
1256  NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
1257  CPT(TransformState) cull_center_transform =
1258  scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
1259  local_frustum->xform(cull_center_transform->get_mat());
1260 
1261  trav->set_view_frustum(local_frustum);
1262  }
1263  }
1264 
1265  trav->traverse(scene_setup->get_scene_root());
1266  trav->end_traverse();
1267 }
1268 
1269 
1270 ////////////////////////////////////////////////////////////////////
1271 // Function: GraphicsEngine::scene_root_func
1272 // Access: Private, Static
1273 // Description: This function is added to PandaNode::scene_root_func
1274 // to implement PandaNode::is_scene_root().
1275 ////////////////////////////////////////////////////////////////////
1276 bool GraphicsEngine::
1277 scene_root_func(const PandaNode *node) {
1278  return _global_ptr->is_scene_root(node);
1279 }
1280 
1281 ////////////////////////////////////////////////////////////////////
1282 // Function: GraphicsEngine::is_scene_root
1283 // Access: Private
1284 // Description: Returns true if the indicated node is known to be
1285 // the render root of some active DisplayRegion
1286 // associated with this GraphicsEngine, false otherwise.
1287 ////////////////////////////////////////////////////////////////////
1288 bool GraphicsEngine::
1289 is_scene_root(const PandaNode *node) {
1290  Thread *current_thread = Thread::get_current_thread();
1291 
1292  Windows::const_iterator wi;
1293  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
1294  GraphicsOutput *win = (*wi);
1295  if (win->is_active() && win->get_gsg()->is_active()) {
1296  int num_display_regions = win->get_num_active_display_regions();
1297  for (int i = 0; i < num_display_regions; i++) {
1298  DisplayRegion *dr = win->get_active_display_region(i);
1299  if (dr != (DisplayRegion *)NULL) {
1300  NodePath camera = dr->get_camera();
1301  if (camera.is_empty()) {
1302  continue;
1303  }
1304 
1305  Camera *camera_node;
1306  DCAST_INTO_R(camera_node, camera.node(), false);
1307  if (!camera_node->is_active()) {
1308  continue;
1309  }
1310 
1311  NodePath scene_root = camera_node->get_scene();
1312  if (scene_root.is_empty()) {
1313  // If there's no explicit scene specified, use whatever scene the
1314  // camera is parented within. This is the normal and preferred
1315  // case; the use of an explicit scene is now deprecated.
1316  scene_root = camera.get_top(current_thread);
1317  }
1318 
1319  if (scene_root.node() == node) {
1320  return true;
1321  }
1322  }
1323  }
1324  }
1325  }
1326 
1327  return false;
1328 }
1329 
1330 ////////////////////////////////////////////////////////////////////
1331 // Function: GraphicsEngine::set_window_sort
1332 // Access: Private
1333 // Description: Changes the sort value of a particular window (or
1334 // buffer) on the GraphicsEngine. This requires
1335 // securing the mutex.
1336 //
1337 // Users shouldn't call this directly; use
1338 // GraphicsOutput::set_sort() instead.
1339 ////////////////////////////////////////////////////////////////////
1340 void GraphicsEngine::
1341 set_window_sort(GraphicsOutput *window, int sort) {
1342  ReMutexHolder holder(_lock);
1343  window->_sort = sort;
1344  _windows_sorted = false;
1345 }
1346 
1347 ////////////////////////////////////////////////////////////////////
1348 // Function: GraphicsEngine::cull_and_draw_together
1349 // Access: Private
1350 // Description: This is called in the cull+draw thread by individual
1351 // RenderThread objects during the frame rendering. It
1352 // culls the geometry and immediately draws it, without
1353 // first collecting it into bins. This is used when the
1354 // threading model begins with the "-" character.
1355 ////////////////////////////////////////////////////////////////////
1356 void GraphicsEngine::
1357 cull_and_draw_together(const GraphicsEngine::Windows &wlist,
1358  Thread *current_thread) {
1359  PStatTimer timer(_cull_pcollector, current_thread);
1360 
1361  size_t wlist_size = wlist.size();
1362  for (size_t wi = 0; wi < wlist_size; ++wi) {
1363  GraphicsOutput *win = wlist[wi];
1364  if (win->is_active() && win->get_gsg()->is_active()) {
1365  if (win->flip_ready()) {
1366  {
1367  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1368  win->begin_flip();
1369  }
1370  {
1371  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1372  win->end_flip();
1373  }
1374  }
1375 
1376  if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
1377  win->clear(current_thread);
1378 
1379  int num_display_regions = win->get_num_active_display_regions();
1380  for (int i = 0; i < num_display_regions; i++) {
1381  DisplayRegion *dr = win->get_active_display_region(i);
1382  if (dr != (DisplayRegion *)NULL) {
1383  cull_and_draw_together(win, dr, current_thread);
1384  }
1385  }
1386  win->end_frame(GraphicsOutput::FM_render, current_thread);
1387 
1388  if (_auto_flip) {
1389  if (win->flip_ready()) {
1390  {
1391  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1392  win->begin_flip();
1393  }
1394  {
1395  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1396  win->end_flip();
1397  }
1398  }
1399  }
1400  }
1401  }
1402  }
1403 }
1404 
1405 ////////////////////////////////////////////////////////////////////
1406 // Function: GraphicsEngine::cull_and_draw_together
1407 // Access: Private
1408 // Description: Called only from within the inner loop in
1409 // cull_and_draw_together(), above.
1410 ////////////////////////////////////////////////////////////////////
1411 void GraphicsEngine::
1412 cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
1413  Thread *current_thread) {
1414  GraphicsStateGuardian *gsg = win->get_gsg();
1415  nassertv(gsg != (GraphicsStateGuardian *)NULL);
1416 
1417  DisplayRegionPipelineReader *dr_reader =
1418  new DisplayRegionPipelineReader(dr, current_thread);
1419 
1420  win->change_scenes(dr_reader);
1421  gsg->prepare_display_region(dr_reader);
1422 
1423  if (dr_reader->is_any_clear_active()) {
1424  gsg->clear(dr);
1425  }
1426 
1427  PT(SceneSetup) scene_setup = setup_scene(gsg, dr_reader);
1428  if (scene_setup == (SceneSetup *)NULL) {
1429  // Never mind.
1430 
1431  } else if (dr_reader->get_object()->is_stereo()) {
1432  // Don't draw stereo DisplayRegions directly.
1433 
1434  } else if (!gsg->set_scene(scene_setup)) {
1435  // The scene or lens is inappropriate somehow.
1436  display_cat.error()
1437  << gsg->get_type() << " cannot render scene with specified lens.\n";
1438 
1439  } else {
1440  DrawCullHandler cull_handler(gsg);
1441  if (gsg->begin_scene()) {
1442  delete dr_reader;
1443  dr_reader = NULL;
1444 
1445  CallbackObject *cbobj = dr->get_cull_callback();
1446  if (cbobj != (CallbackObject *)NULL) {
1447  // Issue the cull callback on this DisplayRegion.
1448  DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
1449  cbobj->do_callback(&cbdata);
1450 
1451  // The callback has taken care of the culling.
1452 
1453  } else {
1454  // Perform the cull normally.
1455  dr->do_cull(&cull_handler, scene_setup, gsg, current_thread);
1456  }
1457 
1458  gsg->end_scene();
1459  }
1460  }
1461 
1462  if (dr_reader != (DisplayRegionPipelineReader *)NULL) {
1463  delete dr_reader;
1464  }
1465 }
1466 
1467 ////////////////////////////////////////////////////////////////////
1468 // Function: GraphicsEngine::cull_to_bins
1469 // Access: Private
1470 // Description: This is called in the cull thread by individual
1471 // RenderThread objects during the frame rendering. It
1472 // collects the geometry into bins in preparation for
1473 // drawing.
1474 ////////////////////////////////////////////////////////////////////
1475 void GraphicsEngine::
1476 cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1477  PStatTimer timer(_cull_pcollector, current_thread);
1478 
1479  _singular_warning_last_frame = _singular_warning_this_frame;
1480  _singular_warning_this_frame = false;
1481 
1482  // Keep track of the cameras we have already used in this thread to
1483  // render DisplayRegions.
1484  typedef pmap<NodePath, DisplayRegion *> AlreadyCulled;
1485  AlreadyCulled already_culled;
1486 
1487  size_t wlist_size = wlist.size();
1488  for (size_t wi = 0; wi < wlist_size; ++wi) {
1489  GraphicsOutput *win = wlist[wi];
1490  if (win->is_active() && win->get_gsg()->is_active()) {
1491  PStatTimer timer(win->get_cull_window_pcollector(), current_thread);
1492  int num_display_regions = win->get_num_active_display_regions();
1493  for (int i = 0; i < num_display_regions; ++i) {
1494  DisplayRegion *dr = win->get_active_display_region(i);
1495  if (dr != (DisplayRegion *)NULL) {
1496  DisplayRegionPipelineReader *dr_reader =
1497  new DisplayRegionPipelineReader(dr, current_thread);
1498  NodePath camera = dr_reader->get_camera();
1499  AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(camera, (DisplayRegion *)NULL)).first;
1500  if ((*aci).second == NULL) {
1501  // We have not used this camera already in this thread.
1502  // Perform the cull operation.
1503  delete dr_reader;
1504  dr_reader = NULL;
1505  (*aci).second = dr;
1506  cull_to_bins(win, dr, current_thread);
1507 
1508  } else {
1509  // We have already culled a scene using this camera in
1510  // this thread, and now we're being asked to cull another
1511  // scene using the same camera. (Maybe this represents
1512  // two different DisplayRegions for the left and right
1513  // channels of a stereo image.) Of course, the cull
1514  // result will be the same, so just use the result from
1515  // the other DisplayRegion.
1516  DisplayRegion *other_dr = (*aci).second;
1517  dr->set_cull_result(other_dr->get_cull_result(current_thread),
1518  setup_scene(win->get_gsg(), dr_reader),
1519  current_thread);
1520  }
1521 
1522  if (dr_reader != (DisplayRegionPipelineReader *)NULL) {
1523  delete dr_reader;
1524  }
1525  }
1526  }
1527  }
1528  }
1529 }
1530 
1531 ////////////////////////////////////////////////////////////////////
1532 // Function: GraphicsEngine::cull_to_bins
1533 // Access: Private
1534 // Description: Called only within the inner loop of cull_to_bins(),
1535 // above.
1536 ////////////////////////////////////////////////////////////////////
1537 void GraphicsEngine::
1538 cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
1539  GraphicsStateGuardian *gsg = win->get_gsg();
1540  if (gsg == (GraphicsStateGuardian *)NULL) {
1541  return;
1542  }
1543 
1544  PT(CullResult) cull_result;
1545  PT(SceneSetup) scene_setup;
1546  {
1547  PStatTimer timer(_cull_setup_pcollector, current_thread);
1548  DisplayRegionPipelineReader dr_reader(dr, current_thread);
1549  scene_setup = setup_scene(gsg, &dr_reader);
1550  cull_result = dr->get_cull_result(current_thread);
1551 
1552  if (cull_result != (CullResult *)NULL) {
1553  cull_result = cull_result->make_next();
1554 
1555  } else {
1556  // This DisplayRegion has no cull results; draw it.
1557  cull_result = new CullResult(gsg, dr->get_draw_region_pcollector());
1558  }
1559  }
1560 
1561  if (scene_setup != (SceneSetup *)NULL) {
1562  BinCullHandler cull_handler(cull_result);
1563  CallbackObject *cbobj = dr->get_cull_callback();
1564  if (cbobj != (CallbackObject *)NULL) {
1565  // Issue the cull callback on this DisplayRegion.
1566  DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
1567  cbobj->do_callback(&cbdata);
1568 
1569  // The callback has taken care of the culling.
1570 
1571  } else {
1572  // Perform the cull normally.
1573  dr->do_cull(&cull_handler, scene_setup, gsg, current_thread);
1574  }
1575 
1576  PStatTimer timer(_cull_sort_pcollector, current_thread);
1577  cull_result->finish_cull(scene_setup, current_thread);
1578  }
1579 
1580  // Save the results for next frame.
1581  dr->set_cull_result(cull_result, scene_setup, current_thread);
1582 }
1583 
1584 ////////////////////////////////////////////////////////////////////
1585 // Function: GraphicsEngine::draw_bins
1586 // Access: Private
1587 // Description: This is called in the draw thread by individual
1588 // RenderThread objects during the frame rendering. It
1589 // issues the graphics commands to draw the objects that
1590 // have been collected into bins by a previous call to
1591 // cull_to_bins().
1592 ////////////////////////////////////////////////////////////////////
1593 void GraphicsEngine::
1594 draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1595  nassertv(wlist.verify_list());
1596 
1597  size_t wlist_size = wlist.size();
1598  for (size_t wi = 0; wi < wlist_size; ++wi) {
1599  GraphicsOutput *win = wlist[wi];
1600 
1601  if (win->is_active()) {
1602  GraphicsStateGuardian *gsg = win->get_gsg();
1603 
1604  GraphicsOutput *host = win->get_host();
1605  if (host->flip_ready()) {
1606  {
1607  // We can't use a PStatGPUTimer before begin_frame, so when using GPU
1608  // timing, it is advisable to set auto-flip to #t.
1609  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1610  host->begin_flip();
1611  }
1612  {
1613  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1614  host->end_flip();
1615  }
1616  }
1617 
1618  if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
1619  // We have to place this collector inside begin_frame, because
1620  // we need a current context for PStatGPUTimer to work.
1621  {
1622  PStatGPUTimer timer(win->get_gsg(), win->get_draw_window_pcollector(), current_thread);
1623  win->clear(current_thread);
1624 
1625  if (display_cat.is_spam()) {
1626  display_cat.spam()
1627  << "Drawing window " << win->get_name() << "\n";
1628  }
1629  int num_display_regions = win->get_num_active_display_regions();
1630  for (int i = 0; i < num_display_regions; ++i) {
1631  DisplayRegion *dr = win->get_active_display_region(i);
1632  if (dr != (DisplayRegion *)NULL) {
1633  draw_bins(win, dr, current_thread);
1634  }
1635  }
1636  }
1637  win->end_frame(GraphicsOutput::FM_render, current_thread);
1638 
1639  if (_auto_flip) {
1640 #ifdef DO_PSTATS
1641  // This is a good time to perform a latency query.
1642  if (win->get_gsg()->get_timer_queries_active()) {
1643  win->get_gsg()->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index());
1644  }
1645 #endif
1646 
1647  if (win->flip_ready()) {
1648  {
1649  // begin_flip doesn't do anything interesting, let's not waste two timer queries on that.
1650  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1651  win->begin_flip();
1652  }
1653  {
1654  PStatGPUTimer timer(win->get_gsg(), GraphicsEngine::_flip_end_pcollector, current_thread);
1655  win->end_flip();
1656  }
1657  }
1658  }
1659 
1660  } else {
1661  if (display_cat.is_spam()) {
1662  display_cat.spam()
1663  << "Not drawing window " << win->get_name() << "\n";
1664  }
1665  }
1666 
1667  } else {
1668  if (display_cat.is_spam()) {
1669  display_cat.spam()
1670  << "Window " << win->get_name() << " is inactive\n";
1671  }
1672  }
1673  }
1674 }
1675 
1676 ////////////////////////////////////////////////////////////////////
1677 // Function: GraphicsEngine::draw_bins
1678 // Access: Private
1679 // Description: This variant on draw_bins() is only called from
1680 // draw_bins(), above. It draws the cull result for a
1681 // particular DisplayRegion.
1682 ////////////////////////////////////////////////////////////////////
1683 void GraphicsEngine::
1684 draw_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
1685  GraphicsStateGuardian *gsg = win->get_gsg();
1686  if (gsg == (GraphicsStateGuardian *)NULL) {
1687  return;
1688  }
1689 
1690  PT(CullResult) cull_result = dr->get_cull_result(current_thread);
1691  PT(SceneSetup) scene_setup = dr->get_scene_setup(current_thread);
1692  do_draw(cull_result, scene_setup, win, dr, current_thread);
1693 }
1694 
1695 ////////////////////////////////////////////////////////////////////
1696 // Function: GraphicsEngine::make_contexts
1697 // Access: Private
1698 // Description: Called in the draw thread, this calls make_context()
1699 // on each window on the list to guarantee its gsg and
1700 // graphics context both get created.
1701 ////////////////////////////////////////////////////////////////////
1702 void GraphicsEngine::
1703 make_contexts(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1704  Windows::const_iterator wi;
1705  for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
1706  GraphicsOutput *win = (*wi);
1707  if (win->begin_frame(GraphicsOutput::FM_refresh, current_thread)) {
1708  win->end_frame(GraphicsOutput::FM_refresh, current_thread);
1709  }
1710  }
1711 }
1712 
1713 ////////////////////////////////////////////////////////////////////
1714 // Function: GraphicsEngine::process_events
1715 // Access: Private
1716 // Description: This is called by the RenderThread object to process
1717 // all the windows events (resize, etc.) for the given
1718 // list of windows. This is run in the window thread.
1719 ////////////////////////////////////////////////////////////////////
1720 void GraphicsEngine::
1721 process_events(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1722  // We're not using a vector iterator here, since it's possible that
1723  // the window list changes in an event, which would invalidate the
1724  // iterator and cause a crash.
1725  for (size_t i = 0; i < wlist.size(); ++i) {
1726  wlist[i]->process_events();
1727  }
1728 }
1729 
1730 ////////////////////////////////////////////////////////////////////
1731 // Function: GraphicsEngine::flip_windows
1732 // Access: Private
1733 // Description: This is called by the RenderThread object to flip the
1734 // buffers for all of the non-single-buffered windows in
1735 // the given list. This is run in the draw thread.
1736 ////////////////////////////////////////////////////////////////////
1737 void GraphicsEngine::
1738 flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1739  size_t num_windows = wlist.size();
1740  size_t warray_size = num_windows * sizeof(GraphicsOutput *);
1741  size_t warray_count = 0;
1742  GraphicsOutput **warray = (GraphicsOutput **)alloca(warray_size);
1743 
1744  size_t i;
1745  for (i = 0; i < num_windows; ++i) {
1746  GraphicsOutput *win = wlist[i];
1747  if (win->flip_ready()) {
1748  nassertv(warray_count < num_windows);
1749  warray[warray_count] = win;
1750  ++warray_count;
1751 
1752  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1753  win->begin_flip();
1754  }
1755  }
1756 
1757  for (i = 0; i < warray_count; ++i) {
1758  GraphicsOutput *win = warray[i];
1759  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1760  win->end_flip();
1761  }
1762 }
1763 
1764 ////////////////////////////////////////////////////////////////////
1765 // Function: GraphicsEngine::ready_flip_windows
1766 // Access: Private
1767 // Description: This is called by the RenderThread object to flip the
1768 // buffers for all of the non-single-buffered windows in
1769 // the given list. This is run in the draw thread.
1770 ////////////////////////////////////////////////////////////////////
1771 void GraphicsEngine::
1772 ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1773  Windows::const_iterator wi;
1774  for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
1775  GraphicsOutput *win = (*wi);
1776  if (win->flip_ready()) {
1777  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1778  win->ready_flip();
1779  }
1780  }
1781 }
1782 
1783 ////////////////////////////////////////////////////////////////////
1784 // Function: GraphicsEngine::do_sync_frame
1785 // Access: Private
1786 // Description: The implementation of sync_frame(). We assume _lock
1787 // is already held before this method is called.
1788 ////////////////////////////////////////////////////////////////////
1789 void GraphicsEngine::
1790 do_sync_frame(Thread *current_thread) {
1791  nassertv(_lock.debug_is_locked());
1792 
1793  // Statistics
1794  PStatTimer timer(_sync_pcollector, current_thread);
1795 
1796  nassertv(_flip_state == FS_draw);
1797 
1798  // Wait for all the threads to finish their current frame. Grabbing
1799  // and releasing the mutex should achieve that.
1800  Threads::const_iterator ti;
1801  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1802  RenderThread *thread = (*ti).second;
1803  thread->_cv_mutex.acquire();
1804  thread->_cv_mutex.release();
1805  }
1806 
1807  _flip_state = FS_sync;
1808 }
1809 
1810 ////////////////////////////////////////////////////////////////////
1811 // Function: GraphicsEngine::do_ready_flip
1812 // Access: Private
1813 // Description: Wait until all draw calls have finished drawing and
1814 // the frame is ready to flip
1815 ////////////////////////////////////////////////////////////////////
1816 void GraphicsEngine::
1817 do_ready_flip(Thread *current_thread) {
1818  nassertv(_lock.debug_is_locked());
1819 
1820  // Statistics
1821  PStatTimer timer(_sync_pcollector, current_thread);
1822 
1823  nassertv(_flip_state == FS_draw);
1824 
1825  // Wait for all the threads to finish their current frame. Grabbing
1826  // and releasing the mutex should achieve that.
1827  Threads::const_iterator ti;
1828  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1829  RenderThread *thread = (*ti).second;
1830  thread->_cv_mutex.acquire();
1831  thread->_cv_mutex.release();
1832  }
1833  _app.do_ready_flip(this,current_thread);
1834  _flip_state = FS_sync;
1835 }
1836 
1837 ////////////////////////////////////////////////////////////////////
1838 // Function: GraphicsEngine::do_flip_frame
1839 // Access: Private
1840 // Description: The implementation of flip_frame(). We assume _lock
1841 // is already held before this method is called.
1842 ////////////////////////////////////////////////////////////////////
1843 void GraphicsEngine::
1844 do_flip_frame(Thread *current_thread) {
1845  nassertv(_lock.debug_is_locked());
1846 
1847  // Statistics
1848  PStatTimer timer(_flip_pcollector, current_thread);
1849 
1850  nassertv(_flip_state == FS_draw || _flip_state == FS_sync);
1851 
1852  // First, wait for all the threads to finish their current frame, if
1853  // necessary. Grabbing the mutex (and waiting for TS_wait) should
1854  // achieve that.
1855  {
1856  PStatTimer timer(_wait_pcollector, current_thread);
1857  Threads::const_iterator ti;
1858  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1859  RenderThread *thread = (*ti).second;
1860  thread->_cv_mutex.acquire();
1861 
1862  while (thread->_thread_state != TS_wait) {
1863  thread->_cv_done.wait();
1864  }
1865  }
1866  }
1867 
1868  // Now signal all of our threads to flip the windows.
1869  _app.do_flip(this, current_thread);
1870 
1871  {
1872  Threads::const_iterator ti;
1873  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1874  RenderThread *thread = (*ti).second;
1875  nassertv(thread->_thread_state == TS_wait);
1876  thread->_thread_state = TS_do_flip;
1877  thread->_cv_start.notify();
1878  thread->_cv_mutex.release();
1879  }
1880  }
1881 
1882  _flip_state = FS_flip;
1883 }
1884 
1885 ////////////////////////////////////////////////////////////////////
1886 // Function: GraphicsEngine::setup_scene
1887 // Access: Private
1888 // Description: Returns a new SceneSetup object appropriate for
1889 // rendering the scene from the indicated camera, or
1890 // NULL if the scene should not be rendered for some
1891 // reason.
1892 ////////////////////////////////////////////////////////////////////
1894 setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
1895  Thread *current_thread = dr->get_current_thread();
1896  PStatTimer timer(_cull_setup_pcollector, current_thread);
1897 
1898  GraphicsOutput *window = dr->get_window();
1899  // The window pointer shouldn't be NULL, since we presumably got to
1900  // this particular DisplayRegion by walking through a list on a
1901  // window.
1902  nassertr(window != (GraphicsOutput *)NULL, NULL);
1903 
1904  NodePath camera = dr->get_camera();
1905  if (camera.is_empty()) {
1906  // No camera, no draw.
1907  return NULL;
1908  }
1909 
1910  Camera *camera_node;
1911  DCAST_INTO_R(camera_node, camera.node(), NULL);
1912 
1913  if (!camera_node->is_active()) {
1914  // Camera inactive, no draw.
1915  return NULL;
1916  }
1917  camera_node->cleanup_aux_scene_data(current_thread);
1918 
1919  int lens_index = dr->get_lens_index();
1920  Lens *lens = camera_node->get_lens(lens_index);
1921  if (lens == (Lens *)NULL || !camera_node->get_lens_active(lens_index)) {
1922  // No lens, no draw.
1923  return NULL;
1924  }
1925 
1926  NodePath scene_root = camera_node->get_scene();
1927  if (scene_root.is_empty()) {
1928  // If there's no explicit scene specified, use whatever scene the
1929  // camera is parented within. This is the normal and preferred
1930  // case; the use of an explicit scene is now deprecated.
1931  scene_root = camera.get_top(current_thread);
1932  }
1933 
1934  PT(SceneSetup) scene_setup = new SceneSetup;
1935 
1936  // We will need both the camera transform (the net transform to the
1937  // camera from the scene) and the world transform (the camera
1938  // transform inverse, or the net transform to the scene from the
1939  // camera). These are actually defined from the parent of the
1940  // scene_root, because the scene_root's own transform is immediately
1941  // applied to these during rendering. (Normally, the parent of the
1942  // scene_root is the empty NodePath, although it need not be.)
1943  NodePath scene_parent = scene_root.get_parent(current_thread);
1944  CPT(TransformState) camera_transform = camera.get_transform(scene_parent, current_thread);
1945  CPT(TransformState) world_transform = scene_parent.get_transform(camera, current_thread);
1946 
1947  if (camera_transform->is_invalid()) {
1948  // There must be a singular transform over the scene.
1949  if (!_singular_warning_last_frame) {
1950  display_cat.warning()
1951  << "Scene " << scene_root << " has net scale ("
1952  << scene_root.get_scale(NodePath()) << "); cannot render.\n";
1953  _singular_warning_this_frame = true;
1954  }
1955  return NULL;
1956  }
1957 
1958  if (world_transform->is_invalid()) {
1959  // There must be a singular transform over the camera.
1960  if (!_singular_warning_last_frame) {
1961  display_cat.warning()
1962  << "Camera " << camera << " has net scale ("
1963  << camera.get_scale(NodePath()) << "); cannot render.\n";
1964  }
1965  _singular_warning_this_frame = true;
1966  return NULL;
1967  }
1968 
1969  CPT(RenderState) initial_state = camera_node->get_initial_state();
1970 
1971  if (window->get_inverted()) {
1972  // If the window is to be inverted, we must set the inverted flag
1973  // on the SceneSetup object, so that the GSG will be able to
1974  // invert the projection matrix at the last minute.
1975  scene_setup->set_inverted(true);
1976 
1977  // This also means we need to globally invert the sense of polygon
1978  // vertex ordering.
1979  initial_state = initial_state->compose(get_invert_polygon_state());
1980  }
1981 
1982  scene_setup->set_display_region(dr->get_object());
1983  scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height());
1984  scene_setup->set_scene_root(scene_root);
1985  scene_setup->set_camera_path(camera);
1986  scene_setup->set_camera_node(camera_node);
1987  scene_setup->set_lens(lens);
1988  scene_setup->set_initial_state(initial_state);
1989  scene_setup->set_camera_transform(camera_transform);
1990  scene_setup->set_world_transform(world_transform);
1991 
1992  CPT(TransformState) cs_transform = gsg->get_cs_transform_for(lens->get_coordinate_system());
1993  scene_setup->set_cs_transform(cs_transform);
1994 
1995  CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform);
1996  scene_setup->set_cs_world_transform(cs_world_transform);
1997 
1998  // Make sure that the GSG has a ShaderGenerator for the munger
1999  // to use. We have to do this here because the ShaderGenerator
2000  // needs a host window pointer. Hopefully we'll be able to
2001  // eliminate that requirement in the future.
2002  if (gsg->get_shader_generator() == NULL) {
2003  gsg->set_shader_generator(new ShaderGenerator(gsg, window));
2004  }
2005 
2006  return scene_setup;
2007 }
2008 
2009 ////////////////////////////////////////////////////////////////////
2010 // Function: GraphicsEngine::do_draw
2011 // Access: Private
2012 // Description: Draws the previously-culled scene.
2013 ////////////////////////////////////////////////////////////////////
2014 void GraphicsEngine::
2015 do_draw(CullResult *cull_result, SceneSetup *scene_setup,
2016  GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
2017 
2018  CallbackObject *cbobj;
2019  GraphicsStateGuardian *gsg = win->get_gsg();
2020 
2021  // Statistics
2022  PStatGPUTimer timer(gsg, dr->get_draw_region_pcollector(), current_thread);
2023 
2024  {
2025  DisplayRegionPipelineReader dr_reader(dr, current_thread);
2026  win->change_scenes(&dr_reader);
2027  gsg->prepare_display_region(&dr_reader);
2028  if (dr_reader.is_any_clear_active()) {
2029  gsg->clear(dr_reader.get_object());
2030  }
2031 
2032  cbobj = dr_reader.get_draw_callback();
2033  }
2034 
2035  if (cbobj != (CallbackObject *)NULL) {
2036  // Issue the draw callback on this DisplayRegion.
2037 
2038  // Set the GSG to the initial state.
2039  gsg->clear_before_callback();
2040  gsg->set_state_and_transform(RenderState::make_empty(), TransformState::make_identity());
2041 
2042  DisplayRegionDrawCallbackData cbdata(cull_result, scene_setup);
2043  cbobj->do_callback(&cbdata);
2044 
2045  // We don't trust the state the callback may have left us in.
2047 
2048  // The callback has taken care of the drawing.
2049  return;
2050  }
2051 
2052  if (cull_result == NULL || scene_setup == NULL) {
2053  // Nothing to see here.
2054 
2055  } else if (dr->is_stereo()) {
2056  // We don't actually draw the stereo DisplayRegions. These are
2057  // just placeholders; we draw the individual left and right eyes
2058  // instead. (We might still clear the stereo DisplayRegions,
2059  // though, since it's probably faster to clear right and left
2060  // channels in one pass, than to clear them in two separate
2061  // passes.)
2062 
2063  } else if (!gsg->set_scene(scene_setup)) {
2064  // The scene or lens is inappropriate somehow.
2065  display_cat.error()
2066  << gsg->get_type() << " cannot render scene with specified lens.\n";
2067 
2068  } else {
2069  if (gsg->begin_scene()) {
2070  cull_result->draw(current_thread);
2071  gsg->end_scene();
2072  }
2073  }
2074 }
2075 
2076 ////////////////////////////////////////////////////////////////////
2077 // Function: GraphicsEngine::do_add_window
2078 // Access: Private
2079 // Description: An internal function called by make_window() and
2080 // make_buffer() and similar functions to add the
2081 // newly-created GraphicsOutput object to the engine's
2082 // list of windows, and to request that the window be
2083 // opened.
2084 ////////////////////////////////////////////////////////////////////
2085 void GraphicsEngine::
2086 do_add_window(GraphicsOutput *window,
2087  const GraphicsThreadingModel &threading_model) {
2088  nassertv(window != NULL);
2089  ReMutexHolder holder(_lock);
2090  nassertv(window->get_engine() == this);
2091 
2092  // We have a special counter that is unique per window that allows
2093  // us to assure that recently-added windows end up on the end of the
2094  // list.
2095  window->_internal_sort_index = _window_sort_index;
2096  ++_window_sort_index;
2097 
2098  _windows_sorted = false;
2099  _windows.push_back(window);
2100 
2101  WindowRenderer *cull =
2102  get_window_renderer(threading_model.get_cull_name(),
2103  threading_model.get_cull_stage());
2104  WindowRenderer *draw =
2105  get_window_renderer(threading_model.get_draw_name(),
2106  threading_model.get_draw_stage());
2107 
2108  if (threading_model.get_cull_sorting()) {
2109  cull->add_window(cull->_cull, window);
2110  draw->add_window(draw->_draw, window);
2111  } else {
2112  cull->add_window(cull->_cdraw, window);
2113  }
2114 
2115  // Ask the pipe which thread it prefers to run its windowing
2116  // commands in (the "window thread"). This is the thread that
2117  // handles the commands to open, resize, etc. the window. X
2118  // requires this to be done in the app thread (along with all the
2119  // other windows, since X is strictly single-threaded), but Windows
2120  // requires this to be done in draw (because once an OpenGL context
2121  // has been bound in a given thread, it cannot subsequently be bound
2122  // in any other thread, and we have to bind a context in
2123  // open_window()).
2124 
2125  switch (window->get_pipe()->get_preferred_window_thread()) {
2126  case GraphicsPipe::PWT_app:
2127  _app.add_window(_app._window, window);
2128  break;
2129 
2130  case GraphicsPipe::PWT_draw:
2131  draw->add_window(draw->_window, window);
2132  break;
2133  }
2134 
2135  if (display_cat.is_debug()) {
2136  display_cat.debug()
2137  << "Created " << window->get_type() << " " << (void *)window << "\n";
2138  }
2139 
2140  window->request_open();
2141  _needs_open_windows = true;
2142 }
2143 
2144 ////////////////////////////////////////////////////////////////////
2145 // Function: GraphicsEngine::do_add_gsg
2146 // Access: Private
2147 // Description: An internal function called by make_output to add
2148 // the newly-created gsg object to the engine's
2149 // list of gsg's. It also adjusts various config
2150 // variables based on the gsg's capabilities.
2151 ////////////////////////////////////////////////////////////////////
2152 void GraphicsEngine::
2153 do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
2154  const GraphicsThreadingModel &threading_model) {
2155  nassertv(gsg != NULL);
2156 
2157  ReMutexHolder holder(_lock);
2158  nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this);
2159  gsg->_threading_model = threading_model;
2160  if (!_default_loader.is_null()) {
2161  gsg->set_loader(_default_loader);
2162  }
2163 
2164  auto_adjust_capabilities(gsg);
2165 
2166  WindowRenderer *draw =
2167  get_window_renderer(threading_model.get_draw_name(),
2168  threading_model.get_draw_stage());
2169 
2170  draw->add_gsg(gsg);
2171 }
2172 
2173 ////////////////////////////////////////////////////////////////////
2174 // Function: GraphicsEngine::do_remove_window
2175 // Access: Private
2176 // Description: An internal function called by remove_window() and
2177 // remove_all_windows() to actually remove the indicated
2178 // window from all relevant structures, except the
2179 // _windows list itself.
2180 ////////////////////////////////////////////////////////////////////
2181 void GraphicsEngine::
2182 do_remove_window(GraphicsOutput *window, Thread *current_thread) {
2183  nassertv(window != NULL);
2184  PT(GraphicsPipe) pipe = window->get_pipe();
2185  window->clear_pipe();
2186 
2187  if (!_windows_sorted) {
2188  do_resort_windows();
2189  }
2190 
2191  // Now remove the window from all threads that know about it.
2192  _app.remove_window(window);
2193  Threads::const_iterator ti;
2194  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2195  RenderThread *thread = (*ti).second;
2196  thread->remove_window(window);
2197  }
2198 
2199  // If the window happened to be controlled by the app thread, we
2200  // might as well close it now rather than waiting for next frame.
2201  _app.do_pending(this, current_thread);
2202 
2203  if (display_cat.is_debug()) {
2204  display_cat.debug()
2205  << "Removed " << window->get_type() << " " << (void *)window << "\n";
2206  }
2207 }
2208 
2209 ////////////////////////////////////////////////////////////////////
2210 // Function: GraphicsEngine::do_resort_windows
2211 // Access: Private
2212 // Description: Resorts all of the Windows lists. This may need to
2213 // be done if one or more of the windows' sort
2214 // properties has changed.
2215 ////////////////////////////////////////////////////////////////////
2216 void GraphicsEngine::
2217 do_resort_windows() {
2218  _windows_sorted = true;
2219 
2220  _app.resort_windows();
2221  Threads::const_iterator ti;
2222  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2223  RenderThread *thread = (*ti).second;
2224  thread->resort_windows();
2225  }
2226 
2227  _windows.sort();
2228 }
2229 
2230 ////////////////////////////////////////////////////////////////////
2231 // Function: GraphicsEngine::auto_adjust_capabilities
2232 // Access: Private
2233 // Description: Video card capability flags are stored on a
2234 // per-gsg basis. However, there are a few cases
2235 // where panda needs to know not the capabilities
2236 // of an individual GSG, but rather, the
2237 // collective capabilities of all the GSGs.
2238 //
2239 // Non-power-of-two (NPOT) texture support is the
2240 // classic example. Panda makes a single global
2241 // decision to either create NPOT textures, or not.
2242 // Therefore, it doesn't need to know whether one GSG
2243 // supports NPOT textures. It needs to know whether ALL
2244 // the GSGs support NPOT textures.
2245 //
2246 // The purpose of this routine is to maintain global
2247 // capability flags that summarize the collective
2248 // capabilities of the computer as a whole.
2249 //
2250 // These global capability flags are initialized from
2251 // config variables. Then, they can be auto-reconfigured
2252 // using built-in heuristic mechanisms if the user so
2253 // desires. Whether auto-reconfiguration is enabled or
2254 // not, the configured values are checked against
2255 // the actual capabilities of the machine and error
2256 // messages will be printed if there is a mismatch.
2257 //
2258 ////////////////////////////////////////////////////////////////////
2259 void GraphicsEngine::
2260 auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
2261 
2262  // The rule we use when auto-reconfiguring is as follows. The
2263  // global capabilities must initially be set to conservative
2264  // values. When the first GSG comes into existence, its
2265  // capabilities will be checked, and the global capabilities
2266  // may be elevated to more aggressive values.
2267  //
2268  // At first glance, this might seem backward, and it might seem
2269  // better to do it the other way: start with all global capabilities
2270  // aggressively set, and then disable capabilities when you discover
2271  // a gsg that doesn't support them.
2272  //
2273  // However, that approach doesn't work, because once a global
2274  // capability is enabled, there is no going back. If
2275  // textures_power_2 has ever been set to 'none', there may be NPOT
2276  // textures already floating about the system. Ie, it's too late:
2277  // you can't turn these global capability flags off, once they've
2278  // been turned on.
2279  //
2280  // That's why we have to start with conservative settings, and then
2281  // elevate those settings to more aggressive values later when
2282  // we're fairly sure it's OK to do so.
2283  //
2284  // For each global capability, we must:
2285  // 1. Make sure the initial setting is conservative.
2286  // 2. Possibly elevate to a more aggressive value.
2287  // 3. Check that we haven't over-elevated.
2288  //
2289 
2290  if (textures_auto_power_2 && (textures_power_2 == ATS_none)) {
2291  display_cat.error()
2292  << "Invalid panda config file: if you set the config-variable\n"
2293  << "textures_auto_power_2 to true, you must set the config-variable"
2294  << "textures_power_2 to 'up' or 'down'.\n";
2295  textures_power_2 = ATS_down; // Not a fix. Just suppresses further error messages.
2296  }
2297 
2298  if (textures_auto_power_2 && !Texture::has_textures_power_2()) {
2299  if (gsg->get_supports_tex_non_pow2()) {
2301  } else {
2302  Texture::set_textures_power_2(textures_power_2);
2303  }
2304  }
2305 
2306  if ((Texture::get_textures_power_2() == ATS_none) &&
2307  (!gsg->get_supports_tex_non_pow2())) {
2308 
2309  // Overaggressive configuration detected
2310 
2311  display_cat.error()
2312  << "The 'textures_power_2' configuration is set to 'none', meaning \n"
2313  << "that non-power-of-two texture support is required, but the video \n"
2314  << "driver I'm trying to use does not support non-power-of-two textures.\n";
2315 
2316  if (textures_power_2 != ATS_none) {
2317  display_cat.error()
2318  << "The 'none' did not come from the config file. In other words,\n"
2319  << "the variable 'textures_power_2' was altered procedurally.\n";
2320 
2321  if (textures_auto_power_2) {
2322  display_cat.error()
2323  << "It is possible that it was set by panda's automatic mechanisms,\n"
2324  << "which are currently enabled, because 'textures_auto_power_2' is\n"
2325  << "true. Panda's automatic mechanisms assume that if one\n"
2326  << "window supports non-power-of-two textures, then they all will.\n"
2327  << "This assumption works for most games, but not all.\n"
2328  << "In particular, it can fail if the game creates multiple windows\n"
2329  << "on multiple displays with different video cards.\n";
2330  }
2331  }
2332  }
2333 
2334  if (shader_auto_utilization && (shader_utilization != SUT_none)) {
2335  display_cat.error()
2336  << "Invalid panda config file: if you set the config-variable\n"
2337  << "shader_auto_utilization to true, you must set the config-variable"
2338  << "shader_utilization to 'none'.\n";
2339  shader_utilization = SUT_none; // Not a fix. Just suppresses further error messages.
2340  }
2341 
2342  if (shader_auto_utilization && !Shader::have_shader_utilization()) {
2343  if (gsg->get_supports_basic_shaders()) {
2344  Shader::set_shader_utilization(SUT_basic);
2345  } else {
2347  }
2348  }
2349 
2350  if ((Shader::get_shader_utilization() != SUT_none) &&
2351  (!gsg->get_supports_basic_shaders())) {
2352 
2353  // Overaggressive configuration detected
2354 
2355  display_cat.error()
2356  << "The 'shader_utilization' config variable is set, meaning\n"
2357  << "that panda may try to generate shaders. However, the video \n"
2358  << "driver I'm trying to use does not support shaders.\n";
2359 
2360  if (shader_utilization == SUT_none) {
2361  display_cat.error()
2362  << "The 'shader_utilization' setting did not come from the config\n"
2363  << "file. In other words, it was altered procedurally.\n";
2364 
2365  if (shader_auto_utilization) {
2366  display_cat.error()
2367  << "It is possible that it was set by panda's automatic mechanisms,\n"
2368  << "which are currently enabled, because 'shader_auto_utilization' is\n"
2369  << "true. Panda's automatic mechanisms assume that if one\n"
2370  << "window supports shaders, then they all will.\n"
2371  << "This assumption works for most games, but not all.\n"
2372  << "In particular, it can fail if the game creates multiple windows\n"
2373  << "on multiple displays with different video cards.\n";
2374  }
2375  }
2376  }
2377 }
2378 
2379 ////////////////////////////////////////////////////////////////////
2380 // Function: GraphicsEngine::terminate_threads
2381 // Access: Private
2382 // Description: Signals our child threads to terminate and waits for
2383 // them to clean up.
2384 ////////////////////////////////////////////////////////////////////
2385 void GraphicsEngine::
2386 terminate_threads(Thread *current_thread) {
2387  ReMutexHolder holder(_lock, current_thread);
2388 
2389  // We spend almost our entire time in this method just waiting for
2390  // threads. Time it appropriately.
2391  PStatTimer timer(_wait_pcollector, current_thread);
2392 
2393  // First, wait for all the threads to finish their current frame.
2394  // Grabbing the mutex should achieve that.
2395  Threads::const_iterator ti;
2396  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2397  RenderThread *thread = (*ti).second;
2398  thread->_cv_mutex.acquire();
2399  }
2400 
2401  // Now tell them to close their windows and terminate.
2402  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2403  RenderThread *thread = (*ti).second;
2404  thread->_thread_state = TS_terminate;
2405  thread->_cv_start.notify();
2406  thread->_cv_mutex.release();
2407  }
2408 
2409  // Finally, wait for them all to finish cleaning up.
2410  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2411  RenderThread *thread = (*ti).second;
2412  thread->join();
2413  }
2414 
2415  _threads.clear();
2416 }
2417 
2418 
2419 #ifdef DO_PSTATS
2420 ////////////////////////////////////////////////////////////////////
2421 // Function: GraphicsEngine::pstats_count_cycler_type
2422 // Access: Private, Static
2423 // Description: A callback function for
2424 // Pipeline::iterate_all_cycler_types() to report the
2425 // cycler types to PStats.
2426 ////////////////////////////////////////////////////////////////////
2427 void GraphicsEngine::
2428 pstats_count_cycler_type(TypeHandle type, int count, void *data) {
2429  GraphicsEngine *self = (GraphicsEngine *)data;
2430  CyclerTypeCounters::iterator ci = self->_all_cycler_types.find(type);
2431  if (ci == self->_all_cycler_types.end()) {
2432  PStatCollector collector(_cyclers_pcollector, type.get_name());
2433  ci = self->_all_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first;
2434  }
2435  (*ci).second.set_level(count);
2436 }
2437 #endif // DO_PSTATS
2438 
2439 #ifdef DO_PSTATS
2440 ////////////////////////////////////////////////////////////////////
2441 // Function: GraphicsEngine::pstats_count_dirty_cycler_type
2442 // Access: Private, Static
2443 // Description: A callback function for
2444 // Pipeline::iterate_dirty_cycler_types() to report the
2445 // cycler types to PStats.
2446 ////////////////////////////////////////////////////////////////////
2447 void GraphicsEngine::
2448 pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data) {
2449  GraphicsEngine *self = (GraphicsEngine *)data;
2450  CyclerTypeCounters::iterator ci = self->_dirty_cycler_types.find(type);
2451  if (ci == self->_dirty_cycler_types.end()) {
2452  PStatCollector collector(_dirty_cyclers_pcollector, type.get_name());
2453  ci = self->_dirty_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first;
2454  }
2455  (*ci).second.set_level(count);
2456 }
2457 #endif // DO_PSTATS
2458 
2459 ////////////////////////////////////////////////////////////////////
2460 // Function: GraphicsEngine::get_invert_polygon_state
2461 // Access: Protected, Static
2462 // Description: Returns a RenderState for inverting the sense of
2463 // polygon vertex ordering: if the scene graph specifies
2464 // a clockwise ordering, this changes it to
2465 // counterclockwise, and vice-versa.
2466 ////////////////////////////////////////////////////////////////////
2467 const RenderState *GraphicsEngine::
2468 get_invert_polygon_state() {
2469  // Once someone asks for this pointer, we hold its reference count
2470  // and never free it.
2471  static CPT(RenderState) state = (const RenderState *)NULL;
2472  if (state == (const RenderState *)NULL) {
2473  state = RenderState::make(CullFaceAttrib::make_reverse());
2474  }
2475 
2476  return state;
2477 }
2478 
2479 ////////////////////////////////////////////////////////////////////
2480 // Function: GraphicsEngine::get_window_renderer
2481 // Access: Private
2482 // Description: Returns the WindowRenderer with the given name.
2483 // Creates a new RenderThread if there is no such thread
2484 // already. The pipeline_stage number specifies the
2485 // pipeline stage that will be assigned to the thread
2486 // (unless was previously given a higher stage).
2487 //
2488 // You must already be holding the lock before calling
2489 // this method.
2490 ////////////////////////////////////////////////////////////////////
2491 GraphicsEngine::WindowRenderer *GraphicsEngine::
2492 get_window_renderer(const string &name, int pipeline_stage) {
2493  nassertr(_lock.debug_is_locked(), NULL);
2494 
2495  if (name.empty()) {
2496  return &_app;
2497  }
2498 
2499  Threads::iterator ti = _threads.find(name);
2500  if (ti != _threads.end()) {
2501  return (*ti).second.p();
2502  }
2503 
2504  PT(RenderThread) thread = new RenderThread(name, this);
2505  thread->set_min_pipeline_stage(pipeline_stage);
2506  _pipeline->set_min_stages(pipeline_stage + 1);
2507 
2508  bool started = thread->start(TP_normal, true);
2509  nassertr(started, thread.p());
2510  _threads[name] = thread;
2511 
2512  nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());
2513 
2514  return thread.p();
2515 }
2516 
2517 ////////////////////////////////////////////////////////////////////
2518 // Function: GraphicsEngine::WindowRenderer::Constructor
2519 // Access: Public
2520 // Description:
2521 ////////////////////////////////////////////////////////////////////
2522 GraphicsEngine::WindowRenderer::
2523 WindowRenderer(const string &name) :
2524  _wl_lock(string("GraphicsEngine::WindowRenderer::_wl_lock ") + name)
2525 {
2526 }
2527 
2528 ////////////////////////////////////////////////////////////////////
2529 // Function: GraphicsEngine::WindowRenderer::add_gsg
2530 // Access: Public
2531 // Description: Adds a new GSG to the _gsg list, if it is not already
2532 // there.
2533 ////////////////////////////////////////////////////////////////////
2534 void GraphicsEngine::WindowRenderer::
2535 add_gsg(GraphicsStateGuardian *gsg) {
2536  LightReMutexHolder holder(_wl_lock);
2537  _gsgs.insert(gsg);
2538 }
2539 
2540 ////////////////////////////////////////////////////////////////////
2541 // Function: GraphicsEngine::WindowRenderer::add_window
2542 // Access: Public
2543 // Description: Adds a new window to the indicated list, which should
2544 // be a member of the WindowRenderer.
2545 ////////////////////////////////////////////////////////////////////
2546 void GraphicsEngine::WindowRenderer::
2547 add_window(Windows &wlist, GraphicsOutput *window) {
2548  LightReMutexHolder holder(_wl_lock);
2549  wlist.insert(window);
2550 }
2551 
2552 ////////////////////////////////////////////////////////////////////
2553 // Function: GraphicsEngine::WindowRenderer::remove_window
2554 // Access: Public
2555 // Description: Immediately removes the indicated window from all
2556 // lists. If the window is currently open and is
2557 // already on the _window list, moves it to the _pending_close
2558 // list for later closure.
2559 ////////////////////////////////////////////////////////////////////
2560 void GraphicsEngine::WindowRenderer::
2561 remove_window(GraphicsOutput *window) {
2562  nassertv(window != NULL);
2563  LightReMutexHolder holder(_wl_lock);
2564  PT(GraphicsOutput) ptwin = window;
2565 
2566  _cull.erase(ptwin);
2567  _cdraw.erase(ptwin);
2568  _draw.erase(ptwin);
2569 
2570  Windows::iterator wi;
2571 
2572  wi = _window.find(ptwin);
2573  if (wi != _window.end()) {
2574  // The window is on our _window list, meaning its open/close
2575  // operations (among other window ops) are serviced by this
2576  // thread.
2577 
2578  // Make sure the window isn't about to request itself open.
2579  ptwin->request_close();
2580 
2581  // If the window is already open, move it to the _pending_close list so
2582  // it can be closed later. We can't close it immediately, because
2583  // we might not have been called from the subthread.
2584  if (ptwin->is_valid()) {
2585  _pending_close.push_back(ptwin);
2586  }
2587 
2588  _window.erase(wi);
2589  }
2590 }
2591 
2592 ////////////////////////////////////////////////////////////////////
2593 // Function: GraphicsEngine::WindowRenderer::resort_windows
2594 // Access: Public
2595 // Description: Resorts all the lists of windows, assuming they may
2596 // have become unsorted.
2597 ////////////////////////////////////////////////////////////////////
2598 void GraphicsEngine::WindowRenderer::
2599 resort_windows() {
2600  LightReMutexHolder holder(_wl_lock);
2601 
2602  _cull.sort();
2603  _cdraw.sort();
2604  _draw.sort();
2605  _window.sort();
2606 
2607  if (display_cat.is_debug()) {
2608  display_cat.debug()
2609  << "Windows resorted:";
2610  Windows::const_iterator wi;
2611  for (wi = _window.begin(); wi != _window.end(); ++wi) {
2612  GraphicsOutput *win = (*wi);
2613  display_cat.debug(false)
2614  << " " << win->get_name() << "(" << win->get_sort() << ")";
2615  }
2616  display_cat.debug(false)
2617  << "\n";
2618 
2619  for (wi = _draw.begin(); wi != _draw.end(); ++wi) {
2620  GraphicsOutput *win = (*wi);
2621  display_cat.debug(false)
2622  << " " << win->get_name() << "(" << win->get_sort() << ")";
2623  }
2624  display_cat.debug(false)
2625  << "\n";
2626  }
2627 }
2628 
2629 ////////////////////////////////////////////////////////////////////
2630 // Function: GraphicsEngine::WindowRenderer::do_frame
2631 // Access: Public
2632 // Description: Executes one stage of the pipeline for the current
2633 // thread: calls cull on all windows that are on the
2634 // cull list for this thread, draw on all the windows on
2635 // the draw list, etc.
2636 ////////////////////////////////////////////////////////////////////
2637 void GraphicsEngine::WindowRenderer::
2638 do_frame(GraphicsEngine *engine, Thread *current_thread) {
2639  PStatTimer timer(engine->_do_frame_pcollector, current_thread);
2640  LightReMutexHolder holder(_wl_lock);
2641 
2642  engine->cull_to_bins(_cull, current_thread);
2643  engine->cull_and_draw_together(_cdraw, current_thread);
2644  engine->draw_bins(_draw, current_thread);
2645  engine->process_events(_window, current_thread);
2646 
2647  // If any GSG's on the list have no more outstanding pointers, clean
2648  // them up. (We are in the draw thread for all of these GSG's.)
2649  if (any_done_gsgs()) {
2650  GSGs new_gsgs;
2651  GSGs::iterator gi;
2652  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2653  GraphicsStateGuardian *gsg = (*gi);
2654  if (gsg->get_ref_count() == 1) {
2655  // This one has no outstanding pointers; clean it up.
2656  GraphicsPipe *pipe = gsg->get_pipe();
2657  engine->close_gsg(pipe, gsg);
2658  } else {
2659  // This one is ok; preserve it.
2660  new_gsgs.insert(gsg);
2661  }
2662  }
2663 
2664  _gsgs.swap(new_gsgs);
2665  }
2666 }
2667 
2668 ////////////////////////////////////////////////////////////////////
2669 // Function: GraphicsEngine::WindowRenderer::do_windows
2670 // Access: Public
2671 // Description: Attempts to fully open or close any windows or
2672 // buffers associated with this thread, but does not
2673 // otherwise perform any rendering. (Normally, this
2674 // step is handled during do_frame(); call this method
2675 // only if you want these things to open immediately.)
2676 ////////////////////////////////////////////////////////////////////
2677 void GraphicsEngine::WindowRenderer::
2678 do_windows(GraphicsEngine *engine, Thread *current_thread) {
2679  LightReMutexHolder holder(_wl_lock);
2680 
2681  engine->process_events(_window, current_thread);
2682 
2683  engine->make_contexts(_cdraw, current_thread);
2684  engine->make_contexts(_draw, current_thread);
2685 }
2686 
2687 ////////////////////////////////////////////////////////////////////
2688 // Function: GraphicsEngine::WindowRenderer::do_flip
2689 // Access: Public
2690 // Description: Flips the windows as appropriate for the current
2691 // thread.
2692 ////////////////////////////////////////////////////////////////////
2693 void GraphicsEngine::WindowRenderer::
2694 do_flip(GraphicsEngine *engine, Thread *current_thread) {
2695  LightReMutexHolder holder(_wl_lock);
2696  engine->flip_windows(_cdraw, current_thread);
2697  engine->flip_windows(_draw, current_thread);
2698 }
2699 
2700 ////////////////////////////////////////////////////////////////////
2701 // Function: GraphicsEngine::WindowRenderer::do_ready_flip
2702 // Access: Public
2703 // Description: Prepares windows for flipping by waiting until all draw
2704 // calls are finished
2705 ////////////////////////////////////////////////////////////////////
2706 void GraphicsEngine::WindowRenderer::
2707 do_ready_flip(GraphicsEngine *engine, Thread *current_thread) {
2708  LightReMutexHolder holder(_wl_lock);
2709  engine->ready_flip_windows(_cdraw, current_thread);
2710  engine->ready_flip_windows(_draw, current_thread);
2711 }
2712 
2713 
2714 ////////////////////////////////////////////////////////////////////
2715 // Function: GraphicsEngine::WindowRenderer::do_close
2716 // Access: Public
2717 // Description: Closes all the windows on the _window list.
2718 ////////////////////////////////////////////////////////////////////
2719 void GraphicsEngine::WindowRenderer::
2720 do_close(GraphicsEngine *engine, Thread *current_thread) {
2721  LightReMutexHolder holder(_wl_lock);
2722  Windows::iterator wi;
2723  for (wi = _window.begin(); wi != _window.end(); ++wi) {
2724  GraphicsOutput *win = (*wi);
2725  win->set_close_now();
2726  }
2727 
2728  // Also close all of the GSG's.
2729  GSGs new_gsgs;
2730  GSGs::iterator gi;
2731  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2732  GraphicsStateGuardian *gsg = (*gi);
2733  if (gsg->get_ref_count() == 1) {
2734  // This one has no outstanding pointers; clean it up.
2735  GraphicsPipe *pipe = gsg->get_pipe();
2736  engine->close_gsg(pipe, gsg);
2737  } else {
2738  // This one is ok; preserve it.
2739  new_gsgs.insert(gsg);
2740  }
2741  }
2742 
2743  _gsgs.swap(new_gsgs);
2744 }
2745 
2746 ////////////////////////////////////////////////////////////////////
2747 // Function: GraphicsEngine::WindowRenderer::do_pending
2748 // Access: Public
2749 // Description: Actually closes any windows that were recently
2750 // removed from the WindowRenderer.
2751 ////////////////////////////////////////////////////////////////////
2752 void GraphicsEngine::WindowRenderer::
2753 do_pending(GraphicsEngine *engine, Thread *current_thread) {
2754  LightReMutexHolder holder(_wl_lock);
2755 
2756  if (!_pending_close.empty()) {
2757  if (display_cat.is_debug()) {
2758  display_cat.debug()
2759  << "_pending_close.size() = " << _pending_close.size() << "\n";
2760  }
2761 
2762  // Close any windows that were pending closure. Carefully protect
2763  // against recursive entry to this function by swapping the vector
2764  // to a local copy first.
2765  Windows::iterator wi;
2766  Windows pending_close;
2767  _pending_close.swap(pending_close);
2768  for (wi = pending_close.begin(); wi != pending_close.end(); ++wi) {
2769  GraphicsOutput *win = (*wi);
2770  win->set_close_now();
2771  }
2772  }
2773 }
2774 
2775 ////////////////////////////////////////////////////////////////////
2776 // Function: GraphicsEngine::WindowRenderer::any_done_gsgs
2777 // Access: Public
2778 // Description: Returns true if any of the GSG's on this thread's
2779 // draw list are done (they have no outstanding pointers
2780 // other than this one), or false if all of them are
2781 // still good.
2782 ////////////////////////////////////////////////////////////////////
2783 bool GraphicsEngine::WindowRenderer::
2784 any_done_gsgs() const {
2785  GSGs::const_iterator gi;
2786  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2787  if ((*gi)->get_ref_count() == 1) {
2788  return true;
2789  }
2790  }
2791 
2792  return false;
2793 }
2794 
2795 ////////////////////////////////////////////////////////////////////
2796 // Function: GraphicsEngine::RenderThread::Constructor
2797 // Access: Public
2798 // Description:
2799 ////////////////////////////////////////////////////////////////////
2800 GraphicsEngine::RenderThread::
2801 RenderThread(const string &name, GraphicsEngine *engine) :
2802  Thread(name, "Main"),
2803  WindowRenderer(name),
2804  _engine(engine),
2805  _cv_mutex(string("GraphicsEngine::RenderThread ") + name),
2806  _cv_start(_cv_mutex),
2807  _cv_done(_cv_mutex)
2808 {
2809  _thread_state = TS_wait;
2810 }
2811 
2812 ////////////////////////////////////////////////////////////////////
2813 // Function: GraphicsEngine::RenderThread::thread_main
2814 // Access: Public, Virtual
2815 // Description: The main loop for a particular render thread. The
2816 // thread will process whatever cull or draw windows it
2817 // has assigned to it.
2818 ////////////////////////////////////////////////////////////////////
2819 void GraphicsEngine::RenderThread::
2820 thread_main() {
2821  Thread *current_thread = Thread::get_current_thread();
2822 
2823  MutexHolder holder(_cv_mutex);
2824  while (true) {
2825  nassertv(_cv_mutex.debug_is_locked());
2826 
2827  switch (_thread_state) {
2828  case TS_wait:
2829  break;
2830 
2831  case TS_do_frame:
2832  do_pending(_engine, current_thread);
2833  do_frame(_engine, current_thread);
2834  break;
2835 
2836  case TS_do_flip:
2837  do_flip(_engine, current_thread);
2838  break;
2839 
2840  case TS_do_release:
2841  do_pending(_engine, current_thread);
2842  break;
2843 
2844  case TS_do_windows:
2845  do_windows(_engine, current_thread);
2846  do_pending(_engine, current_thread);
2847  break;
2848 
2849  case TS_terminate:
2850  do_pending(_engine, current_thread);
2851  do_close(_engine, current_thread);
2852  _thread_state = TS_done;
2853  _cv_done.notify();
2854  return;
2855 
2856  case TS_done:
2857  // Shouldn't be possible to get here.
2858  nassertv(false);
2859  return;
2860  }
2861 
2862  _thread_state = TS_wait;
2863  _cv_done.notify();
2864 
2865  {
2866  PStatTimer timer(_wait_pcollector, current_thread);
2867  _cv_start.wait();
2868  }
2869  }
2870 }
static void flush_global_index()
If there is a global BamCache object, calls flush_index() on it.
Definition: bamCache.I:280
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
const NodePath & get_scene() const
Returns the scene that will be rendered by the camera.
Definition: camera.I:64
void tick(Thread *current_thread=Thread::get_current_thread())
Instructs the clock that a new frame has just begun.
int cleanup_aux_scene_data(Thread *current_thread=Thread::get_current_thread())
Walks through the list of currently-assigned AuxSceneData objects and releases any that are past thei...
Definition: camera.cxx:248
virtual void request_close()
This is called by the GraphicsEngine to request that the window (or whatever) close itself or...
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
static void set_textures_power_2(AutoTextureScale scale)
Set this flag to ATS_none, ATS_up, ATS_down, or ATS_pad to control the scaling of textures in general...
Definition: texture.I:2218
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
A temporary file to hold the vertex data that has been evicted from memory and written to disk...
static SimpleLru * get_small_lru()
Returns a pointer to the global LRU object that manages the GeomVertexArrayData&#39;s that are deemed too...
static void flush_level()
Flushes the PStatCollectors used during traversal.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
static int get_num_states()
Returns the total number of unique RenderState objects allocated in the world.
size_t get_total_size() const
Returns the total size of all objects currently active on the LRU.
Definition: simpleLru.I:23
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
void set_loader(Loader *loader)
Sets the Loader object that will be used by this GSG to load textures when necessary, if get_incomplete_render() is true.
static void set_shader_utilization(ShaderUtilization utl)
Set this flag to SUT_none, SUT_basic, or SUT_advanced to limit panda&#39;s automatic shader generation fa...
Definition: shader.I:112
static void flush_level()
Flushes the PStatCollectors used during traversal.
bool verify_list() const
Maps to verify_list_unique().
virtual void clear_before_callback()
Resets any non-standard graphics state that might give a callback apoplexy.
static bool have_shader_utilization()
If true, then get_shader_utilization has been set using set_shader_utilization.
Definition: shader.I:143
bool empty() const
Returns true if the ordered vector is empty, false otherwise.
void flip_frame()
Waits for all the threads that started drawing their last frame to finish drawing, and then flips all the windows.
virtual void clear(DrawableRegion *clearable)
Clears the framebuffer within the current DisplayRegion, according to the flags indicated by the give...
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
static VertexDataSaveFile * get_save_file()
Returns the global VertexDataSaveFile that will be used to save vertex data buffers to disk when nece...
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
bool get_incomplete_render() const
Returns the incomplete_render flag.
virtual bool is_stereo() const
Returns true if this is a StereoDisplayRegion, false otherwise.
NodePath get_parent(Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath to the parent of the referenced node: that is, this NodePath, shortened by one node.
Definition: nodePath.I:460
void change_scenes(DisplayRegionPipelineReader *new_dr)
Called by the GraphicsEngine when the window is about to change to another DisplayRegion.
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
void set_lens(Lens *lens)
Sets up the LensNode using this particular Lens pointer.
Definition: lensNode.I:47
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
static void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, GraphicsStateGuardian *gsg, Thread *current_thread)
Fires off a cull traversal using the indicated camera.
static void consider_flush_global_index()
If there is a global BamCache object, calls consider_flush_index() on it.
Definition: bamCache.I:267
The abstract base class for all things that can collide with other things in the world, and all the things they can collide with (except geometry).
void release_all()
Releases all prepared objects.
NodePath get_top(Thread *current_thread=Thread::get_current_thread()) const
Returns a singleton NodePath that represents the top of the path, or empty NodePath if this path is e...
Definition: nodePath.cxx:317
size_t get_used_file_size() const
Returns the amount of space within the save file that is currently in use.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:925
static bool is_true_threads()
Returns true if a real threading library is available that supports actual OS-implemented threads...
Definition: thread.I:201
GraphicsOutput * get_window(int n) const
Returns the nth window or buffers managed by the engine, in sorted order.
A cuboid collision volume or object.
Definition: collisionBox.h:30
virtual void dispatch_compute(int size_x, int size_y, int size_z)
Dispatches a currently bound compute shader using the given work group counts.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
void dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg)
Asks the indicated GraphicsStateGuardian to dispatch the compute shader in the given ShaderAttrib usi...
string get_name(TypedObject *object=(TypedObject *) NULL) const
Returns the name of the type.
Definition: typeHandle.I:132
int get_num_active_display_regions() const
Returns the number of active DisplayRegions that have been created within the window.
bool is_active() const
Returns the active flag associated with the GraphicsStateGuardian.
CallbackObject * get_cull_callback() const
Returns the CallbackObject set by set_cull_callback().
static void prepare_for_exit()
Should be called by the main thread just before exiting the program, this blocks until any remaining ...
Definition: thread.I:341
void open_windows()
Fully opens (or closes) any windows that have recently been requested open or closed, without rendering any frames.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
GraphicsPipe * get_pipe() const
Returns the graphics pipe on which this GSG was created.
bool is_valid() const
Returns true if the GSG has been correctly initialized within a graphics context, false if there has ...
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
static ShaderUtilization get_shader_utilization()
This flag returns SUT_none, SUT_basic, or SUT_advanced and controls the automatic generation of shade...
Definition: shader.I:126
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
int get_cull_stage() const
Returns the pipeline stage from which the cull thread should access data.
void finish_cull(SceneSetup *scene_setup, Thread *current_thread)
Called after all the geoms have been added, this indicates that the cull process is finished for this...
Definition: cullResult.cxx:274
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:29
A spherical collision volume or object.
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
static void flush_level()
Flushes the PStatCollectors used during traversal.
bool get_timer_queries_active() const
Returns true if timer queries are currently enabled on this GSG.
static SimpleLru * get_global_lru(RamClass rclass)
Returns a pointer to the global LRU object that manages the VertexDataPage&#39;s with the indicated RamCl...
DisplayRegion * get_display_region() const
Returns the display region for the scene.
Definition: sceneSetup.I:50
bool remove_window(GraphicsOutput *window)
Removes the indicated window or offscreen buffer from the set of windows that will be processed when ...
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
void reserve(size_type_0 n)
Informs the vector of a planned change in size; ensures that the capacity of the vector is greater th...
A table of objects that are saved within the graphics context for reference by handle later...
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame...
static void lru_epoch()
Marks that an epoch has passed in each LRU.
This specialization on CallbackData is passed when the callback is initiated from the cull traversal...
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition: sceneSetup.I:104
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:248
GraphicsEngine * get_engine() const
Returns the graphics engine that created this output.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
void ready_flip()
Waits for all the threads that started drawing their last frame to finish drawing.
void texture_uploaded(Texture *tex)
This method is called by the GraphicsStateGuardian after a texture has been successfully uploaded to ...
void set_cull_result(CullResult *cull_result, SceneSetup *scene_setup, Thread *current_thread)
Stores the result of performing a cull operation on this DisplayRegion.
GraphicsPipe * get_pipe() const
Returns the GraphicsPipe that this window is associated with.
void swap(ordered_vector< Key, Compare, Vector > &other)
Exchanges the contents of this vector and the other vector, in constant time (e.g., with a pointer swap).
This is a special type of PStatTimer that also uses a timer query on the GSG to measure how long a ta...
Definition: pStatGPUTimer.h:41
PStatCollector & get_cull_region_pcollector()
Returns a PStatCollector for timing the cull operation for just this DisplayRegion.
bool is_valid() const
Returns true if the output is fully created and ready for rendering, false otherwise.
void reset_all_windows(bool swapchain)
Resets the framebuffer of the current window.
PStatCollector & get_draw_window_pcollector()
Returns a PStatCollector for timing the draw operation for just this GraphicsOutput.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
bool get_lens_active(int index) const
Returns the active flag for the nth lens.
Definition: lensNode.I:75
~GraphicsEngine()
Gracefully cleans up the graphics engine and its related threads and windows.
static bool is_threading_supported()
Returns true if threading support has been compiled in and enabled, or false if no threading is avail...
Definition: thread.I:185
A lightweight class that represents a single element that may be timed and/or counted via stats...
virtual void set_state_and_transform(const RenderState *state, const TransformState *transform)
Simultaneously resets the render state and the transform state.
static AutoTextureScale get_textures_power_2()
This flag returns ATS_none, ATS_up, or ATS_down and controls the scaling of textures in general...
Definition: texture.I:2232
virtual void do_callback(CallbackData *cbdata)
This method called when the callback is triggered; it replaces* the original function.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
virtual void set_close_now()
This is called by the GraphicsEngine to insist that the output be closed immediately.
void cycle()
Flows all the pipeline data down to the next stage.
Definition: pipeline.cxx:86
A container for the various kinds of properties we might ask to have on a graphics window before we o...
bool get_cull_sorting() const
Returns true if the model involves a separate cull pass, or false if culling happens implicitly...
virtual void request_open()
This is called by the GraphicsEngine to request that the window (or whatever) open itself or...
This special window object doesn&#39;t represent a window in its own right, but instead hooks into some t...
bool is_active() const
Returns the current setting of the active flag on the camera.
Definition: camera.I:34
void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
void stop_threads()
Stops any threads that are currently running.
bool is_empty() const
Returns true if there are no windows or buffers managed by the engine, false if there is at least one...
CullResult * get_cull_result(Thread *current_thread) const
Returns the CullResult value that was stored on this DisplayRegion, presumably by the last successful...
This defines the abstract interface for an object that receives Geoms identified by the CullTraverser...
Definition: cullHandler.h:31
int get_sort() const
Returns the sorting order of this particular GraphicsOutput.
bool get_supports_basic_shaders() const
Returns true if this particular GSG supports arbfp1+arbvp1 or above.
This is a special GraphicsOutput type that acts a lot like a GraphicsBuffer, effectively allowing ren...
int get_draw_stage() const
Returns the pipeline stage from which the draw thread should access data.
virtual void clear_state_and_transform()
Forgets the current graphics state and current transform, so that the next call to set_state_and_tran...
const string & get_cull_name() const
Returns the name of the thread that will handle culling in this model.
GraphicsEngine(Pipeline *pipeline=NULL)
Creates a new GraphicsEngine object.
void sync_frame()
Waits for all the threads that started drawing their last frame to finish drawing.
virtual PreparedGraphicsObjects * get_prepared_objects()
Returns the set of texture and geom objects that have been prepared with this GSG (and possibly other...
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:66
static int get_num_unused_states()
Returns the total number of RenderState objects that have been allocated but have no references outsi...
bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg)
Asks the indicated GraphicsStateGuardian to retrieve the texture memory image of the indicated textur...
static SimpleLru * get_pending_lru()
Returns a pointer to the global LRU object that manages the VertexDataPage&#39;s that are pending process...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
Definition: clockObject.I:113
PStatCollector & get_draw_region_pcollector()
Returns a PStatCollector for timing the draw operation for just this DisplayRegion.
void sort()
Maps to sort_unique().
static GraphicsStateGuardianBase * get_gsg(int n)
Returns the nth GSG in the universe.
The ShaderGenerator is a device that effectively replaces the classic fixed function pipeline with a ...
A container for geometry primitives.
Definition: geom.h:58
const string & get_draw_name() const
Returns the name of the thread that will handle sending the actual graphics primitives to the graphic...
LVecBase3 get_scale() const
Retrieves the scale component of the transform.
Definition: nodePath.cxx:1331
This CullHandler sends all of the geoms it receives into a CullResult object, for binning (and later ...
UpdateSeq get_image_modified() const
Returns a sequence number which is guaranteed to change at least every time the texture image data (i...
Definition: texture.I:2151
GraphicsStateGuardian * get_gsg() const
Returns the GSG that is associated with this window.
bool get_supports_tex_non_pow2() const
Returns true if this GSG can handle non power of two sized textures.
A special CollisionPolygon created just for the purpose of detecting collision against geometry...
Definition: collisionGeom.h:33
bool add_window(GraphicsOutput *window, int sort)
This can be used to add a newly-created GraphicsOutput object (and its GSG) to the engine&#39;s list of w...
This specialization on CallbackData is passed when the callback is initiated from the draw traversal...
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:27
static Pipeline * get_render_pipeline()
Returns a pointer to the global render pipeline.
Definition: pipeline.I:22
static void set_scene_root_func(SceneRootFunc *func)
This is used by the GraphicsEngine to hook in a pointer to the scene_root_func(), the function to det...
Definition: pandaNode.cxx:4142
CallbackObject * get_draw_callback() const
Returns the CallbackObject set by set_draw_callback().
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed.
Definition: sceneSetup.I:203
This is a base class for the various different classes that represent the result of a frame of render...
Similar to MutexHolder, but for a light reentrant mutex.
GraphicsOutput * make_output(GraphicsPipe *pipe, const string &name, int sort, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg=NULL, GraphicsOutput *host=NULL)
Creates a new window (or buffer) and returns it.
This represents the user&#39;s specification of how a particular frame is handled by the various threads...
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:41
virtual void reset_window(bool swapchain)
Resets the window framebuffer from its derived children.
void remove_all_windows()
Removes and closes all windows from the engine.
This is a generic object that can be assigned to a callback at various points in the rendering proces...
bool has_size() const
Returns true if the window size has been specified, false otherwise.
virtual void ready_flip()
This function will be called within the draw thread after end_frame() has been called on all windows...
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:2756
void render_frame()
Renders the next frame in all the registered windows, and flips all of the frame buffers.
This stores the result of a BinCullHandler traversal: an ordered collection of CullBins, each of which holds a number of Geoms and RenderStates to be rendered in some defined order.
Definition: cullResult.h:47
static void stop_threads()
Call this to stop the paging threads, if they were started.
static SimpleLru * get_independent_lru()
Returns a pointer to the global LRU object that manages the GeomVertexArrayData&#39;s that have not (yet)...
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
bool needs_reset() const
Returns true if the gsg is marked as needing a reset.
This special kind of CullHandler immediately draws its contents as soon as it receives them...
NodePath get_camera() const
Returns the camera associated with this DisplayRegion, or an empty NodePath if no camera is associate...
bool get_delete_flag() const
Returns the current setting of the delete flag.
bool is_single_threaded() const
Returns true if the threading model is a single-threaded model, or false if it involves threads...
GraphicsThreadingModel get_threading_model() const
Returns the threading model that will be applied to future objects.
A thread; that is, a lightweight process.
Definition: thread.h:51
virtual bool flip_ready() const
Returns true if a frame has been rendered and needs to be flipped, false otherwise.
A node in the scene graph that can hold any number of CollisionSolids.
Definition: collisionNode.h:33
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows...
size_t get_total_file_size() const
Returns the amount of space consumed by the save file, including unused portions. ...
Encapsulates all the communication with a particular instance of a given rendering backend...
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: renderState.I:622
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a &quot;scene&quot; (usual...
static bool is_connected()
Returns true if the client believes it is connected to a working PStatServer, false otherwise...
Definition: pStatClient.h:269
CullTraverser * get_cull_traverser()
Returns the CullTraverser that will be used to draw the contents of this DisplayRegion.
size_type_0 size() const
Returns the number of elements in the ordered vector.
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:61
bool debug_is_locked() const
Returns true if the current thread has locked the ReMutex, false otherwise.
void set_threading_model(const GraphicsThreadingModel &threading_model)
Specifies how future objects created via make_gsg(), make_buffer(), and make_window() will be threade...
const GraphicsThreadingModel & get_threading_model() const
Returns the threading model that was used to create this GSG.
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
void draw(Thread *current_thread)
Asks all the bins to draw themselves in the correct order.
Definition: cullResult.cxx:299
This class is the main interface to controlling the render process.
PStatCollector & get_cull_window_pcollector()
Returns a PStatCollector for timing the cull operation for just this GraphicsOutput.
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
bool check_errors(Thread *current_thread)
Returns true if a clock error was detected since the last time check_errors() was called...
Definition: clockObject.I:256
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
static bool has_textures_power_2()
If true, then get_textures_power_2 has been set using set_textures_power_2.
Definition: texture.I:2249
int get_num_windows() const
Returns the number of windows (or buffers) managed by the engine.
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition: sceneSetup.h:35
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
const string & get_name() const
Returns the name that was passed to the GraphicsOutput constructor.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:37
bool set_scene(SceneSetup *scene_setup)
Sets the SceneSetup object that indicates the initial camera position, etc.
This implements a solid roughly in cylindrical shape.
Definition: collisionTube.h:30
int get_ref_count() const
Returns the current reference count.
static AsyncTaskManager * get_global_ptr()
Returns a pointer to the global AsyncTaskManager.
NodePath get_camera(Thread *current_thread=Thread::get_current_thread()) const
Returns the camera associated with this DisplayRegion, or an empty NodePath if no camera is associate...
virtual bool is_active() const
Returns true if the window is ready to be rendered into, false otherwise.
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame...
virtual bool extract_texture_data(Texture *tex)
This method should only be called by the GraphicsEngine.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a &quot;scene&quot; ...
An inverted sphere: this is a sphere whose collision surface is the inside surface of the sphere...
void release_all()
Releases all prepared objects of all kinds at once.
void set_cull_handler(CullHandler *cull_handler)
Specifies the object that will receive the culled Geoms.
Lens * get_lens(int index=0) const
Returns a pointer to the particular Lens associated with this LensNode, or NULL if there is not yet a...
Definition: lensNode.I:60