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