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