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_window() 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_window() 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 }
Pipeline::get_render_pipeline
static Pipeline * get_render_pipeline()
Returns a pointer to the global render pipeline.
Definition: pipeline.I:18
GraphicsOutput::get_cull_window_pcollector
PStatCollector & get_cull_window_pcollector()
Returns a PStatCollector for timing the cull operation for just this GraphicsOutput.
Definition: graphicsOutput.I:693
Camera::get_scene
get_scene
Returns the scene that will be rendered by the camera.
Definition: camera.h:54
MutexHolder
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
GraphicsEngine::add_window
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...
Definition: graphicsEngine.cxx:489
ordered_vector::size
size_type_0 size() const
Returns the number of elements in the ordered vector.
Definition: ordered_vector.I:221
bamCache.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_draw_window_pcollector
PStatCollector & get_draw_window_pcollector()
Returns a PStatCollector for timing the draw operation for just this GraphicsOutput.
Definition: graphicsOutput.I:702
PreparedGraphicsObjects
A table of objects that are saved within the graphics context for reference by handle later.
Definition: preparedGraphicsObjects.h:58
GeomVertexArrayData::get_independent_lru
static SimpleLru * get_independent_lru()
Returns a pointer to the global LRU object that manages the GeomVertexArrayData's that have not (yet)...
Definition: geomVertexArrayData.I:183
GraphicsEngine::remove_window
bool remove_window(GraphicsOutput *window)
Removes the indicated window or offscreen buffer from the set of windows that will be processed when ...
Definition: graphicsEngine.cxx:520
PreparedGraphicsObjects::release_all
void release_all()
Releases all prepared objects of all kinds at once.
Definition: preparedGraphicsObjects.I:37
FrameBufferProperties
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
Definition: frameBufferProperties.h:26
ParasiteBuffer
This is a special GraphicsOutput type that acts a lot like a GraphicsBuffer, effectively allowing ren...
Definition: parasiteBuffer.h:43
cullTraverser.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle::get_name
get_name
Returns the name of the type.
Definition: typeHandle.h:136
pipeline.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexArrayData::lru_epoch
static void lru_epoch()
Marks that an epoch has passed in each LRU.
Definition: geomVertexArrayData.cxx:310
throw_event.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup::set_camera_path
void set_camera_path(const NodePath &camera_path)
Specifies the NodePath to the camera.
Definition: sceneSetup.I:91
vertexDataBook.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup::get_scene_root
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition: sceneSetup.I:83
AsyncTaskManager::get_global_ptr
static AsyncTaskManager * get_global_ptr()
Returns a pointer to the global AsyncTaskManager.
Definition: asyncTaskManager.I:50
GraphicsStateGuardian::set_state_and_transform
virtual void set_state_and_transform(const RenderState *state, const TransformState *transform)
Simultaneously resets the render state and the transform state.
Definition: graphicsStateGuardian.cxx:2739
GraphicsEngine::is_empty
bool is_empty() const
Returns true if there are no windows or buffers managed by the engine, false if there is at least one...
Definition: graphicsEngine.cxx:674
Shader
Definition: shader.h:49
DisplayRegionPipelineReader::get_camera
NodePath get_camera() const
Returns the camera associated with this DisplayRegion, or an empty NodePath if no camera is associate...
Definition: displayRegion.I:710
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
GraphicsStateGuardian::get_pipe
get_pipe
Returns the graphics pipe on which this GSG was created.
Definition: graphicsStateGuardian.h:117
Pipeline::cycle
void cycle()
Flows all the pipeline data down to the next stage.
Definition: pipeline.cxx:87
ov_set::sort
void sort()
Maps to sort_unique().
Definition: ordered_vector.I:731
ClockObject
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:58
ordered_vector::reserve
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...
Definition: ordered_vector.I:572
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cullResult.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_gobj.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BinCullHandler
This CullHandler sends all of the geoms it receives into a CullResult object, for binning (and later ...
Definition: binCullHandler.h:27
ReMutexHolder
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
GraphicsOutput::is_valid
bool is_valid() const
Returns true if the output is fully created and ready for rendering, false otherwise.
Definition: graphicsOutput.I:280
CullTraverser::set_view_frustum
void set_view_frustum(GeometricBoundingVolume *view_frustum)
Specifies the bounding volume that corresponds to the view frustum.
Definition: cullTraverser.I:121
GraphicsThreadingModel::get_draw_name
const std::string & get_draw_name() const
Returns the name of the thread that will handle sending the actual graphics primitives to the graphic...
Definition: graphicsThreadingModel.I:73
Camera
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
ReMutexDirect::debug_is_locked
bool debug_is_locked() const
Returns true if the current thread has locked the ReMutex, false otherwise.
Definition: reMutexDirect.I:177
DisplayRegion::get_debug_name
const std::string & get_debug_name() const
Returns a unique name used for debugging.
Definition: displayRegion.I:530
SceneSetup::set_camera_transform
void set_camera_transform(const TransformState *camera_transform)
Specifies the position of the camera relative to the starting node.
Definition: sceneSetup.I:206
Texture::get_textures_power_2
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
GraphicsOutput::get_engine
get_engine
Returns the graphics engine that created this output.
Definition: graphicsOutput.h:119
BamCache::consider_flush_global_index
static void consider_flush_global_index()
If there is a global BamCache object, calls consider_flush_index() on it.
Definition: bamCache.I:234
GraphicsOutput::get_name
get_name
Returns the name that was passed to the GraphicsOutput constructor.
Definition: graphicsOutput.h:120
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
Thread::is_true_threads
is_true_threads
Returns true if a real threading library is available that supports actual OS-implemented threads,...
Definition: thread.h:113
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
SceneSetup::get_cull_center
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed.
Definition: sceneSetup.I:161
NodePath::get_scale
LVecBase3 get_scale() const
Retrieves the scale component of the transform.
Definition: nodePath.cxx:1161
GraphicsStateGuardian::clear
virtual void clear(DrawableRegion *clearable)
Clears the framebuffer within the current DisplayRegion, according to the flags indicated by the give...
Definition: graphicsStateGuardian.cxx:2752
cullableObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsEngine::make_output
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.
Definition: graphicsEngine.cxx:257
GraphicsOutput::flip_ready
virtual bool flip_ready() const
Returns true if a frame has been rendered and needs to be flipped, false otherwise.
Definition: graphicsOutput.cxx:1045
GraphicsOutput::change_scenes
void change_scenes(DisplayRegionPipelineReader *new_dr)
Called by the GraphicsEngine when the window is about to change to another DisplayRegion.
Definition: graphicsOutput.cxx:1182
GraphicsOutput::set_close_now
virtual void set_close_now()
This is called by the GraphicsEngine to insist that the output be closed immediately.
Definition: graphicsOutput.cxx:1083
DisplayRegion::set_cull_result
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.
Definition: displayRegion.I:479
parasiteBuffer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Pipeline
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:38
ordered_vector::empty
bool empty() const
Returns true if the ordered vector is empty, false otherwise.
Definition: ordered_vector.I:240
GraphicsOutput::begin_flip
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
Definition: graphicsOutput.cxx:1275
Thread::consider_yield
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
SceneSetup::get_display_region
DisplayRegion * get_display_region() const
Returns the display region for the scene.
Definition: sceneSetup.I:42
WindowProperties::get_y_size
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:159
ordered_vector::begin
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
Definition: ordered_vector.I:41
RenderState::get_num_states
static int get_num_states()
Returns the total number of unique RenderState objects allocated in the world.
Definition: renderState.cxx:718
DisplayRegionPipelineReader::get_lens_index
int get_lens_index() const
Gets the index into a lens_node lens array.
Definition: displayRegion.I:862
GraphicsOutput::ready_flip
virtual void ready_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
Definition: graphicsOutput.cxx:1288
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsEngine::~GraphicsEngine
~GraphicsEngine()
Gracefully cleans up the graphics engine and its related threads and windows.
Definition: graphicsEngine.cxx:182
GraphicsThreadingModel
This represents the user's specification of how a particular frame is handled by the various threads.
Definition: graphicsThreadingModel.h:23
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DisplayRegion::get_cull_traverser
get_cull_traverser
Returns the CullTraverser that will be used to draw the contents of this DisplayRegion.
Definition: displayRegion.h:127
GraphicsStateGuardian::get_engine
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
Definition: graphicsStateGuardian.cxx:318
GraphicsEngine::extract_texture_data
bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg)
Asks the indicated GraphicsStateGuardian to retrieve the texture memory image of the indicated textur...
Definition: graphicsEngine.cxx:1128
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
CallbackObject
This is a generic object that can be assigned to a callback at various points in the rendering proces...
Definition: callbackObject.h:28
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
graphicsEngine.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DisplayRegion
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
CullTraverser::set_scene
virtual void set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, bool dr_incomplete_render)
Sets the SceneSetup object that indicates the initial camera position, etc.
Definition: cullTraverser.cxx:85
ShaderAttrib
Definition: shaderAttrib.h:39
GraphicsStateGuardian::get_timer_queries_active
get_timer_queries_active
Returns true if timer queries are currently enabled on this GSG.
Definition: graphicsStateGuardian.h:222
CallbackObject::do_callback
virtual void do_callback(CallbackData *cbdata)
This method called when the callback is triggered; it *replaces* the original function.
Definition: callbackObject.cxx:33
CullResult::draw
void draw(Thread *current_thread)
Asks all the bins to draw themselves in the correct order.
Definition: cullResult.cxx:287
NodePath::get_transform
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:791
Texture
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
GeomCacheManager::flush_level
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: geomCacheManager.I:61
ov_set::verify_list
bool verify_list() const
Maps to verify_list_unique().
Definition: ordered_vector.I:740
NodePath::get_parent
get_parent
Returns the NodePath to the parent of the referenced node: that is, this NodePath,...
Definition: nodePath.h:242
GraphicsStateGuardian::is_valid
is_valid
Returns true if the GSG has been correctly initialized within a graphics context, false if there has ...
Definition: graphicsStateGuardian.h:98
WindowProperties
A container for the various kinds of properties we might ask to have on a graphics window before we o...
Definition: windowProperties.h:29
SceneSetup::set_scene_root
void set_scene_root(const NodePath &scene_root)
Specifies the root node of the scene.
Definition: sceneSetup.I:75
WindowProperties::get_x_size
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:149
DrawableRegion::is_any_clear_active
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,...
Definition: drawableRegion.cxx:83
GraphicsEngine::ready_flip
void ready_flip()
Waits for all the threads that started drawing their last frame to finish drawing.
Definition: graphicsEngine.cxx:1082
GraphicsEngine::render_frame
void render_frame()
Renders the next frame in all the registered windows, and flips all of the frame buffers.
Definition: graphicsEngine.cxx:704
DisplayRegion::get_draw_region_pcollector
PStatCollector & get_draw_region_pcollector()
Returns a PStatCollector for timing the draw operation for just this DisplayRegion.
Definition: displayRegion.I:522
SceneSetup::set_inverted
void set_inverted(bool inverted)
Changes the current setting of the inverted flag.
Definition: sceneSetup.I:141
FrameBufferProperties::subsumes
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
Definition: frameBufferProperties.cxx:25
VertexDataPage::get_global_lru
static SimpleLru * get_global_lru(RamClass rclass)
Returns a pointer to the global LRU object that manages the VertexDataPage's with the indicated RamCl...
Definition: vertexDataPage.I:83
graphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ordered_vector::end
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
Definition: ordered_vector.I:50
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
DisplayRegion::get_cull_callback
get_cull_callback
Returns the CallbackObject set by set_cull_callback().
Definition: displayRegion.h:141
SceneSetup::set_viewport_size
void set_viewport_size(int width, int height)
Specifies the size of the viewport (display region), in pixels.
Definition: sceneSetup.I:50
GraphicsStateGuardianBase::get_gsg
get_gsg
Returns the nth GSG in the universe.
Definition: graphicsStateGuardianBase.h:247
Lens::get_coordinate_system
get_coordinate_system
Returns the coordinate system that all 3-d computations are performed within for this Lens.
Definition: lens.h:74
GraphicsEngine
This class is the main interface to controlling the render process.
Definition: graphicsEngine.h:53
GraphicsStateGuardian::get_threading_model
const GraphicsThreadingModel & get_threading_model() const
Returns the threading model that was used to create this GSG.
Definition: graphicsStateGuardian.I:206
LensNode::get_lens_active
bool get_lens_active(int index) const
Returns the active flag for the nth lens.
Definition: lensNode.I:60
GraphicsStateGuardian::set_loader
set_loader
Sets the Loader object that will be used by this GSG to load textures when necessary,...
Definition: graphicsStateGuardian.h:108
Texture::set_textures_power_2
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
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
GraphicsOutput::get_gsg
get_gsg
Returns the GSG that is associated with this window.
Definition: graphicsOutput.h:117
renderState.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition: sceneSetup.h:32
ordered_vector::swap
void swap(ordered_vector< Key, Compare, Vector > &other)
Exchanges the contents of this vector and the other vector, in constant time (e.g....
Definition: ordered_vector.I:561
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
RenderState::get_num_unused_states
static int get_num_unused_states()
Returns the total number of RenderState objects that have been allocated but have no references outsi...
Definition: renderState.cxx:740
Camera::is_active
is_active
Returns the current setting of the active flag on the camera.
Definition: camera.h:50
GraphicsEngine::get_num_windows
get_num_windows
Returns the number of windows (or buffers) managed by the engine.
Definition: graphicsEngine.h:103
GraphicsOutput
This is a base class for the various different classes that represent the result of a frame of render...
Definition: graphicsOutput.h:63
GraphicsOutput::clear
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
Definition: graphicsOutput.cxx:1140
DisplayRegion::get_cull_region_pcollector
PStatCollector & get_cull_region_pcollector()
Returns a PStatCollector for timing the cull operation for just this DisplayRegion.
Definition: displayRegion.I:513
GraphicsStateGuardian::get_supports_tex_non_pow2
get_supports_tex_non_pow2
Returns true if this GSG can handle non power of two sized textures.
Definition: graphicsStateGuardian.h:195
Texture::get_image_modified
get_image_modified
Returns a sequence number which is guaranteed to change at least every time the texture image data (i...
Definition: texture.h:517
DisplayRegionPipelineReader::get_pixel_width
int get_pixel_width(int i=0) const
Returns the width of the DisplayRegion in pixels.
Definition: displayRegion.I:854
VertexDataPage::get_save_file
get_save_file
Returns the global VertexDataSaveFile that will be used to save vertex data buffers to disk when nece...
Definition: vertexDataPage.h:67
displayRegionCullCallbackData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
transformState.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DisplayRegion::get_incomplete_render
get_incomplete_render
Returns the incomplete_render flag.
Definition: displayRegion.h:114
GraphicsStateGuardian::begin_scene
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a "scene" ...
Definition: graphicsStateGuardian.cxx:2289
GraphicsThreadingModel::get_draw_stage
int get_draw_stage() const
Returns the pipeline stage from which the draw thread should access data.
Definition: graphicsThreadingModel.I:95
GraphicsStateGuardian::set_scene
set_scene
Sets the SceneSetup object that indicates the initial camera position, etc.
Definition: graphicsStateGuardian.h:290
ordered_vector::push_back
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
Definition: ordered_vector.I:614
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
Camera::cleanup_aux_scene_data
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
displayRegionDrawCallbackData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatClient::disconnect
static void disconnect()
Closes the connection previously established.
Definition: pStatClient.h:290
GeometricBoundingVolume
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
Definition: geometricBoundingVolume.h:29
SceneSetup::set_cs_world_transform
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
CycleDataReader
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
Definition: cycleDataReader.h:35
SceneSetup::set_display_region
void set_display_region(DisplayRegion *display_region)
Specifies the display region for the scene.
Definition: sceneSetup.I:34
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
GraphicsStateGuardian::needs_reset
bool needs_reset() const
Returns true if the gsg is marked as needing a reset.
Definition: graphicsStateGuardian.I:106
GraphicsEngine::flip_frame
void flip_frame()
Waits for all the threads that started drawing their last frame to finish drawing,...
Definition: graphicsEngine.cxx:1097
WindowProperties::has_size
has_size
Returns true if the window size has been specified, false otherwise.
Definition: windowProperties.h:85
DrawCullHandler
This special kind of CullHandler immediately draws its contents as soon as it receives them.
Definition: drawCullHandler.h:30
Thread::is_threading_supported
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
Texture::has_textures_power_2
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
winInputDeviceManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cullFaceAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsStateGuardian::prepare_display_region
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
Definition: graphicsStateGuardian.cxx:2116
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Lens
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
GraphicsOutput::request_close
virtual void request_close()
This is called by the GraphicsEngine to request that the window (or whatever) close itself or,...
Definition: graphicsOutput.cxx:1075
GraphicsStateGuardian::clear_state_and_transform
virtual void clear_state_and_transform()
Forgets the current graphics state and current transform, so that the next call to set_state_and_tran...
Definition: graphicsStateGuardian.cxx:2179
Pipeline::set_min_stages
void set_min_stages(int min_stages)
Ensures that at least the indicated number of stages are in the pipeline.
Definition: pipeline.I:29
reMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsStateGuardian::end_scene
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
Definition: graphicsStateGuardian.cxx:2300
CallbackGraphicsWindow
This special window object doesn't represent a window in its own right, but instead hooks into some t...
Definition: callbackGraphicsWindow.h:26
GraphicsEngine::GraphicsEngine
GraphicsEngine(Pipeline *pipeline=nullptr)
Creates a new GraphicsEngine object.
Definition: graphicsEngine.cxx:151
ClockObject::check_errors
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
GraphicsEngine::sync_frame
void sync_frame()
Waits for all the threads that started drawing their last frame to finish drawing.
Definition: graphicsEngine.cxx:1061
DisplayRegionCullCallbackData
This specialization on CallbackData is passed when the callback is initiated from the cull traversal,...
Definition: displayRegionCullCallbackData.h:27
DisplayRegion::is_stereo
is_stereo
Returns true if this is a StereoDisplayRegion, false otherwise.
Definition: displayRegion.h:90
PStatClient::is_connected
static bool is_connected()
Returns true if the client believes it is connected to a working PStatServer, false otherwise.
Definition: pStatClient.h:291
LensNode::get_lens
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
vertexDataSaveFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SimpleLru::get_total_size
size_t get_total_size() const
Returns the total size of all objects currently active on the LRU.
Definition: simpleLru.I:18
GraphicsEngine::dispatch_compute
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...
Definition: graphicsEngine.cxx:1186
GraphicsOutput::get_fb_properties
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
Definition: graphicsOutput.I:413
callbackGraphicsWindow.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup::set_camera_node
void set_camera_node(Camera *camera_node)
Specifies the camera used to render the scene.
Definition: sceneSetup.I:107
GraphicsStateGuardian::extract_texture_data
virtual bool extract_texture_data(Texture *tex)
This method should only be called by the GraphicsEngine.
Definition: graphicsStateGuardian.cxx:591
Thread::prepare_for_exit
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
GraphicsOutput::reset_window
virtual void reset_window(bool swapchain)
Resets the window framebuffer from its derived children.
Definition: graphicsOutput.cxx:1091
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
VertexDataPage::get_pending_lru
static SimpleLru * get_pending_lru()
Returns a pointer to the global LRU object that manages the VertexDataPage's that are pending process...
Definition: vertexDataPage.I:93
GraphicsThreadingModel::get_cull_name
const std::string & get_cull_name() const
Returns the name of the thread that will handle culling in this model.
Definition: graphicsThreadingModel.I:43
ClockObject::get_frame_count
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
Definition: clockObject.h:94
depthTestAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath::get_top
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
pStatGPUTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_host
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
Definition: graphicsOutput.cxx:1055
pStatClient.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_delete_flag
bool get_delete_flag() const
Returns the current setting of the delete flag.
Definition: graphicsOutput.cxx:568
mutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_clear_window_pcollector
PStatCollector & get_clear_window_pcollector()
Returns a PStatCollector for timing the clear operation for just this GraphicsOutput.
Definition: graphicsOutput.I:711
binCullHandler.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsStateGuardian::clear_before_callback
virtual void clear_before_callback()
Resets any non-standard graphics state that might give a callback apoplexy.
Definition: graphicsStateGuardian.cxx:2169
DisplayRegionPipelineReader::get_window
GraphicsOutput * get_window() const
Returns the GraphicsOutput that this DisplayRegion is ultimately associated with, or NULL if no windo...
Definition: displayRegion.I:701
CullResult::finish_cull
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:265
vertexDataPage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_pipe
get_pipe
Returns the GraphicsPipe that this window is associated with.
Definition: graphicsOutput.h:118
GraphicsEngine::do_cull
static void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, GraphicsStateGuardian *gsg, Thread *current_thread)
Fires off a cull traversal using the indicated camera.
Definition: graphicsEngine.cxx:1313
Thread::get_current_pipeline_stage
get_current_pipeline_stage
Returns the integer pipeline stage associated with the current thread.
Definition: thread.h:110
PT
PT(Texture) GraphicsEngine
Called by DisplayRegion::do_get_screenshot.
Definition: graphicsEngine.cxx:1275
GraphicsThreadingModel::is_default
bool is_default() const
Returns true if the threading model is the default, cull-then-draw single- threaded model,...
Definition: graphicsThreadingModel.I:134
CullTraverser::end_traverse
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
Definition: cullTraverser.cxx:220
VertexDataPage::stop_threads
static void stop_threads()
Call this to stop the paging threads, if they were started.
Definition: vertexDataPage.cxx:171
ClockObject::tick
void tick(Thread *current_thread=Thread::get_current_thread())
Instructs the clock that a new frame has just begun.
Definition: clockObject.cxx:346
GraphicsThreadingModel::get_cull_sorting
bool get_cull_sorting() const
Returns true if the model involves a separate cull pass, or false if culling happens implicitly,...
Definition: graphicsThreadingModel.I:104
DisplayRegionPipelineReader::get_pixel_height
int get_pixel_height(int i=0) const
Returns the height of the DisplayRegion in pixels.
Definition: displayRegion.I:870
GraphicsOutput::request_open
virtual void request_open()
This is called by the GraphicsEngine to request that the window (or whatever) open itself or,...
Definition: graphicsOutput.cxx:1065
SceneSetup::set_world_transform
void set_world_transform(const TransformState *world_transform)
Specifies the position of the starting node relative to the camera.
Definition: sceneSetup.I:223
GraphicsOutput::is_active
is_active
Returns true if the window is ready to be rendered into, false otherwise.
Definition: graphicsOutput.h:155
VertexDataSaveFile
A temporary file to hold the vertex data that has been evicted from memory and written to disk.
Definition: vertexDataSaveFile.h:36
GraphicsEngine::texture_uploaded
void texture_uploaded(Texture *tex)
This method is called by the GraphicsStateGuardian after a texture has been successfully uploaded to ...
Definition: graphicsEngine.cxx:1260
DisplayRegion::get_cull_result
CullResult * get_cull_result(Thread *current_thread) const
Returns the CullResult value that was stored on this DisplayRegion, presumably by the last successful...
Definition: displayRegion.I:492
GraphicsOutput::end_frame
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.
Definition: graphicsOutput.cxx:1173
GraphicsStateGuardian::get_prepared_objects
get_prepared_objects
Returns the set of texture and geom objects that have been prepared with this GSG (and possibly other...
Definition: graphicsStateGuardian.h:246
VertexDataSaveFile::get_total_file_size
size_t get_total_file_size() const
Returns the amount of space consumed by the save file, including unused portions.
Definition: vertexDataSaveFile.I:28
CullTraverser::flush_level
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: cullTraverser.I:188
GraphicsStateGuardian
Encapsulates all the communication with a particular instance of a given rendering backend.
Definition: graphicsStateGuardian.h:65
RenderState::flush_level
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: renderState.I:479
GraphicsOutput::get_sort
get_sort
Returns the sorting order of this particular GraphicsOutput.
Definition: graphicsOutput.h:192
geomCacheManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CullTraverser::traverse
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
Definition: cullTraverser.cxx:106
CullableObject::flush_level
static void flush_level()
Flushes the PStatCollectors used during traversal.
Definition: cullableObject.I:122
VertexDataSaveFile::get_used_file_size
size_t get_used_file_size() const
Returns the amount of space within the save file that is currently in use.
Definition: vertexDataSaveFile.I:36
SceneSetup::set_cs_transform
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
BoundingVolume
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Definition: boundingVolume.h:41
ov_set< PT(GraphicsOutput), IndirectLess< GraphicsOutput > >
GraphicsEngine::get_window
get_window
Returns the nth window or buffers managed by the engine, in sorted order.
Definition: graphicsEngine.h:103
drawCullHandler.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
GraphicsStateGuardian::dispatch_compute
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.
Definition: graphicsStateGuardian.cxx:747
PStatGPUTimer
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
NodePath::node
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
config_display.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsOutput::get_inverted
get_inverted
Returns the current setting of the inverted flag.
Definition: graphicsOutput.h:163
BamCache::flush_global_index
static void flush_global_index()
If there is a global BamCache object, calls flush_index() on it.
Definition: bamCache.I:244
GraphicsOutput::clear_pipe
virtual void clear_pipe()
Sets the window's _pipe pointer to NULL; this is generally called only as a precursor to deleting the...
Definition: graphicsOutput.cxx:1101
GraphicsOutput::end_flip
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
Definition: graphicsOutput.cxx:1299
GeomVertexArrayData::get_small_lru
static SimpleLru * get_small_lru()
Returns a pointer to the global LRU object that manages the GeomVertexArrayData's that are deemed too...
Definition: geomVertexArrayData.I:192
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
config_pgraph.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DisplayRegionPipelineReader
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
Definition: displayRegion.h:311
CullResult
This stores the result of a BinCullHandler traversal: an ordered collection of CullBins,...
Definition: cullResult.h:44
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup::set_lens
void set_lens(const Lens *lens)
Indicates the particular Lens used for rendering.
Definition: sceneSetup.I:123
DisplayRegionDrawCallbackData
This specialization on CallbackData is passed when the callback is initiated from the draw traversal,...
Definition: displayRegionDrawCallbackData.h:27
AsyncTaskManager::stop_threads
void stop_threads()
Stops any threads that are currently running.
Definition: asyncTaskManager.cxx:376
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
Thread::force_yield
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
Pipeline::get_num_stages
int get_num_stages() const
Returns the number of stages required for the pipeline.
Definition: pipeline.I:37
ReferenceCount::get_ref_count
get_ref_count
Returns the current reference count.
Definition: referenceCount.h:53
CullTraverser::set_cull_handler
void set_cull_handler(CullHandler *cull_handler)
Specifies the object that will receive the culled Geoms.
Definition: cullTraverser.I:144
GraphicsEngine::open_windows
void open_windows()
Fully opens (or closes) any windows that have recently been requested open or closed,...
Definition: graphicsEngine.cxx:965
geomVertexArrayData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsThreadingModel::get_cull_stage
int get_cull_stage() const
Returns the pipeline stage from which the cull thread should access data.
Definition: graphicsThreadingModel.I:64
GraphicsEngine::reset_all_windows
void reset_all_windows(bool swapchain)
Resets the framebuffer of the current window.
Definition: graphicsEngine.cxx:660
GraphicsEngine::set_threading_model
set_threading_model
Specifies how future objects created via make_gsg(), make_buffer(), and make_window() will be threade...
Definition: graphicsEngine.h:60
GraphicsStateGuardian::release_all
void release_all()
Releases all prepared objects.
Definition: graphicsStateGuardian.I:21
PandaNode::set_scene_root_func
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
GraphicsEngine::remove_all_windows
void remove_all_windows()
Removes and closes all windows from the engine.
Definition: graphicsEngine.cxx:589
GraphicsOutput::begin_frame
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.
Definition: graphicsOutput.cxx:1163
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsEngine::get_threading_model
get_threading_model
Returns the threading model that will be applied to future objects.
Definition: graphicsEngine.h:60
CullHandler
This defines the abstract interface for an object that receives Geoms identified by the CullTraverser...
Definition: cullHandler.h:28
Shader::get_filename
Filename get_filename(ShaderType type=ST_none) const
Return the Shader's filename for the given shader type.
Definition: shader.I:20
NodePath::is_empty
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
pStatCollector.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.