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