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