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  for (GraphicsOutput *win : old_windows) {
597  nassertv(win != nullptr);
598  do_remove_window(win, current_thread);
599  GraphicsStateGuardian *gsg = win->get_gsg();
600  if (gsg != nullptr) {
601  gsg->release_all();
602  }
603  }
604 
605  {
606  MutexHolder new_windows_holder(_new_windows_lock, current_thread);
607  for (GraphicsOutput *win : _new_windows) {
608  nassertv(win != nullptr);
609  do_remove_window(win, current_thread);
610  GraphicsStateGuardian *gsg = win->get_gsg();
611  if (gsg != nullptr) {
612  gsg->release_all();
613  }
614  }
615  _new_windows.clear();
616  }
617 
618  _app.do_close(this, current_thread);
619  _app.do_pending(this, current_thread);
620  terminate_threads(current_thread);
621 
622  // It seems a safe assumption that we're about to exit the application or
623  // otherwise shut down Panda. Although it's a bit of a hack, since it's not
624  // really related to removing windows, this would nevertheless be a fine
625  // time to ensure the model cache (if any) has been flushed to disk.
627 
628  // And, hey, let's stop the vertex paging threads, if any.
630 
631  // Stopping the tasks means we have to release the Python GIL while
632  // this method runs (hence it is marked BLOCKING), so that any
633  // Python tasks on other threads won't deadlock grabbing the GIL.
635 
636 #ifdef DO_PSTATS
637  PStatClient::get_global_pstats()->disconnect();
638 #endif
639 
640  // Well, and why not clean up all threads here?
642 }
643 
644 /**
645  * Resets the framebuffer of the current window. This is currently used by
646  * DirectX 8 only. It calls a reset_window function on each active window to
647  * release/create old/new framebuffer
648  */
649 void GraphicsEngine::
650 reset_all_windows(bool swapchain) {
651  Windows::iterator wi;
652  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
653  GraphicsOutput *win = (*wi);
654  // if (win->is_active())
655  win->reset_window(swapchain);
656  }
657 }
658 
659 /**
660  * Returns true if there are no windows or buffers managed by the engine,
661  * false if there is at least one.
662  */
663 bool GraphicsEngine::
664 is_empty() const {
665  return _windows.empty();
666 }
667 
668 /**
669  * Returns the number of windows (or buffers) managed by the engine.
670  */
671 int GraphicsEngine::
672 get_num_windows() const {
673  return _windows.size();
674 }
675 
676 /**
677  * Returns the nth window or buffers managed by the engine, in sorted order.
678  */
680 get_window(int n) const {
681  nassertr(n >= 0 && n < (int)_windows.size(), nullptr);
682 
683  if (!_windows_sorted) {
684  ((GraphicsEngine *)this)->do_resort_windows();
685  }
686  return _windows[n];
687 }
688 
689 /**
690  * Renders the next frame in all the registered windows, and flips all of the
691  * frame buffers.
692  */
693 void GraphicsEngine::
695  Thread *current_thread = Thread::get_current_thread();
696  ReMutexHolder public_holder(_public_lock);
697 
698  // Since this gets called every frame, we should take advantage of the
699  // opportunity to flush the cache if necessary.
701 
702  // Anything that happens outside of GraphicsEngine::render_frame() is deemed
703  // to be App.
704 #ifdef DO_PSTATS
705  _render_frame_pcollector.start();
706  if (_app_pcollector.is_started()) {
707  _app_pcollector.stop();
708  }
709 #endif
710 
711  // Make sure our buffers and windows are fully realized before we render a
712  // frame. We do this particularly to realize our offscreen buffers, so
713  // that we don't render a frame before the offscreen buffers are ready
714  // (which might result in a frame going by without some textures having
715  // been rendered).
716  open_windows();
717 
718  ClockObject *global_clock = ClockObject::get_global_clock();
719 
720  if (display_cat.is_spam()) {
721  display_cat.spam()
722  << "render_frame() - frame " << global_clock->get_frame_count() << "\n";
723  }
724 
725  {
726  ReMutexHolder holder(_lock, current_thread);
727 
728  if (!_windows_sorted) {
729  do_resort_windows();
730  }
731 
732  if (sync_flip && _flip_state != FS_flip) {
733  do_flip_frame(current_thread);
734  }
735 
736  // Are any of the windows ready to be deleted?
737  Windows new_windows;
738  new_windows.reserve(_windows.size());
739  Windows::iterator wi;
740  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
741  GraphicsOutput *win = (*wi);
742  nassertv(win != nullptr);
743  if (win->get_delete_flag()) {
744  do_remove_window(win, current_thread);
745 
746  } else {
747  new_windows.push_back(win);
748 
749  // Let's calculate each scene's bounding volume here in App, before we
750  // cycle the pipeline. The cull traversal will calculate it anyway,
751  // but if we calculate it in App first before it gets calculated in
752  // the Cull thread, it will be more likely to stick for subsequent
753  // frames, so we won't have to recompute it each frame.
754  int num_drs = win->get_num_active_display_regions();
755  for (int i = 0; i < num_drs; ++i) {
756  PT(DisplayRegion) dr = win->get_active_display_region(i);
757  if (dr != nullptr) {
758  NodePath camera_np = dr->get_camera(current_thread);
759  if (!camera_np.is_empty()) {
760  Camera *camera = DCAST(Camera, camera_np.node());
761  NodePath scene = camera->get_scene();
762  if (scene.is_empty()) {
763  scene = camera_np.get_top(current_thread);
764  }
765  if (!scene.is_empty()) {
766  scene.get_bounds(current_thread);
767  }
768  }
769  }
770  }
771  }
772  }
773  _windows.swap(new_windows);
774 
775  // Go ahead and release any textures' ram images for textures that were
776  // drawn in the previous frame.
777  {
778  MutexHolder holder2(_loaded_textures_lock);
779  LoadedTextures::iterator lti;
780  for (lti = _loaded_textures.begin(); lti != _loaded_textures.end(); ++lti) {
781  LoadedTexture &lt = (*lti);
782  if (lt._tex->get_image_modified() == lt._image_modified) {
783  lt._tex->texture_uploaded();
784  }
785  }
786  _loaded_textures.clear();
787  }
788 
789  // Now it's time to do any drawing from the main frame--after all of the
790  // App code has executed, but before we begin the next frame.
791  _app.do_frame(this, current_thread);
792 
793  // Grab each thread's mutex again after all windows have flipped, and wait
794  // for the thread to finish.
795  {
796  PStatTimer timer(_wait_pcollector, current_thread);
797  Threads::const_iterator ti;
798  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
799  RenderThread *thread = (*ti).second;
800  thread->_cv_mutex.acquire();
801 
802  while (thread->_thread_state != TS_wait) {
803  thread->_cv_done.wait();
804  }
805  }
806  }
807 
808 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS)
809  _cyclers_pcollector.set_level(_pipeline->get_num_cyclers());
810  _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers());
811 
812 #ifdef DEBUG_THREADS
814  _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this);
815  _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this);
816  }
817 #endif // DEBUG_THREADS
818 
819 #endif // THREADED_PIPELINE && DO_PSTATS
820 
824  TransformState::flush_level();
826 
827  // Now cycle the pipeline and officially begin the next frame.
828 #ifdef THREADED_PIPELINE
829  {
830  PStatTimer timer(_cycle_pcollector, current_thread);
831  _pipeline->cycle();
832  }
833 #endif // THREADED_PIPELINE
834 
835  global_clock->tick(current_thread);
836  if (global_clock->check_errors(current_thread)) {
837  throw_event("clock_error");
838  }
839 
840 #ifdef DO_PSTATS
841  PStatClient::main_tick();
842 
843  // Reset our pcollectors that track data across the frame.
844  CullTraverser::_nodes_pcollector.clear_level();
845  CullTraverser::_geom_nodes_pcollector.clear_level();
846  CullTraverser::_geoms_pcollector.clear_level();
847  GeomCacheManager::_geom_cache_active_pcollector.clear_level();
848  GeomCacheManager::_geom_cache_record_pcollector.clear_level();
849  GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
850  GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
851 
852  GraphicsStateGuardian::init_frame_pstats();
853 
854  _transform_states_pcollector.set_level(TransformState::get_num_states());
855  _render_states_pcollector.set_level(RenderState::get_num_states());
856  if (pstats_unused_states) {
857  _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states());
858  _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states());
859  }
860 
861  _sw_sprites_pcollector.clear_level();
862 
863  _cnode_volume_pcollector.clear_level();
864  _gnode_volume_pcollector.clear_level();
865  _geom_volume_pcollector.clear_level();
866  _node_volume_pcollector.clear_level();
867  _volume_pcollector.clear_level();
868  _test_pcollector.clear_level();
869  _volume_polygon_pcollector.clear_level();
870  _test_polygon_pcollector.clear_level();
871  _volume_plane_pcollector.clear_level();
872  _test_plane_pcollector.clear_level();
873  _volume_sphere_pcollector.clear_level();
874  _test_sphere_pcollector.clear_level();
875  _volume_box_pcollector.clear_level();
876  _test_box_pcollector.clear_level();
877  _volume_capsule_pcollector.clear_level();
878  _test_capsule_pcollector.clear_level();
879  _volume_inv_sphere_pcollector.clear_level();
880  _test_inv_sphere_pcollector.clear_level();
881  _volume_geom_pcollector.clear_level();
882  _test_geom_pcollector.clear_level();
883  _occlusion_untested_pcollector.clear_level();
884  _occlusion_passed_pcollector.clear_level();
885  _occlusion_failed_pcollector.clear_level();
886  _occlusion_tests_pcollector.clear_level();
887 
889  size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
891  size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size();
892  size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size();
893  size_t pending = VertexDataPage::get_pending_lru()->get_total_size();
894 
895  VertexDataSaveFile *save_file = VertexDataPage::get_save_file();
896  size_t total_disk = save_file->get_total_file_size();
897  size_t used_disk = save_file->get_used_file_size();
898 
899  _vertex_data_small_pcollector.set_level(small_buf);
900  _vertex_data_independent_pcollector.set_level(independent);
901  _vertex_data_pending_pcollector.set_level(pending);
902  _vertex_data_resident_pcollector.set_level(resident);
903  _vertex_data_compressed_pcollector.set_level(compressed);
904  _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk);
905  _vertex_data_used_disk_pcollector.set_level(used_disk);
906  }
907 
908 #endif // DO_PSTATS
909 
911 
912  // Now signal all of our threads to begin their next frame.
913  Threads::const_iterator ti;
914  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
915  RenderThread *thread = (*ti).second;
916  if (thread->_thread_state == TS_wait) {
917  thread->_thread_state = TS_do_frame;
918  thread->_cv_start.notify();
919  }
920  thread->_cv_mutex.release();
921  }
922 
923  // Some threads may still be drawing, so indicate that we have to wait for
924  // those threads before we can flip.
925  _flip_state = _auto_flip ? FS_flip : FS_draw;
926  }
927 
928  // Now the lock is released.
929 
930  if (yield_timeslice) {
931  // Nap for a moment to yield the timeslice, to be polite to other running
932  // applications.
933  PStatTimer timer(_yield_pcollector, current_thread);
935  } else if (!Thread::is_true_threads()) {
936  PStatTimer timer(_yield_pcollector, current_thread);
938  }
939 
940  // Anything that happens outside of GraphicsEngine::render_frame() is deemed
941  // to be App.
942  _app_pcollector.start();
943  _render_frame_pcollector.stop();
944 }
945 
946 
947 /**
948  * Fully opens (or closes) any windows that have recently been requested open
949  * or closed, without rendering any frames. It is not necessary to call this
950  * explicitly, since windows will be automatically opened or closed when the
951  * next frame is rendered, but you may call this if you want your windows now
952  * without seeing a frame go by.
953  */
954 void GraphicsEngine::
956  Thread *current_thread = Thread::get_current_thread();
957 
958  ReMutexHolder holder(_lock, current_thread);
959 
960  pvector<PT(GraphicsOutput)> new_windows;
961  {
962  MutexHolder new_windows_holder(_new_windows_lock, current_thread);
963  if (_new_windows.empty()) {
964  return;
965  }
966 
967  for (auto it = _new_windows.begin(); it != _new_windows.end(); ++it) {
968  GraphicsOutput *window = *it;
969 
970  WindowRenderer *cull =
971  get_window_renderer(_threading_model.get_cull_name(),
972  _threading_model.get_cull_stage());
973  WindowRenderer *draw =
974  get_window_renderer(_threading_model.get_draw_name(),
975  _threading_model.get_draw_stage());
976 
977  if (_threading_model.get_cull_sorting()) {
978  cull->add_window(cull->_cull, window);
979  draw->add_window(draw->_draw, window);
980  } else {
981  cull->add_window(cull->_cdraw, window);
982  }
983 
984  // Ask the pipe which thread it prefers to run its windowing commands in
985  // (the "window thread"). This is the thread that handles the commands
986  // to open, resize, etc. the window. X requires this to be done in the
987  // app thread (along with all the other windows, since X is strictly
988  // single-threaded), but Windows requires this to be done in draw
989  // (because once an OpenGL context has been bound in a given thread, it
990  // cannot subsequently be bound in any other thread, and we have to bind
991  // a context in open_window()).
992 
993  switch (window->get_pipe()->get_preferred_window_thread()) {
994  case GraphicsPipe::PWT_app:
995  _app.add_window(_app._window, window);
996  break;
997 
998  case GraphicsPipe::PWT_draw:
999  draw->add_window(draw->_window, window);
1000  break;
1001  }
1002 
1003  _windows.push_back(window);
1004  }
1005 
1006  // Steal the list, since remove_window() may remove from _new_windows.
1007  new_windows.swap(_new_windows);
1008  }
1009 
1010  do_resort_windows();
1011 
1012  // We do it twice, to allow both cull and draw to process the window.
1013  for (int i = 0; i < 2; ++i) {
1014  _app.do_windows(this, current_thread);
1015  _app.do_pending(this, current_thread);
1016 
1017  PStatTimer timer(_wait_pcollector, current_thread);
1018  Threads::const_iterator ti;
1019  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1020  RenderThread *thread = (*ti).second;
1021  thread->_cv_mutex.acquire();
1022 
1023  while (thread->_thread_state != TS_wait) {
1024  thread->_cv_done.wait();
1025  }
1026 
1027  thread->_thread_state = TS_do_windows;
1028  thread->_cv_start.notify();
1029  thread->_cv_mutex.release();
1030  }
1031  }
1032 
1033  // Now go through the list again to check whether they opened successfully.
1034  for (auto it = new_windows.begin(); it != new_windows.end(); ++it) {
1035  GraphicsOutput *window = *it;
1036  if (window->is_valid()) {
1037  do_add_gsg(window->get_gsg(), window->get_pipe());
1038  } else {
1039  remove_window(window);
1040  }
1041  }
1042 }
1043 
1044 /**
1045  * Waits for all the threads that started drawing their last frame to finish
1046  * drawing. The windows are not yet flipped when this returns; see also
1047  * flip_frame(). It is not usually necessary to call this explicitly, unless
1048  * you need to see the previous frame right away.
1049  */
1050 void GraphicsEngine::
1052  Thread *current_thread = Thread::get_current_thread();
1053  ReMutexHolder holder(_lock, current_thread);
1054 
1055  if (_flip_state == FS_draw) {
1056  do_sync_frame(current_thread);
1057  }
1058 }
1059 
1060 
1061 /**
1062  * Waits for all the threads that started drawing their last frame to finish
1063  * drawing. Returns when all threads have actually finished drawing, as
1064  * opposed to 'sync_frame' we seems to return once all draw calls have been
1065  * submitted. Calling 'flip_frame' after this function should immediately
1066  * cause a buffer flip. This function will only work in opengl right now, for
1067  * all other graphics pipelines it will simply return immediately. In opengl
1068  * it's a bit of a hack: it will attempt to read a single pixel from the frame
1069  * buffer to force the graphics card to finish drawing before it returns
1070  */
1071 void GraphicsEngine::
1073  Thread *current_thread = Thread::get_current_thread();
1074  ReMutexHolder holder(_lock, current_thread);
1075 
1076  if (_flip_state == FS_draw) {
1077  do_ready_flip(current_thread);
1078  }
1079 }
1080 
1081 /**
1082  * Waits for all the threads that started drawing their last frame to finish
1083  * drawing, and then flips all the windows. It is not usually necessary to
1084  * call this explicitly, unless you need to see the previous frame right away.
1085  */
1086 void GraphicsEngine::
1088  Thread *current_thread = Thread::get_current_thread();
1089  ReMutexHolder holder(_lock, current_thread);
1090 
1091  if (_flip_state != FS_flip) {
1092  do_flip_frame(current_thread);
1093  }
1094 }
1095 
1096 /**
1097  * Asks the indicated GraphicsStateGuardian to retrieve the texture memory
1098  * image of the indicated texture and store it in the texture's ram_image
1099  * field. The image can then be written to disk via Texture::write(), or
1100  * otherwise manipulated on the CPU.
1101  *
1102  * This is useful for retrieving the contents of a texture that has been
1103  * somehow generated on the graphics card, instead of having been loaded the
1104  * normal way via Texture::read() or Texture::load(). It is particularly
1105  * useful for getting the data associated with a compressed texture image.
1106  *
1107  * Since this requires a round-trip to the draw thread, it may require waiting
1108  * for the current thread to finish rendering if it is called in a
1109  * multithreaded environment. However, you can call this several consecutive
1110  * times on different textures for little additional cost.
1111  *
1112  * If the texture has not yet been loaded to the GSG in question, it will be
1113  * loaded immediately.
1114  *
1115  * The return value is true if the operation is successful, false otherwise.
1116  */
1117 bool GraphicsEngine::
1119  ReMutexHolder holder(_lock);
1120 
1121  string draw_name = gsg->get_threading_model().get_draw_name();
1122  if (draw_name.empty()) {
1123  // A single-threaded environment. No problem.
1124  return gsg->extract_texture_data(tex);
1125 
1126  } else {
1127  // A multi-threaded environment. We have to wait until the draw thread
1128  // has finished its current task.
1129  WindowRenderer *wr = get_window_renderer(draw_name, 0);
1130  RenderThread *thread = (RenderThread *)wr;
1131  MutexHolder cv_holder(thread->_cv_mutex);
1132 
1133  while (thread->_thread_state != TS_wait) {
1134  thread->_cv_done.wait();
1135  }
1136 
1137  // Temporarily set this so that it accesses data from the current thread.
1138  int pipeline_stage = Thread::get_current_pipeline_stage();
1139  int draw_pipeline_stage = thread->get_pipeline_stage();
1140  thread->set_pipeline_stage(pipeline_stage);
1141 
1142  // Now that the draw thread is idle, signal it to do the extraction task.
1143  thread->_gsg = gsg;
1144  thread->_texture = tex;
1145  thread->_thread_state = TS_do_extract;
1146  thread->_cv_start.notify();
1147  thread->_cv_mutex.release();
1148  thread->_cv_mutex.acquire();
1149 
1150  //XXX is this necessary, or is acquiring the mutex enough?
1151  while (thread->_thread_state != TS_wait) {
1152  thread->_cv_done.wait();
1153  }
1154 
1155  thread->set_pipeline_stage(draw_pipeline_stage);
1156  thread->_gsg = nullptr;
1157  thread->_texture = nullptr;
1158  return thread->_result;
1159  }
1160 }
1161 
1162 /**
1163  * Asks the indicated GraphicsStateGuardian to dispatch the compute shader in
1164  * the given ShaderAttrib using the given work group counts. This can act as
1165  * an interface for running a one-off compute shader, without having to store
1166  * it in the scene graph using a ComputeNode.
1167  *
1168  * Since this requires a round-trip to the draw thread, it may require waiting
1169  * for the current thread to finish rendering if it is called in a
1170  * multithreaded environment. However, you can call this several consecutive
1171  * times on different textures for little additional cost.
1172  *
1173  * The return value is true if the operation is successful, false otherwise.
1174  */
1175 void GraphicsEngine::
1176 dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
1177  const Shader *shader = sattr->get_shader();
1178  nassertv(shader != nullptr);
1179  nassertv(gsg != nullptr);
1180 
1181  ReMutexHolder holder(_lock);
1182 
1183  CPT(RenderState) state = RenderState::make(sattr);
1184 
1185  string draw_name = gsg->get_threading_model().get_draw_name();
1186  if (draw_name.empty()) {
1187  // A single-threaded environment. No problem.
1188  gsg->push_group_marker(std::string("Compute ") + shader->get_filename(Shader::ST_compute).get_basename());
1189  gsg->set_state_and_transform(state, TransformState::make_identity());
1190  gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]);
1191  gsg->pop_group_marker();
1192 
1193  } else {
1194  // A multi-threaded environment. We have to wait until the draw thread
1195  // has finished its current task.
1196  WindowRenderer *wr = get_window_renderer(draw_name, 0);
1197  RenderThread *thread = (RenderThread *)wr;
1198  MutexHolder cv_holder(thread->_cv_mutex);
1199 
1200  while (thread->_thread_state != TS_wait) {
1201  thread->_cv_done.wait();
1202  }
1203 
1204  // Temporarily set this so that it accesses data from the current thread.
1205  int pipeline_stage = Thread::get_current_pipeline_stage();
1206  int draw_pipeline_stage = thread->get_pipeline_stage();
1207  thread->set_pipeline_stage(pipeline_stage);
1208 
1209  // Now that the draw thread is idle, signal it to do the compute task.
1210  thread->_gsg = gsg;
1211  thread->_state = state.p();
1212  thread->_work_groups = work_groups;
1213  thread->_thread_state = TS_do_compute;
1214  thread->_cv_start.notify();
1215  thread->_cv_mutex.release();
1216  thread->_cv_mutex.acquire();
1217 
1218  //XXX is this necessary, or is acquiring the mutex enough?
1219  while (thread->_thread_state != TS_wait) {
1220  thread->_cv_done.wait();
1221  }
1222 
1223  thread->set_pipeline_stage(draw_pipeline_stage);
1224  thread->_gsg = nullptr;
1225  thread->_state = nullptr;
1226  }
1227 }
1228 
1229 /**
1230  *
1231  */
1232 GraphicsEngine *GraphicsEngine::
1233 get_global_ptr() {
1234  if (_global_ptr == nullptr) {
1235  _global_ptr = new GraphicsEngine;
1236  PandaNode::set_scene_root_func(&scene_root_func);
1237  }
1238  return _global_ptr;
1239 }
1240 
1241 /**
1242  * This method is called by the GraphicsStateGuardian after a texture has been
1243  * successfully uploaded to graphics memory. It is intended as a callback so
1244  * the texture can release its RAM image, if _keep_ram_image is false.
1245  *
1246  * Normally, this is not called directly except by the GraphicsStateGuardian.
1247  * It will be called in the draw thread.
1248  */
1249 void GraphicsEngine::
1251  MutexHolder holder(_loaded_textures_lock);
1252  // We defer this until the end of the frame; multiple GSG's might be
1253  // rendering the texture within the same frame, and we don't want to dump
1254  // the texture image until they've all had a chance at it.
1255  _loaded_textures.push_back(LoadedTexture());
1256  LoadedTexture &lt = _loaded_textures.back();
1257  lt._tex = tex;
1258  lt._image_modified = tex->get_image_modified();
1259 // Usually only called by DisplayRegion::do_cull.
1260 }
1261 
1262 /**
1263  * Called by DisplayRegion::do_get_screenshot
1264  */
1265 PT(Texture) GraphicsEngine::
1266 do_get_screenshot(DisplayRegion *region, GraphicsStateGuardian *gsg) {
1267  // A multi-threaded environment. We have to wait until the draw thread
1268  // has finished its current task.
1269 
1270  ReMutexHolder holder(_lock);
1271 
1272  const std::string &draw_name = gsg->get_threading_model().get_draw_name();
1273  WindowRenderer *wr = get_window_renderer(draw_name, 0);
1274  RenderThread *thread = (RenderThread *)wr;
1275  MutexHolder cv_holder(thread->_cv_mutex);
1276 
1277  while (thread->_thread_state != TS_wait) {
1278  thread->_cv_done.wait();
1279  }
1280 
1281  // Now that the draw thread is idle, signal it to do the extraction task.
1282  thread->_region = region;
1283  thread->_thread_state = TS_do_screenshot;
1284  thread->_cv_start.notify();
1285  thread->_cv_mutex.release();
1286  thread->_cv_mutex.acquire();
1287 
1288  //XXX is this necessary, or is acquiring the mutex enough?
1289  while (thread->_thread_state != TS_wait) {
1290  thread->_cv_done.wait();
1291  }
1292 
1293  PT(Texture) tex = std::move(thread->_texture);
1294  thread->_region = nullptr;
1295  thread->_texture = nullptr;
1296  return tex;
1297 }
1298 
1299 /**
1300  * Fires off a cull traversal using the indicated camera.
1301  */
1302 void GraphicsEngine::
1303 do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
1304  GraphicsStateGuardian *gsg, Thread *current_thread) {
1305  DisplayRegion *dr = scene_setup->get_display_region();
1306  PStatTimer timer(dr->get_cull_region_pcollector(), current_thread);
1307 
1308  CullTraverser *trav = dr->get_cull_traverser();
1309  trav->set_cull_handler(cull_handler);
1310  trav->set_scene(scene_setup, gsg, dr->get_incomplete_render());
1311 
1312  trav->set_view_frustum(nullptr);
1313  if (view_frustum_cull) {
1314  // If we're to be performing view-frustum culling, determine the bounding
1315  // volume associated with the current viewing frustum.
1316 
1317  // First, we have to get the current viewing frustum, which comes from the
1318  // lens.
1319  PT(BoundingVolume) bv = scene_setup->get_cull_bounds();
1320 
1321  if (bv != nullptr && !bv->is_infinite() &&
1322  bv->as_geometric_bounding_volume() != nullptr) {
1323  // Transform it into the appropriate coordinate space.
1324  PT(GeometricBoundingVolume) local_frustum;
1325  local_frustum = bv->make_copy()->as_geometric_bounding_volume();
1326  nassertv(!local_frustum.is_null());
1327 
1328  NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
1329  CPT(TransformState) cull_center_transform =
1330  scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
1331  local_frustum->xform(cull_center_transform->get_mat());
1332 
1333  trav->set_view_frustum(local_frustum);
1334  }
1335  }
1336 
1337  trav->traverse(scene_setup->get_scene_root());
1338  trav->end_traverse();
1339 }
1340 
1341 
1342 /**
1343  * This function is added to PandaNode::scene_root_func to implement
1344  * PandaNode::is_scene_root().
1345  */
1346 bool GraphicsEngine::
1347 scene_root_func(const PandaNode *node) {
1348  return _global_ptr->is_scene_root(node);
1349 }
1350 
1351 /**
1352  * Returns true if the indicated node is known to be the render root of some
1353  * active DisplayRegion associated with this GraphicsEngine, false otherwise.
1354  */
1355 bool GraphicsEngine::
1356 is_scene_root(const PandaNode *node) {
1357  Thread *current_thread = Thread::get_current_thread();
1358 
1359  Windows::const_iterator wi;
1360  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
1361  GraphicsOutput *win = (*wi);
1362  if (win->is_active() && win->get_gsg()->is_active()) {
1363  int num_display_regions = win->get_num_active_display_regions();
1364  for (int i = 0; i < num_display_regions; i++) {
1365  PT(DisplayRegion) dr = win->get_active_display_region(i);
1366  if (dr != nullptr) {
1367  NodePath camera = dr->get_camera();
1368  if (camera.is_empty()) {
1369  continue;
1370  }
1371 
1372  Camera *camera_node;
1373  DCAST_INTO_R(camera_node, camera.node(), false);
1374  if (!camera_node->is_active()) {
1375  continue;
1376  }
1377 
1378  NodePath scene_root = camera_node->get_scene();
1379  if (scene_root.is_empty()) {
1380  // If there's no explicit scene specified, use whatever scene the
1381  // camera is parented within. This is the normal and preferred
1382  // case; the use of an explicit scene is now deprecated.
1383  scene_root = camera.get_top(current_thread);
1384  }
1385 
1386  if (scene_root.node() == node) {
1387  return true;
1388  }
1389  }
1390  }
1391  }
1392  }
1393 
1394  return false;
1395 }
1396 
1397 /**
1398  * Changes the sort value of a particular window (or buffer) on the
1399  * GraphicsEngine. This requires securing the mutex.
1400  *
1401  * Users shouldn't call this directly; use GraphicsOutput::set_sort() instead.
1402  */
1403 void GraphicsEngine::
1404 set_window_sort(GraphicsOutput *window, int sort) {
1405  ReMutexHolder holder(_lock);
1406  window->_sort = sort;
1407  _windows_sorted = false;
1408 }
1409 
1410 /**
1411  * This is called in the cull+draw thread by individual RenderThread objects
1412  * during the frame rendering. It culls the geometry and immediately draws
1413  * it, without first collecting it into bins. This is used when the threading
1414  * model begins with the "-" character.
1415  */
1416 void GraphicsEngine::
1417 cull_and_draw_together(GraphicsEngine::Windows wlist,
1418  Thread *current_thread) {
1419  PStatTimer timer(_cull_pcollector, current_thread);
1420 
1421  size_t wlist_size = wlist.size();
1422  for (size_t wi = 0; wi < wlist_size; ++wi) {
1423  GraphicsOutput *win = wlist[wi];
1424  if (win->is_active() && win->get_gsg()->is_active()) {
1425  if (win->flip_ready()) {
1426  {
1427  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1428  win->begin_flip();
1429  }
1430  {
1431  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1432  win->end_flip();
1433  }
1434  }
1435 
1436  if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
1437  if (win->is_any_clear_active()) {
1438  GraphicsStateGuardian *gsg = win->get_gsg();
1439  PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
1440  gsg->push_group_marker("Clear");
1441  win->clear(current_thread);
1442  gsg->pop_group_marker();
1443  }
1444 
1445  int num_display_regions = win->get_num_active_display_regions();
1446  for (int i = 0; i < num_display_regions; i++) {
1447  PT(DisplayRegion) dr = win->get_active_display_region(i);
1448  if (dr != nullptr) {
1449  cull_and_draw_together(win, dr, current_thread);
1450  }
1451  }
1452  win->end_frame(GraphicsOutput::FM_render, current_thread);
1453 
1454  if (_auto_flip) {
1455  if (win->flip_ready()) {
1456  {
1457  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1458  win->begin_flip();
1459  }
1460  {
1461  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1462  win->end_flip();
1463  }
1464  }
1465  }
1466  }
1467  }
1468  }
1469 }
1470 
1471 /**
1472  * Called only from within the inner loop in cull_and_draw_together(), above.
1473  */
1474 void GraphicsEngine::
1475 cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
1476  Thread *current_thread) {
1477  GraphicsStateGuardian *gsg = win->get_gsg();
1478  nassertv(gsg != nullptr);
1479 
1480  gsg->push_group_marker(dr->get_debug_name());
1481 
1482  PT(SceneSetup) scene_setup;
1483 
1484  {
1485  DisplayRegionPipelineReader dr_reader(dr, current_thread);
1486  win->change_scenes(&dr_reader);
1487  gsg->prepare_display_region(&dr_reader);
1488 
1489  if (dr_reader.is_any_clear_active()) {
1490  PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
1491  gsg->clear(dr);
1492  }
1493 
1494  scene_setup = setup_scene(gsg, &dr_reader);
1495  }
1496 
1497  if (scene_setup == nullptr) {
1498  // Never mind.
1499 
1500  } else if (dr->is_stereo()) {
1501  // Don't draw stereo DisplayRegions directly.
1502 
1503  } else if (!gsg->set_scene(scene_setup)) {
1504  // The scene or lens is inappropriate somehow.
1505  display_cat.error()
1506  << gsg->get_type() << " cannot render scene with specified lens.\n";
1507 
1508  } else {
1509  DrawCullHandler cull_handler(gsg);
1510  if (gsg->begin_scene()) {
1511  CallbackObject *cbobj = dr->get_cull_callback();
1512  if (cbobj != nullptr) {
1513  // Issue the cull callback on this DisplayRegion.
1514  DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
1515  cbobj->do_callback(&cbdata);
1516 
1517  // The callback has taken care of the culling.
1518 
1519  } else {
1520  // Perform the cull normally.
1521  dr->do_cull(&cull_handler, scene_setup, gsg, current_thread);
1522  }
1523 
1524  gsg->end_scene();
1525  }
1526  }
1527 
1528  gsg->pop_group_marker();
1529 }
1530 
1531 /**
1532  * This is called in the cull thread by individual RenderThread objects during
1533  * the frame rendering. It collects the geometry into bins in preparation for
1534  * drawing.
1535  */
1536 void GraphicsEngine::
1537 cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) {
1538  PStatTimer timer(_cull_pcollector, current_thread);
1539 
1540  _singular_warning_last_frame = _singular_warning_this_frame;
1541  _singular_warning_this_frame = false;
1542 
1543  // Keep track of the cameras we have already used in this thread to render
1544  // DisplayRegions.
1545  typedef pmap<CullKey, DisplayRegion *> AlreadyCulled;
1546  AlreadyCulled already_culled;
1547 
1548  size_t wlist_size = wlist.size();
1549  for (size_t wi = 0; wi < wlist_size; ++wi) {
1550  GraphicsOutput *win = wlist[wi];
1551  if (win->is_active() && win->get_gsg()->is_active()) {
1552  GraphicsStateGuardian *gsg = win->get_gsg();
1553  PStatTimer timer(win->get_cull_window_pcollector(), current_thread);
1554  int num_display_regions = win->get_num_active_display_regions();
1555  for (int i = 0; i < num_display_regions; ++i) {
1556  PT(DisplayRegion) dr = win->get_active_display_region(i);
1557  if (dr != nullptr) {
1558  PT(SceneSetup) scene_setup;
1559  PT(CullResult) cull_result;
1560  CullKey key;
1561  {
1562  PStatTimer timer(_cull_setup_pcollector, current_thread);
1563  DisplayRegionPipelineReader dr_reader(dr, current_thread);
1564  scene_setup = setup_scene(gsg, &dr_reader);
1565  if (scene_setup == nullptr) {
1566  continue;
1567  }
1568 
1569  key._gsg = gsg;
1570  key._camera = dr_reader.get_camera();
1571  key._lens_index = dr_reader.get_lens_index();
1572  }
1573 
1574  AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(std::move(key), nullptr)).first;
1575  if ((*aci).second == nullptr) {
1576  // We have not used this camera already in this thread. Perform
1577  // the cull operation.
1578  cull_result = dr->get_cull_result(current_thread);
1579  if (cull_result != nullptr) {
1580  cull_result = cull_result->make_next();
1581  } else {
1582  // This DisplayRegion has no cull results; draw it.
1583  cull_result = new CullResult(gsg, dr->get_draw_region_pcollector());
1584  }
1585  (*aci).second = dr;
1586  cull_to_bins(win, gsg, dr, scene_setup, cull_result, current_thread);
1587 
1588  } else {
1589  // We have already culled a scene using this camera in this
1590  // thread, and now we're being asked to cull another scene using
1591  // the same camera. (Maybe this represents two different
1592  // DisplayRegions for the left and right channels of a stereo
1593  // image.) Of course, the cull result will be the same, so just
1594  // use the result from the other DisplayRegion.
1595  DisplayRegion *other_dr = (*aci).second;
1596  cull_result = other_dr->get_cull_result(current_thread);
1597  }
1598 
1599  // Save the results for next frame.
1600  dr->set_cull_result(std::move(cull_result), MOVE(scene_setup), current_thread);
1601  }
1602  }
1603  }
1604  }
1605 }
1606 
1607 /**
1608  * Called only within the inner loop of cull_to_bins(), above.
1609  */
1610 void GraphicsEngine::
1611 cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg,
1612  DisplayRegion *dr, SceneSetup *scene_setup,
1613  CullResult *cull_result, Thread *current_thread) {
1614 
1615  BinCullHandler cull_handler(cull_result);
1616  CallbackObject *cbobj = dr->get_cull_callback();
1617  if (cbobj != nullptr) {
1618  // Issue the cull callback on this DisplayRegion.
1619  DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
1620  cbobj->do_callback(&cbdata);
1621 
1622  // The callback has taken care of the culling.
1623 
1624  } else {
1625  // Perform the cull normally.
1626  dr->do_cull(&cull_handler, scene_setup, gsg, current_thread);
1627  }
1628 
1629  PStatTimer timer(_cull_sort_pcollector, current_thread);
1630  cull_result->finish_cull(scene_setup, current_thread);
1631 }
1632 
1633 /**
1634  * This is called in the draw thread by individual RenderThread objects during
1635  * the frame rendering. It issues the graphics commands to draw the objects
1636  * that have been collected into bins by a previous call to cull_to_bins().
1637  */
1638 void GraphicsEngine::
1639 draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1640  nassertv(wlist.verify_list());
1641 
1642  size_t wlist_size = wlist.size();
1643  for (size_t wi = 0; wi < wlist_size; ++wi) {
1644  GraphicsOutput *win = wlist[wi];
1645 
1646  if (win->is_active()) {
1647  GraphicsStateGuardian *gsg = win->get_gsg();
1648 
1649  GraphicsOutput *host = win->get_host();
1650  if (host->flip_ready()) {
1651  {
1652  // We can't use a PStatGPUTimer before begin_frame, so when using
1653  // GPU timing, it is advisable to set auto-flip to #t.
1654  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1655  host->begin_flip();
1656  }
1657  {
1658  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1659  host->end_flip();
1660  }
1661  }
1662 
1663  if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
1664  // We have to place this collector inside begin_frame, because we need
1665  // a current context for PStatGPUTimer to work.
1666  {
1667  PStatGPUTimer timer(gsg, win->get_draw_window_pcollector(), current_thread);
1668  if (win->is_any_clear_active()) {
1669  PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
1670  win->get_gsg()->push_group_marker("Clear");
1671  win->clear(current_thread);
1672  win->get_gsg()->pop_group_marker();
1673  }
1674 
1675  if (display_cat.is_spam()) {
1676  display_cat.spam()
1677  << "Drawing window " << win->get_name() << "\n";
1678  }
1679  int num_display_regions = win->get_num_active_display_regions();
1680  for (int i = 0; i < num_display_regions; ++i) {
1681  PT(DisplayRegion) dr = win->get_active_display_region(i);
1682  if (dr != nullptr) {
1683  do_draw(win, gsg, dr, current_thread);
1684  }
1685  }
1686  }
1687  win->end_frame(GraphicsOutput::FM_render, current_thread);
1688 
1689  if (_auto_flip) {
1690 #ifdef DO_PSTATS
1691  // This is a good time to perform a latency query.
1692  if (gsg->get_timer_queries_active()) {
1693  gsg->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index());
1694  }
1695 #endif
1696 
1697  if (win->flip_ready()) {
1698  {
1699  // begin_flip doesn't do anything interesting, let's not waste
1700  // two timer queries on that.
1701  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1702  win->begin_flip();
1703  }
1704  {
1705  PStatGPUTimer timer(gsg, GraphicsEngine::_flip_end_pcollector, current_thread);
1706  win->end_flip();
1707  }
1708  }
1709  }
1710 
1711  } else {
1712  if (display_cat.is_spam()) {
1713  display_cat.spam()
1714  << "Not drawing window " << win->get_name() << "\n";
1715  }
1716  }
1717 
1718  } else {
1719  if (display_cat.is_spam()) {
1720  display_cat.spam()
1721  << "Window " << win->get_name() << " is inactive\n";
1722  }
1723  }
1724  }
1725 }
1726 
1727 /**
1728  * Called in the draw thread, this calls make_context() on each window on the
1729  * list to guarantee its gsg and graphics context both get created.
1730  */
1731 void GraphicsEngine::
1732 make_contexts(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1733  Windows::const_iterator wi;
1734  for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
1735  GraphicsOutput *win = (*wi);
1736  if (win->begin_frame(GraphicsOutput::FM_refresh, current_thread)) {
1737  win->end_frame(GraphicsOutput::FM_refresh, current_thread);
1738  }
1739  }
1740 }
1741 
1742 /**
1743  * This is called by the RenderThread object to process all the windows events
1744  * (resize, etc.) for the given list of windows. This is run in the window
1745  * thread.
1746  */
1747 void GraphicsEngine::
1748 process_events(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1749  // We're not using a vector iterator here, since it's possible that the
1750  // window list changes in an event, which would invalidate the iterator and
1751  // cause a crash.
1752  for (size_t i = 0; i < wlist.size(); ++i) {
1753  wlist[i]->process_events();
1754  }
1755 }
1756 
1757 /**
1758  * This is called by the RenderThread object to flip the buffers for all of
1759  * the non-single-buffered windows in the given list. This is run in the draw
1760  * thread.
1761  */
1762 void GraphicsEngine::
1763 flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1764  size_t num_windows = wlist.size();
1765  size_t warray_size = num_windows * sizeof(GraphicsOutput *);
1766  size_t warray_count = 0;
1767  GraphicsOutput **warray = (GraphicsOutput **)alloca(warray_size);
1768 
1769  size_t i;
1770  for (i = 0; i < num_windows; ++i) {
1771  GraphicsOutput *win = wlist[i];
1772  if (win->flip_ready()) {
1773  nassertv(warray_count < num_windows);
1774  warray[warray_count] = win;
1775  ++warray_count;
1776 
1777  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1778  win->begin_flip();
1779  }
1780  }
1781 
1782  for (i = 0; i < warray_count; ++i) {
1783  GraphicsOutput *win = warray[i];
1784  PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
1785  win->end_flip();
1786  }
1787 }
1788 
1789 /**
1790  * This is called by the RenderThread object to flip the buffers for all of
1791  * the non-single-buffered windows in the given list. This is run in the draw
1792  * thread.
1793  */
1794 void GraphicsEngine::
1795 ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
1796  Windows::const_iterator wi;
1797  for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
1798  GraphicsOutput *win = (*wi);
1799  if (win->flip_ready()) {
1800  PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
1801  win->ready_flip();
1802  }
1803  }
1804 }
1805 
1806 /**
1807  * The implementation of sync_frame(). We assume _lock is already held before
1808  * this method is called.
1809  */
1810 void GraphicsEngine::
1811 do_sync_frame(Thread *current_thread) {
1812  nassertv(_lock.debug_is_locked());
1813 
1814  // Statistics
1815  PStatTimer timer(_sync_pcollector, current_thread);
1816 
1817  nassertv(_flip_state == FS_draw);
1818 
1819  // Wait for all the threads to finish their current frame. Grabbing and
1820  // releasing the mutex should achieve that.
1821  Threads::const_iterator ti;
1822  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1823  RenderThread *thread = (*ti).second;
1824  thread->_cv_mutex.acquire();
1825  thread->_cv_mutex.release();
1826  }
1827 
1828  _flip_state = FS_sync;
1829 }
1830 
1831 /**
1832  * Wait until all draw calls have finished drawing and the frame is ready to
1833  * flip
1834  */
1835 void GraphicsEngine::
1836 do_ready_flip(Thread *current_thread) {
1837  nassertv(_lock.debug_is_locked());
1838 
1839  // Statistics
1840  PStatTimer timer(_sync_pcollector, current_thread);
1841 
1842  nassertv(_flip_state == FS_draw);
1843 
1844  // Wait for all the threads to finish their current frame. Grabbing and
1845  // releasing the mutex should achieve that.
1846  Threads::const_iterator ti;
1847  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1848  RenderThread *thread = (*ti).second;
1849  thread->_cv_mutex.acquire();
1850  thread->_cv_mutex.release();
1851  }
1852  _app.do_ready_flip(this,current_thread);
1853  _flip_state = FS_sync;
1854 }
1855 
1856 /**
1857  * The implementation of flip_frame(). We assume _lock is already held before
1858  * this method is called.
1859  */
1860 void GraphicsEngine::
1861 do_flip_frame(Thread *current_thread) {
1862  nassertv(_lock.debug_is_locked());
1863 
1864  // Statistics
1865  PStatTimer timer(_flip_pcollector, current_thread);
1866 
1867  nassertv(_flip_state == FS_draw || _flip_state == FS_sync);
1868 
1869  // First, wait for all the threads to finish their current frame, if
1870  // necessary. Grabbing the mutex (and waiting for TS_wait) should achieve
1871  // that.
1872  {
1873  PStatTimer timer(_wait_pcollector, current_thread);
1874  Threads::const_iterator ti;
1875  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1876  RenderThread *thread = (*ti).second;
1877  thread->_cv_mutex.acquire();
1878 
1879  while (thread->_thread_state != TS_wait) {
1880  thread->_cv_done.wait();
1881  }
1882  }
1883  }
1884 
1885  // Now signal all of our threads to flip the windows.
1886  _app.do_flip(this, current_thread);
1887 
1888  {
1889  Threads::const_iterator ti;
1890  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
1891  RenderThread *thread = (*ti).second;
1892  nassertv(thread->_thread_state == TS_wait);
1893  thread->_thread_state = TS_do_flip;
1894  thread->_cv_start.notify();
1895  thread->_cv_mutex.release();
1896  }
1897  }
1898 
1899  _flip_state = FS_flip;
1900 }
1901 
1902 /**
1903  * Returns a new SceneSetup object appropriate for rendering the scene from
1904  * the indicated camera, or NULL if the scene should not be rendered for some
1905  * reason.
1906  */
1907 PT(SceneSetup) GraphicsEngine::
1908 setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
1909  Thread *current_thread = dr->get_current_thread();
1910  PStatTimer timer(_cull_setup_pcollector, current_thread);
1911 
1912  GraphicsOutput *window = dr->get_window();
1913  // The window pointer shouldn't be NULL, since we presumably got to this
1914  // particular DisplayRegion by walking through a list on a window.
1915  nassertr(window != nullptr, nullptr);
1916 
1917  NodePath camera = dr->get_camera();
1918  if (camera.is_empty()) {
1919  // No camera, no draw.
1920  return nullptr;
1921  }
1922 
1923  Camera *camera_node;
1924  DCAST_INTO_R(camera_node, camera.node(), nullptr);
1925 
1926  if (!camera_node->is_active()) {
1927  // Camera inactive, no draw.
1928  return nullptr;
1929  }
1930  camera_node->cleanup_aux_scene_data(current_thread);
1931 
1932  int lens_index = dr->get_lens_index();
1933  Lens *lens = camera_node->get_lens(lens_index);
1934  if (lens == nullptr || !camera_node->get_lens_active(lens_index)) {
1935  // No lens, no draw.
1936  return nullptr;
1937  }
1938 
1939  NodePath scene_root = camera_node->get_scene();
1940  if (scene_root.is_empty()) {
1941  // If there's no explicit scene specified, use whatever scene the camera
1942  // is parented within. This is the normal and preferred case; the use of
1943  // an explicit scene is now deprecated.
1944  scene_root = camera.get_top(current_thread);
1945  }
1946 
1947  PT(SceneSetup) scene_setup = new SceneSetup;
1948 
1949  // We will need both the camera transform (the net transform to the camera
1950  // from the scene) and the world transform (the camera transform inverse, or
1951  // the net transform to the scene from the camera). These are actually
1952  // defined from the parent of the scene_root, because the scene_root's own
1953  // transform is immediately applied to these during rendering. (Normally,
1954  // the parent of the scene_root is the empty NodePath, although it need not
1955  // be.)
1956  NodePath scene_parent = scene_root.get_parent(current_thread);
1957  CPT(TransformState) camera_transform = camera.get_transform(scene_parent, current_thread);
1958  CPT(TransformState) world_transform = scene_parent.get_transform(camera, current_thread);
1959 
1960  if (camera_transform->is_invalid()) {
1961  // There must be a singular transform over the scene.
1962  if (!_singular_warning_last_frame) {
1963  display_cat.warning()
1964  << "Scene " << scene_root << " has net scale ("
1965  << scene_root.get_scale(NodePath()) << "); cannot render.\n";
1966  _singular_warning_this_frame = true;
1967  }
1968  return nullptr;
1969  }
1970 
1971  if (world_transform->is_invalid()) {
1972  // There must be a singular transform over the camera.
1973  if (!_singular_warning_last_frame) {
1974  display_cat.warning()
1975  << "Camera " << camera << " has net scale ("
1976  << camera.get_scale(NodePath()) << "); cannot render.\n";
1977  }
1978  _singular_warning_this_frame = true;
1979  return nullptr;
1980  }
1981 
1982  CPT(RenderState) initial_state = camera_node->get_initial_state();
1983 
1984  if (window->get_inverted()) {
1985  // If the window is to be inverted, we must set the inverted flag on the
1986  // SceneSetup object, so that the GSG will be able to invert the
1987  // projection matrix at the last minute.
1988  scene_setup->set_inverted(true);
1989 
1990  // This also means we need to globally invert the sense of polygon vertex
1991  // ordering.
1992  initial_state = initial_state->compose(get_invert_polygon_state());
1993  }
1994 
1995  scene_setup->set_display_region(dr->get_object());
1996  scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height());
1997  scene_setup->set_scene_root(scene_root);
1998  scene_setup->set_camera_path(camera);
1999  scene_setup->set_camera_node(camera_node);
2000  scene_setup->set_lens(lens);
2001  scene_setup->set_initial_state(initial_state);
2002  scene_setup->set_camera_transform(camera_transform);
2003  scene_setup->set_world_transform(world_transform);
2004 
2005  CPT(TransformState) cs_transform = gsg->get_cs_transform_for(lens->get_coordinate_system());
2006  scene_setup->set_cs_transform(cs_transform);
2007 
2008  CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform);
2009  scene_setup->set_cs_world_transform(cs_world_transform);
2010 
2011  return scene_setup;
2012 }
2013 
2014 /**
2015  * Draws the previously-culled scene.
2016  */
2017 void GraphicsEngine::
2018 do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thread *current_thread) {
2019  // Statistics
2020  PStatGPUTimer timer(gsg, dr->get_draw_region_pcollector(), current_thread);
2021 
2022  gsg->push_group_marker(dr->get_debug_name());
2023 
2024  PT(CullResult) cull_result;
2025  PT(SceneSetup) scene_setup;
2026  {
2027  DisplayRegion::CDCullReader cdata(dr->_cycler_cull, current_thread);
2028  cull_result = cdata->_cull_result;
2029  scene_setup = cdata->_scene_setup;
2030  }
2031 
2032  CallbackObject *cbobj;
2033 
2034  {
2035  DisplayRegionPipelineReader dr_reader(dr, current_thread);
2036  win->change_scenes(&dr_reader);
2037  gsg->prepare_display_region(&dr_reader);
2038  if (dr_reader.is_any_clear_active()) {
2039  PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
2040  gsg->clear(dr_reader.get_object());
2041  }
2042 
2043  cbobj = dr_reader.get_draw_callback();
2044  }
2045 
2046  if (cbobj != nullptr) {
2047  // Issue the draw callback on this DisplayRegion.
2048 
2049  // Set the GSG to the initial state. We disable depth testing since that
2050  // is the default OpenGL state, and some libraries (eg. Kivy) expect that.
2051  static CPT(RenderState) state = RenderState::make(
2052  DepthTestAttrib::make(DepthTestAttrib::M_none));
2053  gsg->clear_before_callback();
2054  gsg->set_state_and_transform(state, TransformState::make_identity());
2055 
2056  DisplayRegionDrawCallbackData cbdata(cull_result, scene_setup);
2057  cbobj->do_callback(&cbdata);
2058 
2059  // We don't trust the state the callback may have left us in.
2061 
2062  } else if (cull_result == nullptr || scene_setup == nullptr) {
2063  // Nothing to see here.
2064 
2065  } else if (dr->is_stereo()) {
2066  // We don't actually draw the stereo DisplayRegions. These are just
2067  // placeholders; we draw the individual left and right eyes instead. (We
2068  // might still clear the stereo DisplayRegions, though, since it's
2069  // probably faster to clear right and left channels in one pass, than to
2070  // clear them in two separate passes.)
2071 
2072  } else if (!gsg->set_scene(scene_setup)) {
2073  // The scene or lens is inappropriate somehow.
2074  display_cat.error()
2075  << gsg->get_type() << " cannot render scene with specified lens.\n";
2076 
2077  } else {
2078  if (gsg->begin_scene()) {
2079  cull_result->draw(current_thread);
2080  gsg->end_scene();
2081  }
2082  }
2083 
2084  gsg->pop_group_marker();
2085 }
2086 
2087 /**
2088  * An internal function called by make_window() and make_buffer() and similar
2089  * functions to add the newly-created GraphicsOutput object to the engine's
2090  * list of windows, and to request that the window be opened.
2091  */
2092 void GraphicsEngine::
2093 do_add_window(GraphicsOutput *window) {
2094  nassertv(window != nullptr);
2095 
2096  MutexHolder holder(_new_windows_lock);
2097  nassertv(window->get_engine() == this);
2098 
2099  // We have a special counter that is unique per window that allows us to
2100  // assure that recently-added windows end up on the end of the list.
2101  window->_internal_sort_index = _window_sort_index;
2102  ++_window_sort_index;
2103 
2104  if (display_cat.is_debug()) {
2105  display_cat.debug()
2106  << "Created " << window->get_type() << " " << (void *)window << "\n";
2107  }
2108 
2109  window->request_open();
2110  _new_windows.push_back(window);
2111 }
2112 
2113 /**
2114  * An internal function called by make_output to add the newly-created gsg
2115  * object to the engine's list of gsg's. It also adjusts various config
2116  * variables based on the gsg's capabilities.
2117  */
2118 void GraphicsEngine::
2119 do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe) {
2120  nassertv(gsg != nullptr);
2121 
2122  ReMutexHolder holder(_lock);
2123  nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this);
2124  gsg->_threading_model = _threading_model;
2125  if (!_default_loader.is_null()) {
2126  gsg->set_loader(_default_loader);
2127  }
2128 
2129  auto_adjust_capabilities(gsg);
2130 
2131  WindowRenderer *draw =
2132  get_window_renderer(_threading_model.get_draw_name(),
2133  _threading_model.get_draw_stage());
2134 
2135  draw->add_gsg(gsg);
2136 }
2137 
2138 /**
2139  * An internal function called by remove_window() and remove_all_windows() to
2140  * actually remove the indicated window from all relevant structures, except
2141  * the _windows list itself.
2142  */
2143 void GraphicsEngine::
2144 do_remove_window(GraphicsOutput *window, Thread *current_thread) {
2145  nassertv(window != nullptr);
2146  PT(GraphicsPipe) pipe = window->get_pipe();
2147  window->clear_pipe();
2148 
2149  if (!_windows_sorted) {
2150  do_resort_windows();
2151  }
2152 
2153  // Now remove the window from all threads that know about it.
2154  _app.remove_window(window);
2155  Threads::const_iterator ti;
2156  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2157  RenderThread *thread = (*ti).second;
2158  thread->remove_window(window);
2159  }
2160 
2161  // If the window happened to be controlled by the app thread, we might as
2162  // well close it now rather than waiting for next frame.
2163  _app.do_pending(this, current_thread);
2164 
2165  if (display_cat.is_debug()) {
2166  display_cat.debug()
2167  << "Removed " << window->get_type() << " " << (void *)window << "\n";
2168  }
2169 }
2170 
2171 /**
2172  * Resorts all of the Windows lists. This may need to be done if one or more
2173  * of the windows' sort properties has changed.
2174  */
2175 void GraphicsEngine::
2176 do_resort_windows() {
2177  _windows_sorted = true;
2178 
2179  _app.resort_windows();
2180  Threads::const_iterator ti;
2181  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2182  RenderThread *thread = (*ti).second;
2183  thread->resort_windows();
2184  }
2185 
2186  _windows.sort();
2187 }
2188 
2189 /**
2190  * Video card capability flags are stored on a per-gsg basis. However, there
2191  * are a few cases where panda needs to know not the capabilities of an
2192  * individual GSG, but rather, the collective capabilities of all the GSGs.
2193  *
2194  * Non-power-of-two (NPOT) texture support is the classic example. Panda
2195  * makes a single global decision to either create NPOT textures, or not.
2196  * Therefore, it doesn't need to know whether one GSG supports NPOT textures.
2197  * It needs to know whether ALL the GSGs support NPOT textures.
2198  *
2199  * The purpose of this routine is to maintain global capability flags that
2200  * summarize the collective capabilities of the computer as a whole.
2201  *
2202  * These global capability flags are initialized from config variables. Then,
2203  * they can be auto-reconfigured using built-in heuristic mechanisms if the
2204  * user so desires. Whether auto-reconfiguration is enabled or not, the
2205  * configured values are checked against the actual capabilities of the
2206  * machine and error messages will be printed if there is a mismatch.
2207  *
2208  */
2209 void GraphicsEngine::
2210 auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
2211 
2212 /*
2213  * The rule we use when auto-reconfiguring is as follows. The global
2214  * capabilities must initially be set to conservative values. When the first
2215  * GSG comes into existence, its capabilities will be checked, and the global
2216  * capabilities may be elevated to more aggressive values. At first glance,
2217  * this might seem backward, and it might seem better to do it the other way:
2218  * start with all global capabilities aggressively set, and then disable
2219  * capabilities when you discover a gsg that doesn't support them. However,
2220  * that approach doesn't work, because once a global capability is enabled,
2221  * there is no going back. If textures_power_2 has ever been set to 'none',
2222  * there may be NPOT textures already floating about the system. Ie, it's too
2223  * late: you can't turn these global capability flags off, once they've been
2224  * turned on. That's why we have to start with conservative settings, and
2225  * then elevate those settings to more aggressive values later when we're
2226  * fairly sure it's OK to do so. For each global capability, we must: 1. Make
2227  * sure the initial setting is conservative. 2. Possibly elevate to a more
2228  * aggressive value. 3. Check that we haven't over-elevated.
2229  */
2230 
2231  if (textures_auto_power_2 && (textures_power_2 == ATS_none)) {
2232  display_cat.error()
2233  << "Invalid panda config file: if you set the config-variable\n"
2234  << "textures_auto_power_2 to true, you must set the config-variable"
2235  << "textures_power_2 to 'up' or 'down'.\n";
2236  textures_power_2 = ATS_down; // Not a fix. Just suppresses further error messages.
2237  }
2238 
2239  if (textures_auto_power_2 && !Texture::has_textures_power_2()) {
2240  if (gsg->get_supports_tex_non_pow2()) {
2242  } else {
2243  Texture::set_textures_power_2(textures_power_2);
2244  }
2245  }
2246 
2247  if ((Texture::get_textures_power_2() == ATS_none) &&
2248  (!gsg->get_supports_tex_non_pow2())) {
2249 
2250  // Overaggressive configuration detected
2251 
2252  display_cat.error()
2253  << "The 'textures_power_2' configuration is set to 'none', meaning \n"
2254  << "that non-power-of-two texture support is required, but the video \n"
2255  << "driver I'm trying to use does not support non-power-of-two textures.\n";
2256 
2257  if (textures_power_2 != ATS_none) {
2258  display_cat.error()
2259  << "The 'none' did not come from the config file. In other words,\n"
2260  << "the variable 'textures_power_2' was altered procedurally.\n";
2261 
2262  if (textures_auto_power_2) {
2263  display_cat.error()
2264  << "It is possible that it was set by panda's automatic mechanisms,\n"
2265  << "which are currently enabled, because 'textures_auto_power_2' is\n"
2266  << "true. Panda's automatic mechanisms assume that if one\n"
2267  << "window supports non-power-of-two textures, then they all will.\n"
2268  << "This assumption works for most games, but not all.\n"
2269  << "In particular, it can fail if the game creates multiple windows\n"
2270  << "on multiple displays with different video cards.\n";
2271  }
2272  }
2273  }
2274 }
2275 
2276 /**
2277  * Signals our child threads to terminate and waits for them to clean up.
2278  */
2279 void GraphicsEngine::
2280 terminate_threads(Thread *current_thread) {
2281  ReMutexHolder holder(_lock, current_thread);
2282 
2283  // We spend almost our entire time in this method just waiting for threads.
2284  // Time it appropriately.
2285  PStatTimer timer(_wait_pcollector, current_thread);
2286 
2287  // First, wait for all the threads to finish their current frame. Grabbing
2288  // the mutex should achieve that.
2289  Threads::const_iterator ti;
2290  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2291  RenderThread *thread = (*ti).second;
2292  thread->_cv_mutex.acquire();
2293  }
2294 
2295  // Now tell them to close their windows and terminate.
2296  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2297  RenderThread *thread = (*ti).second;
2298  thread->_thread_state = TS_terminate;
2299  thread->_cv_start.notify();
2300  thread->_cv_mutex.release();
2301  }
2302 
2303  // Finally, wait for them all to finish cleaning up.
2304  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
2305  RenderThread *thread = (*ti).second;
2306  thread->join();
2307  }
2308 
2309  _threads.clear();
2310 }
2311 
2312 
2313 #ifdef DO_PSTATS
2314 /**
2315  * A callback function for Pipeline::iterate_all_cycler_types() to report the
2316  * cycler types to PStats.
2317  */
2318 void GraphicsEngine::
2319 pstats_count_cycler_type(TypeHandle type, int count, void *data) {
2320  GraphicsEngine *self = (GraphicsEngine *)data;
2321  CyclerTypeCounters::iterator ci = self->_all_cycler_types.find(type);
2322  if (ci == self->_all_cycler_types.end()) {
2323  PStatCollector collector(_cyclers_pcollector, type.get_name());
2324  ci = self->_all_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first;
2325  }
2326  (*ci).second.set_level(count);
2327 }
2328 #endif // DO_PSTATS
2329 
2330 #ifdef DO_PSTATS
2331 /**
2332  * A callback function for Pipeline::iterate_dirty_cycler_types() to report
2333  * the cycler types to PStats.
2334  */
2335 void GraphicsEngine::
2336 pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data) {
2337  GraphicsEngine *self = (GraphicsEngine *)data;
2338  CyclerTypeCounters::iterator ci = self->_dirty_cycler_types.find(type);
2339  if (ci == self->_dirty_cycler_types.end()) {
2340  PStatCollector collector(_dirty_cyclers_pcollector, type.get_name());
2341  ci = self->_dirty_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first;
2342  }
2343  (*ci).second.set_level(count);
2344 }
2345 #endif // DO_PSTATS
2346 
2347 /**
2348  * Returns a RenderState for inverting the sense of polygon vertex ordering:
2349  * if the scene graph specifies a clockwise ordering, this changes it to
2350  * counterclockwise, and vice-versa.
2351  */
2352 const RenderState *GraphicsEngine::
2353 get_invert_polygon_state() {
2354  // Once someone asks for this pointer, we hold its reference count and never
2355  // free it.
2356  static CPT(RenderState) state = nullptr;
2357  if (state == nullptr) {
2358  state = RenderState::make(CullFaceAttrib::make_reverse());
2359  }
2360 
2361  return state;
2362 }
2363 
2364 /**
2365  * Returns the WindowRenderer with the given name. Creates a new RenderThread
2366  * if there is no such thread already. The pipeline_stage number specifies
2367  * the pipeline stage that will be assigned to the thread (unless was
2368  * previously given a higher stage).
2369  *
2370  * You must already be holding the lock before calling this method.
2371  */
2372 GraphicsEngine::WindowRenderer *GraphicsEngine::
2373 get_window_renderer(const string &name, int pipeline_stage) {
2374  nassertr(_lock.debug_is_locked(), nullptr);
2375 
2376  if (name.empty()) {
2377  return &_app;
2378  }
2379 
2380  Threads::iterator ti = _threads.find(name);
2381  if (ti != _threads.end()) {
2382  return (*ti).second.p();
2383  }
2384 
2385  PT(RenderThread) thread = new RenderThread(name, this);
2386  thread->set_min_pipeline_stage(pipeline_stage);
2387  _pipeline->set_min_stages(pipeline_stage + 1);
2388 
2389  bool started = thread->start(TP_normal, true);
2390  nassertr(started, thread.p());
2391  _threads[name] = thread;
2392 
2393  nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());
2394 
2395  return thread.p();
2396 }
2397 
2398 /**
2399  *
2400  */
2401 GraphicsEngine::WindowRenderer::
2402 WindowRenderer(const string &name) :
2403  _wl_lock(string("GraphicsEngine::WindowRenderer::_wl_lock ") + name)
2404 {
2405 }
2406 
2407 /**
2408  * Adds a new GSG to the _gsg list, if it is not already there.
2409  */
2410 void GraphicsEngine::WindowRenderer::
2411 add_gsg(GraphicsStateGuardian *gsg) {
2412  LightReMutexHolder holder(_wl_lock);
2413  _gsgs.insert(gsg);
2414 }
2415 
2416 /**
2417  * Adds a new window to the indicated list, which should be a member of the
2418  * WindowRenderer.
2419  */
2420 void GraphicsEngine::WindowRenderer::
2421 add_window(Windows &wlist, GraphicsOutput *window) {
2422  LightReMutexHolder holder(_wl_lock);
2423  wlist.insert(window);
2424 }
2425 
2426 /**
2427  * Immediately removes the indicated window from all lists. If the window is
2428  * currently open and is already on the _window list, moves it to the
2429  * _pending_close list for later closure.
2430  */
2431 void GraphicsEngine::WindowRenderer::
2432 remove_window(GraphicsOutput *window) {
2433  nassertv(window != nullptr);
2434  LightReMutexHolder holder(_wl_lock);
2435  PT(GraphicsOutput) ptwin = window;
2436 
2437  _cull.erase(ptwin);
2438  _cdraw.erase(ptwin);
2439  _draw.erase(ptwin);
2440 
2441  Windows::iterator wi;
2442 
2443  wi = _window.find(ptwin);
2444  if (wi != _window.end()) {
2445  // The window is on our _window list, meaning its openclose operations
2446  // (among other window ops) are serviced by this thread.
2447 
2448  // Make sure the window isn't about to request itself open.
2449  ptwin->request_close();
2450 
2451  // If the window is already open, move it to the _pending_close list so it
2452  // can be closed later. We can't close it immediately, because we might
2453  // not have been called from the subthread.
2454  if (ptwin->is_valid()) {
2455  _pending_close.push_back(ptwin);
2456  }
2457 
2458  _window.erase(wi);
2459  }
2460 }
2461 
2462 /**
2463  * Resorts all the lists of windows, assuming they may have become unsorted.
2464  */
2465 void GraphicsEngine::WindowRenderer::
2466 resort_windows() {
2467  LightReMutexHolder holder(_wl_lock);
2468 
2469  _cull.sort();
2470  _cdraw.sort();
2471  _draw.sort();
2472  _window.sort();
2473 
2474  if (display_cat.is_debug()) {
2475  display_cat.debug()
2476  << "Windows resorted:";
2477  Windows::const_iterator wi;
2478  for (wi = _window.begin(); wi != _window.end(); ++wi) {
2479  GraphicsOutput *win = (*wi);
2480  display_cat.debug(false)
2481  << " " << win->get_name() << "(" << win->get_sort() << ")";
2482  }
2483  display_cat.debug(false)
2484  << "\n";
2485 
2486  for (wi = _draw.begin(); wi != _draw.end(); ++wi) {
2487  GraphicsOutput *win = (*wi);
2488  display_cat.debug(false)
2489  << " " << win->get_name() << "(" << win->get_sort() << ")";
2490  }
2491  display_cat.debug(false)
2492  << "\n";
2493  }
2494 }
2495 
2496 /**
2497  * Executes one stage of the pipeline for the current thread: calls cull on
2498  * all windows that are on the cull list for this thread, draw on all the
2499  * windows on the draw list, etc.
2500  */
2501 void GraphicsEngine::WindowRenderer::
2502 do_frame(GraphicsEngine *engine, Thread *current_thread) {
2503  PStatTimer timer(engine->_do_frame_pcollector, current_thread);
2504  LightReMutexHolder holder(_wl_lock);
2505 
2506  engine->cull_to_bins(_cull, current_thread);
2507  engine->cull_and_draw_together(_cdraw, current_thread);
2508  engine->draw_bins(_draw, current_thread);
2509  engine->process_events(_window, current_thread);
2510 
2511  // If any GSG's on the list have no more outstanding pointers, clean them
2512  // up. (We are in the draw thread for all of these GSG's.)
2513  if (any_done_gsgs()) {
2514  GSGs new_gsgs;
2515  GSGs::iterator gi;
2516  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2517  GraphicsStateGuardian *gsg = (*gi);
2518  if (gsg->get_ref_count() == 1) {
2519  // This one has no outstanding pointers; clean it up.
2520  GraphicsPipe *pipe = gsg->get_pipe();
2521  engine->close_gsg(pipe, gsg);
2522  } else {
2523  // This one is ok; preserve it.
2524  new_gsgs.insert(gsg);
2525  }
2526  }
2527 
2528  _gsgs.swap(new_gsgs);
2529  }
2530 }
2531 
2532 /**
2533  * Attempts to fully open or close any windows or buffers associated with this
2534  * thread, but does not otherwise perform any rendering. (Normally, this step
2535  * is handled during do_frame(); call this method only if you want these
2536  * things to open immediately.)
2537  */
2538 void GraphicsEngine::WindowRenderer::
2539 do_windows(GraphicsEngine *engine, Thread *current_thread) {
2540  LightReMutexHolder holder(_wl_lock);
2541 
2542  engine->process_events(_window, current_thread);
2543 
2544  engine->make_contexts(_cdraw, current_thread);
2545  engine->make_contexts(_draw, current_thread);
2546 }
2547 
2548 /**
2549  * Flips the windows as appropriate for the current thread.
2550  */
2551 void GraphicsEngine::WindowRenderer::
2552 do_flip(GraphicsEngine *engine, Thread *current_thread) {
2553  LightReMutexHolder holder(_wl_lock);
2554  engine->flip_windows(_cdraw, current_thread);
2555  engine->flip_windows(_draw, current_thread);
2556 }
2557 
2558 /**
2559  * Prepares windows for flipping by waiting until all draw calls are finished
2560  */
2561 void GraphicsEngine::WindowRenderer::
2562 do_ready_flip(GraphicsEngine *engine, Thread *current_thread) {
2563  LightReMutexHolder holder(_wl_lock);
2564  engine->ready_flip_windows(_cdraw, current_thread);
2565  engine->ready_flip_windows(_draw, current_thread);
2566 }
2567 
2568 
2569 /**
2570  * Closes all the windows on the _window list.
2571  */
2572 void GraphicsEngine::WindowRenderer::
2573 do_close(GraphicsEngine *engine, Thread *current_thread) {
2574  LightReMutexHolder holder(_wl_lock);
2575  Windows::iterator wi;
2576  for (wi = _window.begin(); wi != _window.end(); ++wi) {
2577  GraphicsOutput *win = (*wi);
2578  win->set_close_now();
2579  }
2580 
2581  // Also close all of the GSG's.
2582  GSGs new_gsgs;
2583  GSGs::iterator gi;
2584  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2585  GraphicsStateGuardian *gsg = (*gi);
2586  if (gsg->get_ref_count() == 1) {
2587  // This one has no outstanding pointers; clean it up.
2588  GraphicsPipe *pipe = gsg->get_pipe();
2589  engine->close_gsg(pipe, gsg);
2590  } else {
2591  // This one is ok; preserve it.
2592  new_gsgs.insert(gsg);
2593  }
2594  }
2595 
2596  _gsgs.swap(new_gsgs);
2597 }
2598 
2599 /**
2600  * Actually closes any windows that were recently removed from the
2601  * WindowRenderer.
2602  */
2603 void GraphicsEngine::WindowRenderer::
2604 do_pending(GraphicsEngine *engine, Thread *current_thread) {
2605  LightReMutexHolder holder(_wl_lock);
2606 
2607  if (!_pending_close.empty()) {
2608  if (display_cat.is_debug()) {
2609  display_cat.debug()
2610  << "_pending_close.size() = " << _pending_close.size() << "\n";
2611  }
2612 
2613  // Close any windows that were pending closure. Carefully protect against
2614  // recursive entry to this function by swapping the vector to a local copy
2615  // first.
2616  Windows::iterator wi;
2617  Windows pending_close;
2618  _pending_close.swap(pending_close);
2619  for (wi = pending_close.begin(); wi != pending_close.end(); ++wi) {
2620  GraphicsOutput *win = (*wi);
2621  win->set_close_now();
2622  }
2623  }
2624 }
2625 
2626 /**
2627  * Returns true if any of the GSG's on this thread's draw list are done (they
2628  * have no outstanding pointers other than this one), or false if all of them
2629  * are still good.
2630  */
2631 bool GraphicsEngine::WindowRenderer::
2632 any_done_gsgs() const {
2633  GSGs::const_iterator gi;
2634  for (gi = _gsgs.begin(); gi != _gsgs.end(); ++gi) {
2635  if ((*gi)->get_ref_count() == 1) {
2636  return true;
2637  }
2638  }
2639 
2640  return false;
2641 }
2642 
2643 /**
2644  *
2645  */
2646 GraphicsEngine::RenderThread::
2647 RenderThread(const string &name, GraphicsEngine *engine) :
2648  Thread(name, "Main"),
2649  WindowRenderer(name),
2650  _engine(engine),
2651  _cv_mutex(string("GraphicsEngine::RenderThread ") + name),
2652  _cv_start(_cv_mutex),
2653  _cv_done(_cv_mutex)
2654 {
2655  _thread_state = TS_wait;
2656 }
2657 
2658 /**
2659  * The main loop for a particular render thread. The thread will process
2660  * whatever cull or draw windows it has assigned to it.
2661  */
2662 void GraphicsEngine::RenderThread::
2663 thread_main() {
2664  Thread *current_thread = Thread::get_current_thread();
2665 
2666  MutexHolder holder(_cv_mutex);
2667  while (true) {
2668  nassertv(_cv_mutex.debug_is_locked());
2669 
2670  switch (_thread_state) {
2671  case TS_wait:
2672  break;
2673 
2674  case TS_do_frame:
2675  do_pending(_engine, current_thread);
2676  do_frame(_engine, current_thread);
2677  break;
2678 
2679  case TS_do_flip:
2680  do_flip(_engine, current_thread);
2681  break;
2682 
2683  case TS_do_release:
2684  do_pending(_engine, current_thread);
2685  break;
2686 
2687  case TS_do_windows:
2688  do_windows(_engine, current_thread);
2689  do_pending(_engine, current_thread);
2690  break;
2691 
2692  case TS_do_compute:
2693  nassertd(_gsg != nullptr && _state != nullptr) break;
2694  {
2695  const ShaderAttrib *sattr;
2696  _state->get_attrib(sattr);
2697  _gsg->push_group_marker(std::string("Compute ") + sattr->get_shader()->get_filename(Shader::ST_compute).get_basename());
2698  _gsg->set_state_and_transform(_state, TransformState::make_identity());
2699  _gsg->dispatch_compute(_work_groups[0], _work_groups[1], _work_groups[2]);
2700  _gsg->pop_group_marker();
2701  }
2702  break;
2703 
2704  case TS_do_extract:
2705  nassertd(_gsg != nullptr && _texture != nullptr) break;
2706  _result = _gsg->extract_texture_data(_texture);
2707  break;
2708 
2709  case TS_do_screenshot:
2710  nassertd(_region != nullptr) break;
2711  _texture = _region->get_screenshot();
2712  break;
2713 
2714  case TS_terminate:
2715  do_pending(_engine, current_thread);
2716  do_close(_engine, current_thread);
2717  _thread_state = TS_done;
2718  _cv_done.notify();
2719  return;
2720 
2721  case TS_done:
2722  // Shouldn't be possible to get here.
2723  nassertv(false);
2724  return;
2725  }
2726 
2727  _thread_state = TS_wait;
2728  _cv_done.notify();
2729 
2730  {
2731  PStatTimer timer(_wait_pcollector, current_thread);
2732  _cv_start.wait();
2733  }
2734  }
2735 }
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:1852
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'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,...
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'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's coordinate system to the GSG'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,...
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'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,...
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,...
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'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....
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
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
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:1863
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'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'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,...
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'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'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'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,...
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'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:1877
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.