Panda3D
graphicsOutput.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 graphicsOutput.cxx
10  * @author drose
11  * @date 2004-02-06
12  */
13 
14 #include "graphicsOutput.h"
15 #include "graphicsPipe.h"
16 #include "graphicsEngine.h"
17 #include "graphicsWindow.h"
18 #include "config_display.h"
19 #include "lightMutexHolder.h"
20 #include "renderBuffer.h"
21 #include "indirectLess.h"
22 #include "pStatTimer.h"
23 #include "configVariableBool.h"
24 #include "camera.h"
25 #include "displayRegion.h"
26 #include "lens.h"
27 #include "perspectiveLens.h"
28 #include "pointerTo.h"
29 #include "compassEffect.h"
30 #include "geom.h"
31 #include "geomNode.h"
32 #include "geomTristrips.h"
33 #include "geomVertexWriter.h"
34 #include "throw_event.h"
35 #include "config_gobj.h"
36 
37 using std::string;
38 
39 TypeHandle GraphicsOutput::_type_handle;
40 
41 PStatCollector GraphicsOutput::_make_current_pcollector("Draw:Make current");
42 PStatCollector GraphicsOutput::_copy_texture_pcollector("Draw:Copy texture");
43 PStatCollector GraphicsOutput::_cull_pcollector("Cull");
44 PStatCollector GraphicsOutput::_draw_pcollector("Draw");
45 
46 struct CubeFaceDef {
47  CubeFaceDef(const char *name, const LPoint3 &look_at, const LVector3 &up) :
48  _name(name), _look_at(look_at), _up(up) { }
49 
50  const char *_name;
51  LPoint3 _look_at;
52  LVector3 _up;
53 };
54 
55 static CubeFaceDef cube_faces[6] = {
56  CubeFaceDef("positive_x", LPoint3(1, 0, 0), LVector3(0, -1, 0)),
57  CubeFaceDef("negative_x", LPoint3(-1, 0, 0), LVector3(0, -1, 0)),
58  CubeFaceDef("positive_y", LPoint3(0, 1, 0), LVector3(0, 0, 1)),
59  CubeFaceDef("negative_y", LPoint3(0, -1, 0), LVector3(0, 0, -1)),
60  CubeFaceDef("positive_z", LPoint3(0, 0, 1), LVector3(0, -1, 0)),
61  CubeFaceDef("negative_z", LPoint3(0, 0, -1), LVector3(0, -1, 0))
62 };
63 
64 /**
65  * Normally, the GraphicsOutput constructor is not called directly; these are
66  * created instead via the GraphicsEngine::make_output() function.
67  */
68 GraphicsOutput::
69 GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
70  const string &name,
71  const FrameBufferProperties &fb_prop,
72  const WindowProperties &win_prop,
73  int flags,
75  GraphicsOutput *host,
76  bool default_stereo_flags) :
77  _lock("GraphicsOutput"),
78  _cull_window_pcollector(_cull_pcollector, name),
79  _draw_window_pcollector(_draw_pcollector, name),
80  _clear_window_pcollector(_draw_window_pcollector, "Clear"),
81  _size(0, 0)
82 {
83 #ifdef DO_MEMORY_USAGE
84  MemoryUsage::update_type(this, this);
85 #endif
86  _engine = engine;
87  _pipe = pipe;
88  _gsg = gsg;
89  _host = host;
90  _fb_properties = fb_prop;
91  _name = name;
92  _creation_flags = flags;
93  _has_size = win_prop.has_size();
94  _is_nonzero_size = false;
95  if (_has_size) {
96  _size = win_prop.get_size();
97  _is_nonzero_size = (_size[0] > 0 && _size[1] > 0);
98  }
99  if (_creation_flags & GraphicsPipe::BF_size_track_host) {
100  // If we're tracking the host size, we assume we'll be nonzero eventually.
101  _is_nonzero_size = true;
102  }
103 
104  _is_valid = false;
105  _flip_ready = false;
106  _target_tex_page = -1;
107  _prev_page_dr = nullptr;
108  _sort = 0;
109  _child_sort = 0;
110  _got_child_sort = false;
111  _internal_sort_index = 0;
112  _inverted = window_inverted;
113  _swap_eyes = swap_eyes;
114  _red_blue_stereo = false;
115  _left_eye_color_mask = 0x0f;
116  _right_eye_color_mask = 0x0f;
117  _side_by_side_stereo = false;
118  _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
119  _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
120  _delete_flag = false;
121 
122  if (_fb_properties.is_single_buffered()) {
123  _draw_buffer_type = RenderBuffer::T_front;
124  } else {
125  _draw_buffer_type = RenderBuffer::T_back;
126  }
127 
128  if (default_stereo_flags) {
129  // Check the config variables to see if we should make this a "stereo"
130  // buffer or window.
131  _red_blue_stereo = red_blue_stereo && !fb_prop.is_stereo();
132  if (_red_blue_stereo) {
133  _left_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(0));
134  _right_eye_color_mask = parse_color_mask(red_blue_stereo_colors.get_word(1));
135  }
136  _side_by_side_stereo = side_by_side_stereo && !fb_prop.is_stereo();
137  if (_side_by_side_stereo) {
138  _sbs_left_dimensions.set(sbs_left_dimensions[0], sbs_left_dimensions[1],
139  sbs_left_dimensions[2], sbs_left_dimensions[3]);
140  _sbs_right_dimensions.set(sbs_right_dimensions[0], sbs_right_dimensions[1],
141  sbs_right_dimensions[2], sbs_right_dimensions[3]);
142  }
143  }
144 
145  // We start out with one DisplayRegion that covers the whole window, which
146  // we may use internally for full-window operations like clear() and
147  // get_screenshot().
148  _overlay_display_region = make_mono_display_region(0.0f, 1.0f, 0.0f, 1.0f);
149  _overlay_display_region->set_active(false);
150  _overlay_display_region->set_scissor_enabled(false);
151 
152  // Make sure the "active" flag is set true for pipeline stage 0.
153  {
154  CDWriter cdata(_cycler, true);
155  cdata->_active = true;
156  }
157 
158  // By default, each new GraphicsOutput is set up to clear color and depth.
159  set_clear_color_active(true);
160  set_clear_depth_active(true);
161  set_clear_stencil_active(true);
162  set_clear_color(background_color.get_value());
163 }
164 
165 /**
166  *
167  */
168 GraphicsOutput::
169 ~GraphicsOutput() {
170  // The window should be closed by the time we destruct.
171  nassertv(!is_valid());
172 
173  // We shouldn't have a GraphicsPipe pointer anymore.
174  nassertv(_pipe == nullptr);
175 
176  // We don't have to destruct our child display regions explicitly, since
177  // they are all reference-counted and will go away when their pointers do.
178  // However, we do need to zero out their pointers to us.
179  TotalDisplayRegions::iterator dri;
180  for (dri = _total_display_regions.begin();
181  dri != _total_display_regions.end();
182  ++dri) {
183  (*dri)->_window = nullptr;
184  }
185 
186  _total_display_regions.clear();
187  _overlay_display_region = nullptr;
188 }
189 
190 /**
191  * If the GraphicsOutput is currently rendering to a texture, then all
192  * textures are dissociated from the GraphicsOuput.
193  */
196  CDWriter cdata(_cycler, true);
197  cdata->_textures.clear();
198  ++(cdata->_textures_seq);
199  throw_event("render-texture-targets-changed");
200 }
201 
202 /**
203  * Creates a new Texture object, suitable for rendering the contents of this
204  * buffer into, and appends it to the list of render textures.
205  *
206  * If tex is not NULL, it is the texture that will be set up for rendering
207  * into; otherwise, a new Texture object will be created, in which case you
208  * may call get_texture() to retrieve the new texture pointer.
209  *
210  * You can specify a bitplane to attach the texture to. the legal choices
211  * are:
212  *
213  * - RTP_depth
214  * - RTP_depth_stencil
215  * - RTP_color
216  * - RTP_aux_rgba_0
217  * - RTP_aux_rgba_1
218  * - RTP_aux_rgba_2
219  * - RTP_aux_rgba_3
220  *
221  * If you do not specify a bitplane to attach the texture to, this routine
222  * will use a default based on the texture's format:
223  *
224  * - F_depth_component attaches to RTP_depth
225  * - F_depth_stencil attaches to RTP_depth_stencil
226  * - all other formats attach to RTP_color.
227  *
228  * The texture's format will be changed to match the format of the bitplane to
229  * which it is attached. For example, if you pass in an F_rgba texture and
230  * order that it be attached to RTP_depth_stencil, it will turn into an
231  * F_depth_stencil texture.
232  *
233  * Also see make_texture_buffer(), which is a higher-level interface for
234  * preparing render-to-a-texture mode.
235  */
237 add_render_texture(Texture *tex, RenderTextureMode mode,
238  RenderTexturePlane plane) {
239 
240  if (mode == RTM_none) {
241  return;
242  }
243  LightMutexHolder holder(_lock);
244 
245  // Create texture if necessary.
246  if (tex == nullptr) {
247  tex = new Texture(get_name());
248  tex->set_wrap_u(SamplerState::WM_clamp);
249  tex->set_wrap_v(SamplerState::WM_clamp);
250  } else {
251  tex->clear_ram_image();
252  }
253 
254  // Set it to have no compression by default. You can restore compression
255  // later if you really, really want it; but this freaks out some drivers,
256  // and presumably it's a mistake if you have compression enabled for a
257  // rendered texture.
258  tex->set_compression(Texture::CM_off);
259 
260  // Choose a default bitplane.
261  if (plane == RTP_COUNT) {
262  switch (tex->get_format()) {
263  case Texture::F_depth_stencil:
264  plane = RTP_depth_stencil;
265  break;
266 
267  case Texture::F_depth_component:
268  case Texture::F_depth_component16:
269  case Texture::F_depth_component24:
270  case Texture::F_depth_component32:
271  plane = RTP_depth;
272  break;
273 
274  default:
275  plane = RTP_color;
276  break;
277  }
278  }
279 
280  // Set the texture's format to match the bitplane. (And validate the
281  // bitplane, while we're at it).
282 
283  if (plane == RTP_depth) {
284  _fb_properties.setup_depth_texture(tex);
285  tex->set_match_framebuffer_format(true);
286 
287  } else if (plane == RTP_depth_stencil) {
288  tex->set_format(Texture::F_depth_stencil);
289  if (_fb_properties.get_float_depth()) {
290  tex->set_component_type(Texture::T_float);
291  } else {
292  tex->set_component_type(Texture::T_unsigned_int_24_8);
293  }
294  tex->set_match_framebuffer_format(true);
295 
296  } else if (plane == RTP_color ||
297  plane == RTP_aux_rgba_0 ||
298  plane == RTP_aux_rgba_1 ||
299  plane == RTP_aux_rgba_2 ||
300  plane == RTP_aux_rgba_3) {
301  _fb_properties.setup_color_texture(tex);
302  tex->set_match_framebuffer_format(true);
303 
304  } else if (plane == RTP_aux_hrgba_0 ||
305  plane == RTP_aux_hrgba_1 ||
306  plane == RTP_aux_hrgba_2 ||
307  plane == RTP_aux_hrgba_3) {
308  tex->set_format(Texture::F_rgba16);
309  tex->set_match_framebuffer_format(true);
310 
311  } else if (plane == RTP_aux_float_0 ||
312  plane == RTP_aux_float_1 ||
313  plane == RTP_aux_float_2 ||
314  plane == RTP_aux_float_3) {
315  tex->set_format(Texture::F_rgba32);
316  tex->set_component_type(Texture::T_float);
317  tex->set_match_framebuffer_format(true);
318 
319  } else {
320  display_cat.error() <<
321  "add_render_texture: invalid bitplane specified.\n";
322  return;
323  }
324 
325  // Go ahead and tell the texture our anticipated size, even if it might be
326  // inaccurate (particularly if this is a GraphicsWindow, which has system-
327  // imposed restrictions on size).
328  tex->set_size_padded(get_x_size(), get_y_size(), tex->get_z_size());
329 
330  if (_fb_properties.is_stereo() && plane == RTP_color) {
331  if (tex->get_num_views() < 2) {
332  tex->set_num_views(2);
333  }
334  }
335 
336  if (!support_render_texture || !get_supports_render_texture()) {
337  // Binding is not supported or it is disabled, so just fall back to copy
338  // instead.
339  if (mode == RTM_bind_or_copy) {
340  mode = RTM_copy_texture;
341  } else if (mode == RTM_bind_layered) {
342  // We can't fallback to copy, because that doesn't work for layered
343  // textures. The best thing we can do is raise an error message.
344  display_cat.error() <<
345  "add_render_texture: RTM_bind_layered was requested but "
346  "render-to-texture is not supported or has been disabled!\n";
347  }
348  }
349 
350  if (mode == RTM_bind_layered && _gsg != nullptr && !_gsg->get_supports_geometry_shaders()) {
351  // Layered FBOs require a geometry shader to write to any but the first
352  // layer.
353  display_cat.warning() <<
354  "add_render_texture: RTM_bind_layered was requested but "
355  "geometry shaders are not supported!\n";
356  }
357 
358  if (mode == RTM_bind_or_copy || mode == RTM_bind_layered) {
359  // If we're still planning on binding, indicate it in texture properly.
360  tex->set_render_to_texture(true);
361  }
362  else if ((plane == RTP_depth || plane == RTP_depth_stencil) && _fb_properties.get_depth_bits() == 0) {
363  // If we're not providing the depth buffer, we need something to copy from.
364  display_cat.error()
365  << "add_render_texture: can't copy depth from framebuffer without depth bits!\n";
366  return;
367  }
368 
369  CDWriter cdata(_cycler, true);
370  RenderTexture result;
371  result._texture = tex;
372  result._plane = plane;
373  result._rtm_mode = mode;
374  cdata->_textures.push_back(result);
375  ++(cdata->_textures_seq);
376 
377  throw_event("render-texture-targets-changed");
378 }
379 
380 /**
381  * This is a deprecated interface that made sense back when GraphicsOutputs
382  * could only render into one texture at a time. From now on, use
383  * clear_render_textures and add_render_texture instead.
384  *
385  * @deprecated Use add_render_texture() instead.
386  */
388 setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) {
389  display_cat.warning() <<
390  "Using deprecated setup_render_texture interface.\n";
392  if (to_ram) {
393  add_render_texture(tex, RTM_copy_ram);
394  } else if (allow_bind) {
395  add_render_texture(tex, RTM_bind_or_copy);
396  } else {
397  add_render_texture(tex, RTM_copy_texture);
398  }
399 }
400 
401 /**
402  * Sets the active flag associated with the GraphicsOutput. If the
403  * GraphicsOutput is marked inactive, nothing is rendered.
404  */
405 void GraphicsOutput::
406 set_active(bool active) {
407  CDLockedReader cdata(_cycler);
408  if (cdata->_active != active) {
409  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
410  cdataw->_active = active;
411  }
412 }
413 
414 /**
415  * Returns true if the window is ready to be rendered into, false otherwise.
416  */
417 bool GraphicsOutput::
418 is_active() const {
419  if (!is_valid()) {
420  return false;
421  }
422 
423  CDLockedReader cdata(_cycler);
424  if (!cdata->_active) {
425  return false;
426  }
427 
428  if (cdata->_one_shot_frame != -1) {
429  // If one_shot is in effect, then we are active only for the one indicated
430  // frame.
431  if (cdata->_one_shot_frame != ClockObject::get_global_clock()->get_frame_count()) {
432  return false;
433  } else {
434  return true;
435  }
436  }
437 
438  // If the window has a clear value set, it is active.
439  if (is_any_clear_active()) {
440  return true;
441  }
442 
443  // If we triggered a copy operation, it is also active.
444  if (_trigger_copy) {
445  return true;
446  }
447 
448  // The window is active if at least one display region is active.
449  if (cdata->_active_display_regions_stale) {
450  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, false);
451  ((GraphicsOutput *)this)->do_determine_display_regions(cdataw);
452  return !cdataw->_active_display_regions.empty();
453  } else {
454  return !cdata->_active_display_regions.empty();
455  }
456 }
457 
458 /**
459  * Changes the current setting of the one-shot flag. When this is true, the
460  * GraphicsOutput will render the current frame and then automatically set
461  * itself inactive. This is particularly useful for buffers that are created
462  * for the purposes of render-to-texture, for static textures that don't need
463  * to be continually re-rendered once they have been rendered the first time.
464  *
465  * Setting the buffer inactive is not the same thing as destroying it. You
466  * are still responsible for passing this buffer to
467  * GraphicsEngine::remove_window() when you no longer need the texture, in
468  * order to clean up fully. (However, you should not call remove_window() on
469  * this buffer while the texture is still needed, because depending on the
470  * render-to-texture mechanism in use, this may invalidate the texture
471  * contents.)
472  */
473 void GraphicsOutput::
474 set_one_shot(bool one_shot) {
475  CDWriter cdata(_cycler, true);
476  if (one_shot) {
477  cdata->_one_shot_frame = ClockObject::get_global_clock()->get_frame_count();
478  } else {
479  cdata->_one_shot_frame = -1;
480  }
481 }
482 
483 /**
484  * Returns the current setting of the one-shot flag. When this is true, the
485  * GraphicsOutput will automatically set itself inactive after the next frame.
486  */
487 bool GraphicsOutput::
488 get_one_shot() const {
489  CDReader cdata(_cycler);
490  return (cdata->_one_shot_frame == ClockObject::get_global_clock()->get_frame_count());
491 }
492 
493 /**
494  * Changes the current setting of the inverted flag. When this is true, the
495  * scene is rendered into the window upside-down and backwards, that is,
496  * inverted as if viewed through a mirror placed on the floor.
497  *
498  * This is primarily intended to support DirectX (and a few buggy OpenGL
499  * graphics drivers) that perform a framebuffer-to-texture copy upside-down
500  * from the usual OpenGL (and Panda) convention. Panda will automatically set
501  * this flag for offscreen buffers on hardware that is known to do this, to
502  * compensate when rendering offscreen into a texture.
503  */
504 void GraphicsOutput::
505 set_inverted(bool inverted) {
506  if (_inverted != inverted) {
507  _inverted = inverted;
508 
509  if (get_y_size() != 0) {
510  // All of our DisplayRegions need to recompute their pixel positions
511  // now.
512  TotalDisplayRegions::iterator dri;
513  for (dri = _total_display_regions.begin();
514  dri != _total_display_regions.end();
515  ++dri) {
516  (*dri)->compute_pixels(get_x_size(), get_y_size());
517  }
518  }
519  }
520 }
521 
522 /**
523  * Enables side-by-side stereo mode on this particular window. When side-by-
524  * side stereo mode is in effect, DisplayRegions that have the "left" channel
525  * set will render on the part of the window specified by sbs_left_dimensions
526  * (typically the left half: (0, 0.5, 0, 1)), while DisplayRegions that have
527  * the "right" channel set will render on the part of the window specified by
528  * sbs_right_dimensions (typically the right half: (0.5, 1, 0, 1)).
529  *
530  * This is commonly used in a dual-monitor mode, where a window is opened that
531  * spans two monitors, and each monitor represents a different eye.
532  */
534 set_side_by_side_stereo(bool side_by_side_stereo) {
535  LVecBase4 left, right;
536  left.set(sbs_left_dimensions[0], sbs_left_dimensions[1],
537  sbs_left_dimensions[2], sbs_left_dimensions[3]);
538  right.set(sbs_right_dimensions[0], sbs_right_dimensions[1],
539  sbs_right_dimensions[2], sbs_right_dimensions[3]);
540  set_side_by_side_stereo(side_by_side_stereo, left, right);
541 }
542 
543 /**
544  * Enables side-by-side stereo mode on this particular window. When side-by-
545  * side stereo mode is in effect, DisplayRegions that have the "left" channel
546  * set will render on the part of the window specified by sbs_left_dimensions
547  * (typically the left half: (0, 0.5, 0, 1)), while DisplayRegions that have
548  * the "right" channel set will render on the part of the window specified by
549  * sbs_right_dimensions (typically the right half: (0.5, 1, 0, 1)).
550  *
551  * This is commonly used in a dual-monitor mode, where a window is opened that
552  * spans two monitors, and each monitor represents a different eye.
553  */
555 set_side_by_side_stereo(bool side_by_side_stereo,
556  const LVecBase4 &sbs_left_dimensions,
557  const LVecBase4 &sbs_right_dimensions) {
558  _side_by_side_stereo = side_by_side_stereo;
559  if (_side_by_side_stereo) {
560  _sbs_left_dimensions = sbs_left_dimensions;
561  _sbs_right_dimensions = sbs_right_dimensions;
562  } else {
563  _sbs_left_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
564  _sbs_right_dimensions.set(0.0f, 1.0f, 0.0f, 1.0f);
565  }
566 }
567 
568 /**
569  * Returns the current setting of the delete flag. When this is true, the
570  * GraphicsOutput will automatically be removed before the beginning of the
571  * next frame by the GraphicsEngine.
572  */
574 get_delete_flag() const {
575  // We only delete the window or buffer automatically when it is no longer
576  // associated with a texture.
577  for (int i = 0; i < (int)_hold_textures.size(); i++) {
578  if (_hold_textures[i].is_valid_pointer()) {
579  return false;
580  }
581  }
582 
583  return _delete_flag;
584 }
585 
586 /**
587  * Adjusts the sorting order of this particular GraphicsOutput, relative to
588  * other GraphicsOutputs.
589  */
590 void GraphicsOutput::
591 set_sort(int sort) {
592  if (_sort != sort) {
593  if (_gsg != nullptr &&
594  _gsg->get_engine() != nullptr) {
595  _gsg->get_engine()->set_window_sort(this, sort);
596  }
597  }
598 }
599 
600 /**
601  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
602  * the window. The range on all parameters is 0..1.
603  *
604  * If is_stereo() is true for this window, and default-stereo-camera is
605  * configured true, this actually makes a StereoDisplayRegion. Call
606  * make_mono_display_region() or make_stereo_display_region() if you want to
607  * insist on one or the other.
608  */
610 make_display_region(const LVecBase4 &dimensions) {
611  if (is_stereo() && default_stereo_camera) {
612  return make_stereo_display_region(dimensions);
613  } else {
614  return make_mono_display_region(dimensions);
615  }
616 }
617 
618 /**
619  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
620  * the window. The range on all parameters is 0..1.
621  *
622  * This generally returns a mono DisplayRegion, even if is_stereo() is true.
623  * However, if side-by-side stereo is enabled, this will return a
624  * StereoDisplayRegion whose two eyes are both set to SC_mono. (This is
625  * necessary because in side-by-side stereo mode, it is necessary to draw even
626  * mono DisplayRegions twice).
627  */
629 make_mono_display_region(const LVecBase4 &dimensions) {
630  if (_side_by_side_stereo) {
632  dr->get_left_eye()->set_stereo_channel(Lens::SC_mono);
633  dr->get_right_eye()->set_stereo_channel(Lens::SC_mono);
634  return dr;
635  }
636 
637  return new DisplayRegion(this, dimensions);
638 }
639 
640 /**
641  * Creates a new DisplayRegion that covers the indicated sub-rectangle within
642  * the window. The range on all parameters is 0..1.
643  *
644  * This always returns a stereo DisplayRegion, even if is_stereo() is false.
645  */
647 make_stereo_display_region(const LVecBase4 &dimensions) {
648  PT(DisplayRegion) left, right;
649 
650  if (_side_by_side_stereo) {
651  // On a side-by-side stereo window, each eye gets the corresponding
652  // dimensions of its own sub-region.
653  PN_stdfloat left_l = _sbs_left_dimensions[0];
654  PN_stdfloat left_b = _sbs_left_dimensions[2];
655  PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
656  PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
657  LVecBase4 left_dimensions(dimensions[0] * left_w + left_l,
658  dimensions[1] * left_w + left_l,
659  dimensions[2] * left_h + left_b,
660  dimensions[3] * left_h + left_b);
661  left = new DisplayRegion(this, left_dimensions);
662 
663  PN_stdfloat right_l = _sbs_right_dimensions[0];
664  PN_stdfloat right_b = _sbs_right_dimensions[2];
665  PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
666  PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
667  LVecBase4 right_dimensions(dimensions[0] * right_w + right_l,
668  dimensions[1] * right_w + right_l,
669  dimensions[2] * right_h + right_b,
670  dimensions[3] * right_h + right_b);
671  right = new DisplayRegion(this, right_dimensions);
672 
673  if (_swap_eyes) {
674  DisplayRegion *t = left;
675  left = right;
676  right = t;
677  }
678 
679  } else {
680  // Not a side-by-side stereo window; thus, both the left and right eyes
681  // are the same region: the region specified.
682  left = new DisplayRegion(this, dimensions);
683  right = new DisplayRegion(this, dimensions);
684 
685  // In this case, we assume that the two eyes will share the same depth
686  // buffer, which means the right eye should clear the depth buffer by
687  // default.
688  if (get_clear_depth_active()) {
689  right->set_clear_depth_active(true);
690  }
691  if (get_clear_stencil_active()) {
692  right->set_clear_stencil_active(true);
693  }
694  }
695 
696  PT(StereoDisplayRegion) stereo = new StereoDisplayRegion(this, dimensions,
697  left, right);
698 
699  return stereo;
700 }
701 
702 /**
703  * Removes the indicated DisplayRegion from the window, and destructs it if
704  * there are no other references.
705  *
706  * Returns true if the DisplayRegion is found and removed, false if it was not
707  * a part of the window.
708  */
710 remove_display_region(DisplayRegion *display_region) {
711  LightMutexHolder holder(_lock);
712 
713  nassertr(display_region != _overlay_display_region, false);
714 
715  if (display_region->is_stereo()) {
716  StereoDisplayRegion *sdr;
717  DCAST_INTO_R(sdr, display_region, false);
718  do_remove_display_region(sdr->get_left_eye());
719  do_remove_display_region(sdr->get_right_eye());
720  }
721 
722  return do_remove_display_region(display_region);
723 }
724 
725 /**
726  * Removes all display regions from the window, except the default one that is
727  * created with the window.
728  */
731  LightMutexHolder holder(_lock);
732 
733  TotalDisplayRegions::iterator dri;
734  for (dri = _total_display_regions.begin();
735  dri != _total_display_regions.end();
736  ++dri) {
737  DisplayRegion *display_region = (*dri);
738  if (display_region != _overlay_display_region) {
739  // Let's aggressively clean up the display region too.
740  display_region->cleanup();
741  display_region->_window = nullptr;
742  }
743  }
744  _total_display_regions.clear();
745  _total_display_regions.push_back(_overlay_display_region);
746 
747  OPEN_ITERATE_ALL_STAGES(_cycler) {
748  CDStageWriter cdata(_cycler, pipeline_stage);
749  cdata->_active_display_regions_stale = true;
750  }
751  CLOSE_ITERATE_ALL_STAGES(_cycler);
752 }
753 
754 /**
755  * Replaces the special "overlay" DisplayRegion that is created for each
756  * window or buffer. See get_overlay_display_region(). This must be a new
757  * DisplayRegion that has already been created for this window, for instance
758  * via a call to make_mono_display_region(). You are responsible for ensuring
759  * that the new DisplayRegion covers the entire window. The previous overlay
760  * display region is not automatically removed; you must explicitly call
761  * remove_display_region() on it after replacing it with this method, if you
762  * wish it to be removed.
763  *
764  * Normally, there is no reason to change the overlay DisplayRegion, so this
765  * method should be used only in very unusual circumstances.
766  */
768 set_overlay_display_region(DisplayRegion *display_region) {
769  nassertv(display_region->get_window() == this);
770  _overlay_display_region = display_region;
771 }
772 
773 /**
774  * Returns the number of DisplayRegions that have been created within the
775  * window, active or otherwise.
776  */
778 get_num_display_regions() const {
779  LightMutexHolder holder(_lock);
780  return _total_display_regions.size();
781 }
782 
783 /**
784  * Returns the nth DisplayRegion of those that have been created within the
785  * window. This may return NULL if n is out of bounds; particularly likely if
786  * the number of display regions has changed since the last call to
787  * get_num_display_regions().
788  */
789 PT(DisplayRegion) GraphicsOutput::
790 get_display_region(int n) const {
791  determine_display_regions();
792  PT(DisplayRegion) result;
793  {
794  LightMutexHolder holder(_lock);
795  if (n >= 0 && n < (int)_total_display_regions.size()) {
796  result = _total_display_regions[n];
797  } else {
798  result = nullptr;
799  }
800  }
801  return result;
802 }
803 
804 /**
805  * Returns the number of active DisplayRegions that have been created within
806  * the window.
807  */
808 int GraphicsOutput::
809 get_num_active_display_regions() const {
810  determine_display_regions();
811  CDReader cdata(_cycler);
812  return cdata->_active_display_regions.size();
813 }
814 
815 /**
816  * Returns the nth active DisplayRegion of those that have been created within
817  * the window. This may return NULL if n is out of bounds; particularly
818  * likely if the number of display regions has changed since the last call to
819  * get_num_active_display_regions().
820  */
821 PT(DisplayRegion) GraphicsOutput::
822 get_active_display_region(int n) const {
823  determine_display_regions();
824 
825  CDReader cdata(_cycler);
826  if (n >= 0 && n < (int)cdata->_active_display_regions.size()) {
827  return cdata->_active_display_regions[n];
828  }
829  return nullptr;
830 }
831 
832 /**
833  * Creates and returns an offscreen buffer for rendering into, the result of
834  * which will be a texture suitable for applying to geometry within the scene
835  * rendered into this window.
836  *
837  * If tex is not NULL, it is the texture that will be set up for rendering
838  * into; otherwise, a new Texture object will be created. In either case, the
839  * target texture can be retrieved from the return value with
840  * buffer->get_texture() (assuming the return value is not NULL).
841  *
842  * If to_ram is true, the buffer will be set up to download its contents to
843  * the system RAM memory associated with the Texture object, instead of
844  * keeping it strictly within texture memory; this is much slower, but it
845  * allows using the texture with any GSG.
846  *
847  * This will attempt to be smart about maximizing render performance while
848  * minimizing framebuffer waste. It might return a GraphicsBuffer set to
849  * render directly into a texture, if possible; or it might return a
850  * ParasiteBuffer that renders into this window. The return value is NULL if
851  * the buffer could not be created for some reason.
852  *
853  * When you are done using the buffer, you should remove it with a call to
854  * GraphicsEngine::remove_window().
855  */
856 GraphicsOutput *GraphicsOutput::
857 make_texture_buffer(const string &name, int x_size, int y_size,
858  Texture *tex, bool to_ram, FrameBufferProperties *fbp) {
859 
860  FrameBufferProperties props;
861  props.set_rgb_color(1);
862  props.set_color_bits(1);
863  props.set_alpha_bits(1);
864  props.set_depth_bits(1);
865 
866  if (fbp == nullptr) {
867  fbp = &props;
868  }
869 
870  int flags = GraphicsPipe::BF_refuse_window;
871  if (textures_power_2 != ATS_none) {
872  flags |= GraphicsPipe::BF_size_power_2;
873  }
874  if (tex != nullptr &&
875  tex->get_texture_type() == Texture::TT_cube_map) {
876  flags |= GraphicsPipe::BF_size_square;
877  }
878 
879  GraphicsOutput *buffer = get_gsg()->get_engine()->
880  make_output(get_gsg()->get_pipe(),
881  name, get_child_sort(),
882  *fbp, WindowProperties::size(x_size, y_size),
883  flags, get_gsg(), get_host());
884 
885  if (buffer != nullptr) {
886  if (buffer->get_gsg() == nullptr ||
887  buffer->get_gsg()->get_prepared_objects() != get_gsg()->get_prepared_objects()) {
888  // If the newly-created buffer doesn't share texture objects with the
889  // current GSG, then we will have to force the texture copy to go
890  // through RAM.
891  to_ram = true;
892  }
893 
894  buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_bind_or_copy);
895  return buffer;
896  }
897 
898  return nullptr;
899 }
900 
901 /**
902  * This is similar to make_texture_buffer() in that it allocates a separate
903  * buffer suitable for rendering to a texture that can be assigned to geometry
904  * in this window, but in this case, the buffer is set up to render the six
905  * faces of a cube map.
906  *
907  * The buffer is automatically set up with six display regions and six
908  * cameras, each of which are assigned the indicated draw_mask and parented to
909  * the given camera_rig node (which you should then put in your scene to
910  * render the cube map from the appropriate point of view).
911  *
912  * You may take the texture associated with the buffer and apply it to
913  * geometry, particularly with TexGenAttrib::M_world_cube_map also in effect,
914  * to apply a reflection of everything seen by the camera rig.
915  */
917 make_cube_map(const string &name, int size, NodePath &camera_rig,
918  DrawMask camera_mask, bool to_ram, FrameBufferProperties *fbp) {
919  if (!to_ram) {
920  // Check the limits imposed by the GSG. (However, if we're rendering the
921  // texture to RAM only, these limits may be irrelevant.)
923  int max_dimension = gsg->get_max_cube_map_dimension();
924  if (max_dimension == 0 || !gsg->get_supports_cube_map()) {
925  // The GSG doesn't support cube mapping; too bad for you.
926  display_cat.warning()
927  << "Cannot make dynamic cube map; GSG does not support cube maps.\n";
928  return nullptr;
929  }
930  if (max_dimension > 0) {
931  size = std::min(max_dimension, size);
932  }
933  }
934 
935  // Usually, we want the whole camera_rig to keep itself unrotated with
936  // respect to the world coordinate space, so the user can apply
937  // TexGenAttrib::M_world_cube_map to the objects on which the cube map
938  // texture is applied. If for some reason the user doesn't want this
939  // behavior, he can take this effect off again.
940  camera_rig.node()->set_effect(CompassEffect::make(NodePath()));
941 
942  PT(Texture) tex = new Texture(name);
943  tex->setup_cube_map();
944  tex->set_wrap_u(SamplerState::WM_clamp);
945  tex->set_wrap_v(SamplerState::WM_clamp);
946  GraphicsOutput *buffer;
947 
948  buffer = make_texture_buffer(name, size, size, tex, to_ram, fbp);
949 
950  // We don't need to clear the overall buffer; instead, we'll clear each
951  // display region.
952  buffer->set_clear_color_active(false);
953  buffer->set_clear_depth_active(false);
954  buffer->set_clear_stencil_active(false);
955 
956  PT(Lens) lens = new PerspectiveLens(90, 90);
957 
958  for (int i = 0; i < 6; i++) {
959  PT(Camera) camera = new Camera(cube_faces[i]._name);
960  camera->set_lens(lens);
961  camera->set_camera_mask(camera_mask);
962  NodePath camera_np = camera_rig.attach_new_node(camera);
963  camera_np.look_at(cube_faces[i]._look_at, cube_faces[i]._up);
964 
965  DisplayRegion *dr;
966  dr = buffer->make_display_region();
967 
968  dr->set_target_tex_page(i);
969  dr->copy_clear_settings(*this);
970  dr->set_camera(camera_np);
971  }
972 
973  return buffer;
974 }
975 
976 /**
977  * Returns a PandaNode containing a square polygon. The dimensions are
978  * (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of
979  * this GraphicsOutput is aligned properly to the polygon. The GraphicsOutput
980  * promises to surgically update the Geom inside the PandaNode if necessary to
981  * maintain this invariant.
982  *
983  * Each invocation of this function returns a freshly- allocated PandaNode.
984  * You can therefore safely modify the RenderAttribs of the PandaNode. The
985  * PandaNode is initially textured with the texture of this GraphicOutput.
986  */
989  if (_texture_card == nullptr) {
990  PT(GeomVertexData) vdata = create_texture_card_vdata(get_x_size(), get_y_size());
991  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
992  strip->set_shade_model(Geom::SM_uniform);
993  strip->add_next_vertices(4);
994  strip->close_primitive();
995  PT(Geom) geom = new Geom(vdata);
996  geom->add_primitive(strip);
997  _texture_card = new GeomNode("texture card");
998  _texture_card->add_geom(geom);
999  }
1000 
1001  NodePath path("texture card");
1002  path.node()->add_child(_texture_card);
1003 
1004  // The texture card, by default, is textured with the first render-to-
1005  // texture output texture. Depth and stencil textures are ignored. The
1006  // user can freely alter the card's texture attrib.
1007  CDReader cdata(_cycler);
1008  RenderTextures::const_iterator ri;
1009  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1010  Texture *texture = (*ri)._texture;
1011  if ((texture->get_format() != Texture::F_depth_stencil)) {
1012  path.set_texture(texture, 0);
1013  break;
1014  }
1015  }
1016 
1017  return path;
1018 }
1019 
1020 /**
1021  * Will attempt to use the depth buffer of the input graphics_output. The
1022  * buffer sizes must be exactly the same.
1023  */
1025 share_depth_buffer(GraphicsOutput *graphics_output) {
1026  return false;
1027 }
1028 
1029 /**
1030  * Discontinue sharing the depth buffer.
1031  */
1034 }
1035 
1036 /**
1037  * Returns true if this particular GraphicsOutput can render directly into a
1038  * texture, or false if it must always copy-to-texture at the end of each
1039  * frame to achieve this effect.
1040  */
1041 bool GraphicsOutput::
1043  return false;
1044 }
1045 
1046 /**
1047  * Returns true if a frame has been rendered and needs to be flipped, false
1048  * otherwise.
1049  */
1051 flip_ready() const {
1052  return _flip_ready;
1053 }
1054 
1055 /**
1056  * This is normally called only from within make_texture_buffer(). When
1057  * called on a ParasiteBuffer, it returns the host of that buffer; but when
1058  * called on some other buffer, it returns the buffer itself.
1059  */
1061 get_host() {
1062  return this;
1063 }
1064 
1065 /**
1066  * This is called by the GraphicsEngine to request that the window (or
1067  * whatever) open itself or, in general, make itself valid, at the next call
1068  * to process_events().
1069  */
1071 request_open() {
1072 }
1073 
1074 /**
1075  * This is called by the GraphicsEngine to request that the window (or
1076  * whatever) close itself or, in general, make itself invalid, at the next
1077  * call to process_events(). By that time we promise the gsg pointer will be
1078  * cleared.
1079  */
1081 request_close() {
1082 }
1083 
1084 /**
1085  * This is called by the GraphicsEngine to insist that the output be closed
1086  * immediately. This is only called from the window thread.
1087  */
1089 set_close_now() {
1090 }
1091 
1092 /**
1093  * Resets the window framebuffer from its derived children. Does nothing
1094  * here.
1095  */
1097 reset_window(bool swapchain) {
1098  display_cat.info()
1099  << "Resetting " << get_type() << "\n";
1100 }
1101 
1102 /**
1103  * Sets the window's _pipe pointer to NULL; this is generally called only as a
1104  * precursor to deleting the window.
1105  */
1107 clear_pipe() {
1108  _pipe = nullptr;
1109 }
1110 
1111 /**
1112  * Changes the x_size and y_size, then recalculates structures that depend on
1113  * size. The recalculation currently includes: - compute_pixels on all the
1114  * graphics regions. - updating the texture card, if one is present.
1115  */
1117 set_size_and_recalc(int x, int y) {
1118  _size.set(x, y);
1119  _has_size = true;
1120 
1121  _is_nonzero_size = (x > 0 && y > 0);
1122 
1123  int fb_x_size = get_fb_x_size();
1124  int fb_y_size = get_fb_y_size();
1125 
1126  TotalDisplayRegions::iterator dri;
1127  for (dri = _total_display_regions.begin();
1128  dri != _total_display_regions.end();
1129  ++dri) {
1130  (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
1131  }
1132 
1133  if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {
1134  _texture_card->modify_geom(0)->set_vertex_data(create_texture_card_vdata(x, y));
1135  }
1136 }
1137 
1138 /**
1139  * Clears the entire framebuffer before rendering, according to the settings
1140  * of get_color_clear_active() and get_depth_clear_active() (inherited from
1141  * DrawableRegion).
1142  *
1143  * This function is called only within the draw thread.
1144  */
1146 clear(Thread *current_thread) {
1147  if (is_any_clear_active()) {
1148  if (display_cat.is_spam()) {
1149  display_cat.spam()
1150  << "clear(): " << get_type() << " "
1151  << get_name() << " " << (void *)this << "\n";
1152  }
1153 
1154  nassertv(_gsg != nullptr);
1155 
1156  DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread);
1157  _gsg->prepare_display_region(&dr_reader);
1158  _gsg->clear(this);
1159  }
1160 }
1161 
1162 /**
1163  * This function will be called within the draw thread before beginning
1164  * rendering for a given frame. It should do whatever setup is required, and
1165  * return true if the frame should be rendered, or false if it should be
1166  * skipped.
1167  */
1169 begin_frame(FrameMode mode, Thread *current_thread) {
1170  return false;
1171 }
1172 
1173 /**
1174  * This function will be called within the draw thread after rendering is
1175  * completed for a given frame. It should do whatever finalization is
1176  * required.
1177  */
1179 end_frame(FrameMode mode, Thread *current_thread) {
1180 }
1181 
1182 /**
1183  * Called by the GraphicsEngine when the window is about to change to another
1184  * DisplayRegion. This exists mainly to provide a callback for switching the
1185  * cube map face, if we are rendering to the different faces of a cube map.
1186  */
1189  int new_target_tex_page = new_dr->get_target_tex_page();
1190 
1191  if (new_target_tex_page != -1 && new_target_tex_page != _target_tex_page) {
1192 
1193  if (new_target_tex_page == -1) {
1194  new_target_tex_page = 0;
1195  }
1196  int old_target_tex_page = _target_tex_page;
1197  DisplayRegion *old_page_dr = _prev_page_dr;
1198  _target_tex_page = new_target_tex_page;
1199  _prev_page_dr = new_dr->get_object();
1200 
1201  CDReader cdata(_cycler);
1202  RenderTextures::const_iterator ri;
1203  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1204  RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1205  RenderTexturePlane plane = (*ri)._plane;
1206  Texture *texture = (*ri)._texture;
1207  if (rtm_mode != RTM_none) {
1208  if (rtm_mode == RTM_bind_or_copy || rtm_mode == RTM_bind_layered) {
1209  // In render-to-texture mode, switch the rendering backend to the
1210  // new page, so that the subsequent frame will be rendered to the
1211  // correct page.
1212  select_target_tex_page(_target_tex_page);
1213 
1214  } else if (old_target_tex_page != -1) {
1215  // In copy-to-texture mode, copy the just-rendered framebuffer to
1216  // the old texture page.
1217 
1218  nassertv(old_page_dr != nullptr);
1219  if (display_cat.is_debug()) {
1220  display_cat.debug()
1221  << "Copying texture for " << get_name() << " at scene change.\n";
1222  display_cat.debug()
1223  << "target_tex_page = " << old_target_tex_page << "\n";
1224  }
1225  RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1226  get_fb_properties());
1227 
1228  if (plane == RTP_color && _fb_properties.is_stereo()) {
1229  // We've got two texture views to copy.
1230  RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1231  RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1232 
1233  if (rtm_mode == RTM_copy_ram) {
1234  _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1235  old_page_dr, left);
1236  _gsg->framebuffer_copy_to_ram(texture, 1, old_target_tex_page,
1237  old_page_dr, right);
1238  } else {
1239  _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1240  old_page_dr, left);
1241  _gsg->framebuffer_copy_to_texture(texture, 1, old_target_tex_page,
1242  old_page_dr, right);
1243  }
1244  } else {
1245  if (rtm_mode == RTM_copy_ram) {
1246  _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1247  old_page_dr, buffer);
1248  } else {
1249  _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1250  old_page_dr, buffer);
1251  }
1252  }
1253  }
1254  }
1255  }
1256  }
1257 }
1258 
1259 /**
1260  * Called internally when the window is in render-to-a-texture mode and we are
1261  * in the process of rendering the six faces of a cube map, or any other
1262  * multi-page texture. This should do whatever needs to be done to switch the
1263  * buffer to the indicated page.
1264  */
1267 }
1268 
1269 /**
1270  * This function will be called within the draw thread after end_frame() has
1271  * been called on all windows, to initiate the exchange of the front and back
1272  * buffers.
1273  *
1274  * This should instruct the window to prepare for the flip at the next video
1275  * sync, but it should not wait.
1276  *
1277  * We have the two separate functions, begin_flip() and end_flip(), to make it
1278  * easier to flip all of the windows at the same time.
1279  */
1281 begin_flip() {
1282 }
1283 
1284 /**
1285  * This function will be called within the draw thread after end_frame() has
1286  * been called on all windows, to initiate the exchange of the front and back
1287  * buffers.
1288  *
1289  * This should instruct the window to prepare for the flip when it is command
1290  * but not actually flip
1291  *
1292  */
1294 ready_flip() {
1295 }
1296 
1297 /**
1298  * This function will be called within the draw thread after begin_flip() has
1299  * been called on all windows, to finish the exchange of the front and back
1300  * buffers.
1301  *
1302  * This should cause the window to wait for the flip, if necessary.
1303  */
1305 end_flip() {
1306  _flip_ready = false;
1307 }
1308 
1309 /**
1310  * Do whatever processing in the window thread is appropriate for this output
1311  * object each frame.
1312  *
1313  * This function is called only within the window thread.
1314  */
1316 process_events() {
1317 }
1318 
1319 /**
1320  * Called internally when the pixel factor changes.
1321  */
1322 void GraphicsOutput::
1323 pixel_factor_changed() {
1324  if (_has_size) {
1326  }
1327 }
1328 
1329 /**
1330  * Set the delete flag, and do the usual cleanup activities associated with
1331  * that.
1332  */
1333 void GraphicsOutput::
1334 prepare_for_deletion() {
1335  CDWriter cdata(_cycler, true);
1336  cdata->_active = false;
1337 
1338  // If we were rendering directly to texture, we can't delete the buffer
1339  // until all the textures are gone too.
1340  RenderTextures::iterator ri;
1341  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1342  if ((*ri)._rtm_mode == RTM_bind_or_copy || (*ri)._rtm_mode == RTM_bind_layered) {
1343  _hold_textures.push_back((*ri)._texture);
1344  }
1345  }
1346  cdata->_textures.clear();
1347 
1348  _delete_flag = true;
1349 
1350  // We have to be sure to remove all of the display regions immediately, so
1351  // that circular reference counts can be cleared up (each display region
1352  // keeps a pointer to a CullResult, which can hold all sorts of pointers).
1354 }
1355 
1356 /**
1357  * If any textures are marked RTM_bind_or_copy, change them to
1358  * RTM_copy_texture. This does not change textures that are set to
1359  * RTM_bind_layered, as layered framebuffers aren't supported with
1360  * RTM_copy_texture.
1361  */
1362 void GraphicsOutput::
1363 promote_to_copy_texture() {
1364  CDLockedReader cdata(_cycler);
1365  RenderTextures::const_iterator ri;
1366 
1367  bool any_bind = false;
1368  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1369  if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1370  any_bind = true;
1371  break;
1372  }
1373  }
1374  if (any_bind) {
1375  CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
1376  RenderTextures::iterator ri;
1377  for (ri = cdataw->_textures.begin(); ri != cdataw->_textures.end(); ++ri) {
1378  if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1379  (*ri)._rtm_mode = RTM_copy_texture;
1380  }
1381  }
1382  }
1383 }
1384 
1385 /**
1386  * For all textures marked RTM_copy_texture, RTM_copy_ram,
1387  * RTM_triggered_copy_texture, or RTM_triggered_copy_ram, do the necessary
1388  * copies.
1389  *
1390  * Returns true if all copies are successful, false otherwise.
1391  */
1392 bool GraphicsOutput::
1393 copy_to_textures() {
1394  bool okflag = true;
1395 
1396  CDReader cdata(_cycler);
1397  RenderTextures::const_iterator ri;
1398  for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1399  RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1400  if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
1401  continue;
1402  }
1403 
1404  Texture *texture = (*ri)._texture;
1405  PStatTimer timer(_copy_texture_pcollector);
1406 
1407  if ((rtm_mode == RTM_copy_texture)||
1408  (rtm_mode == RTM_copy_ram)||
1409  ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
1410  ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
1411  if (display_cat.is_debug()) {
1412  display_cat.debug()
1413  << "Copying texture for " << get_name() << " at frame end.\n";
1414  display_cat.debug()
1415  << "target_tex_page = " << _target_tex_page << "\n";
1416  }
1417  RenderTexturePlane plane = (*ri)._plane;
1419  if (plane == RTP_color) {
1420  buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1421  get_fb_properties());
1422  }
1423 
1424  bool copied = false;
1425  DisplayRegion *dr = _overlay_display_region;
1426  if (_prev_page_dr != nullptr) {
1427  dr = _prev_page_dr;
1428  }
1429 
1430  if (plane == RTP_color && _fb_properties.is_stereo()) {
1431  // We've got two texture views to copy.
1432  RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1433  RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1434 
1435  if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1436  copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1437  dr, left);
1438  copied = _gsg->framebuffer_copy_to_ram(texture, 1, _target_tex_page,
1439  dr, right) && copied;
1440  } else {
1441  copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1442  dr, left);
1443  copied = _gsg->framebuffer_copy_to_texture(texture, 1, _target_tex_page,
1444  dr, right) && copied;
1445  }
1446  } else {
1447  if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1448  copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1449  dr, buffer);
1450  } else {
1451  copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1452  dr, buffer);
1453  }
1454  }
1455  if (!copied) {
1456  okflag = false;
1457  }
1458  }
1459  }
1460  if (_trigger_copy != nullptr) {
1461  _trigger_copy->set_result(nullptr);
1462  _trigger_copy = nullptr;
1463  }
1464 
1465  return okflag;
1466 }
1467 
1468 /**
1469  * Generates a GeomVertexData for a texture card.
1470  */
1471 PT(GeomVertexData) GraphicsOutput::
1472 create_texture_card_vdata(int x, int y) {
1473  PN_stdfloat xhi = 1.0;
1474  PN_stdfloat yhi = 1.0;
1475 
1476  if (Texture::get_textures_power_2() != ATS_none) {
1477  int xru = Texture::up_to_power_2(x);
1478  int yru = Texture::up_to_power_2(y);
1479  xhi = (x * 1.0f) / xru;
1480  yhi = (y * 1.0f) / yru;
1481  }
1482 
1484 
1485  PT(GeomVertexData) vdata = new GeomVertexData
1486  ("card", format, Geom::UH_static);
1487 
1488  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
1489  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
1490  GeomVertexWriter normal(vdata, InternalName::get_normal());
1491 
1492  vertex.add_data3(LVertex::rfu(-1.0f, 0.0f, 1.0f));
1493  vertex.add_data3(LVertex::rfu(-1.0f, 0.0f, -1.0f));
1494  vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, 1.0f));
1495  vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, -1.0f));
1496 
1497  texcoord.add_data2( 0.0f, yhi);
1498  texcoord.add_data2( 0.0f, 0.0f);
1499  texcoord.add_data2( xhi, yhi);
1500  texcoord.add_data2( xhi, 0.0f);
1501 
1502  normal.add_data3(LVector3::back());
1503  normal.add_data3(LVector3::back());
1504  normal.add_data3(LVector3::back());
1505  normal.add_data3(LVector3::back());
1506 
1507  return vdata;
1508 }
1509 
1510 /**
1511  * Called by the DisplayRegion constructor to add the new DisplayRegion to the
1512  * list.
1513  */
1514 DisplayRegion *GraphicsOutput::
1515 add_display_region(DisplayRegion *display_region) {
1516  LightMutexHolder holder(_lock);
1517  CDWriter cdata(_cycler, true);
1518  cdata->_active_display_regions_stale = true;
1519 
1520  _total_display_regions.push_back(display_region);
1521 
1522  return display_region;
1523 }
1524 
1525 /**
1526  * Internal implementation of remove_display_region. Assumes the lock is
1527  * already held.
1528  */
1529 bool GraphicsOutput::
1530 do_remove_display_region(DisplayRegion *display_region) {
1531  nassertr(display_region != _overlay_display_region, false);
1532 
1533  PT(DisplayRegion) drp = display_region;
1534  TotalDisplayRegions::iterator dri =
1535  find(_total_display_regions.begin(), _total_display_regions.end(), drp);
1536  if (dri != _total_display_regions.end()) {
1537  // Let's aggressively clean up the display region too.
1538  display_region->cleanup();
1539  display_region->_window = nullptr;
1540  _total_display_regions.erase(dri);
1541 
1542  OPEN_ITERATE_ALL_STAGES(_cycler) {
1543  CDStageWriter cdata(_cycler, pipeline_stage);
1544  cdata->_active_display_regions_stale = true;
1545  }
1546  CLOSE_ITERATE_ALL_STAGES(_cycler);
1547  return true;
1548  }
1549 
1550  return false;
1551 }
1552 
1553 /**
1554  * Re-sorts the list of active DisplayRegions within the window.
1555  */
1556 void GraphicsOutput::
1557 do_determine_display_regions(GraphicsOutput::CData *cdata) {
1558  cdata->_active_display_regions_stale = false;
1559 
1560  cdata->_active_display_regions.clear();
1561  cdata->_active_display_regions.reserve(_total_display_regions.size());
1562 
1563  int index = 0;
1564  TotalDisplayRegions::const_iterator dri;
1565  for (dri = _total_display_regions.begin();
1566  dri != _total_display_regions.end();
1567  ++dri) {
1568  DisplayRegion *display_region = (*dri);
1569  if (display_region->is_active()) {
1570  cdata->_active_display_regions.push_back(display_region);
1571  display_region->set_active_index(index);
1572  ++index;
1573  } else {
1574  display_region->set_active_index(-1);
1575  }
1576  }
1577 
1578  std::stable_sort(cdata->_active_display_regions.begin(),
1579  cdata->_active_display_regions.end(),
1581 }
1582 
1583 /**
1584  * Parses one of the keywords in the red-blue-stereo-colors Config.prc
1585  * variable, and returns the corresponding bitmask.
1586  *
1587  * These bitmask values are taken from ColorWriteAttrib.
1588  */
1589 unsigned int GraphicsOutput::
1590 parse_color_mask(const string &word) {
1591  unsigned int result = 0;
1592  vector_string components;
1593  tokenize(word, components, "|");
1594 
1595  vector_string::const_iterator ci;
1596  for (ci = components.begin(); ci != components.end(); ++ci) {
1597  string w = downcase(*ci);
1598  if (w == "red" || w == "r") {
1599  result |= 0x001;
1600 
1601  } else if (w == "green" || w == "g") {
1602  result |= 0x002;
1603 
1604  } else if (w == "blue" || w == "b") {
1605  result |= 0x004;
1606 
1607  } else if (w == "yellow" || w == "y") {
1608  result |= 0x003;
1609 
1610  } else if (w == "magenta" || w == "m") {
1611  result |= 0x005;
1612 
1613  } else if (w == "cyan" || w == "c") {
1614  result |= 0x006;
1615 
1616  } else if (w == "alpha" || w == "a") {
1617  result |= 0x008;
1618 
1619  } else if (w == "off") {
1620 
1621  } else {
1622  display_cat.warning()
1623  << "Invalid color in red-blue-stereo-colors: " << (*ci) << "\n";
1624  }
1625  }
1626 
1627  return result;
1628 }
1629 
1630 /**
1631  *
1632  */
1633 GraphicsOutput::CData::
1634 CData() {
1635  // The default is *not* active, so the entire pipeline stage is initially
1636  // populated with inactive outputs. Pipeline stage 0 is set to active in
1637  // the constructor.
1638  _active = false;
1639  _one_shot_frame = -1;
1640  _active_display_regions_stale = false;
1641 }
1642 
1643 /**
1644  *
1645  */
1646 GraphicsOutput::CData::
1647 CData(const GraphicsOutput::CData &copy) :
1648  _textures(copy._textures),
1649  _active(copy._active),
1650  _one_shot_frame(copy._one_shot_frame),
1651  _active_display_regions(copy._active_display_regions),
1652  _active_display_regions_stale(copy._active_display_regions_stale)
1653 {
1654 }
1655 
1656 /**
1657  *
1658  */
1659 CycleData *GraphicsOutput::CData::
1660 make_copy() const {
1661  return new CData(*this);
1662 }
1663 
1664 /**
1665  *
1666  */
1667 std::ostream &
1668 operator << (std::ostream &out, GraphicsOutput::FrameMode fm) {
1669  switch (fm) {
1670  case GraphicsOutput::FM_render:
1671  return out << "render";
1672  case GraphicsOutput::FM_parasite:
1673  return out << "parasite";
1674  case GraphicsOutput::FM_refresh:
1675  return out << "refresh";
1676  }
1677 
1678  return out << "(**invalid GraphicsOutput::FrameMode(" << (int)fm << ")**)";
1679 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
Definition: clockObject.h:94
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
const LColor & get_value() const
Returns the variable's value.
std::string get_word(size_t n) const
Returns the variable's nth value.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
int get_target_tex_page() const
Returns the target page number associated with this particular DisplayRegion, or -1 if it is not asso...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
is_stereo
Returns true if this is a StereoDisplayRegion, false otherwise.
Definition: displayRegion.h:90
set_camera
Sets the camera that is associated with this DisplayRegion.
Definition: displayRegion.h:94
set_target_tex_page
This is a special parameter that is only used when rendering the faces of a cube map or multipage and...
is_active
Returns the active flag associated with the DisplayRegion.
Definition: displayRegion.h:98
get_window
Returns the GraphicsOutput that this DisplayRegion is ultimately associated with, or NULL if no windo...
Definition: displayRegion.h:88
void cleanup()
Cleans up some pointers associated with the DisplayRegion to help reduce the chance of memory leaks d...
int get_draw_buffer_type() const
Returns the RenderBuffer into which the GSG should issue draw commands.
void set_clear_stencil_active(bool clear_stencil_active)
Toggles the flag that indicates whether the stencil buffer should be cleared every frame.
void set_clear_color_active(bool clear_color_active)
Toggles the flag that indicates whether the color buffer should be cleared every frame.
static int get_renderbuffer_type(int plane)
Returns the RenderBuffer::Type that corresponds to a RenderTexturePlane.
bool get_clear_depth_active() const
Returns the current setting of the flag that indicates whether the depth buffer should be cleared eve...
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,...
void copy_clear_settings(const DrawableRegion &copy)
Copies only the clear settings from the other drawable region.
bool get_clear_stencil_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
void set_clear_depth_active(bool clear_depth_active)
Toggles the flag that indicates whether the depth buffer should be cleared every frame.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
bool setup_color_texture(Texture *tex) const
Sets the texture up for render-to-texture matching these framebuffer properties.
bool setup_depth_texture(Texture *tex) const
Sets the texture up for render-to-texture matching these framebuffer properties.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
Defines a series of triangle strips.
Definition: geomTristrips.h:23
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
static const GeomVertexFormat * get_v3n3t2()
Returns a standard vertex format with a 2-component texture coordinate pair, a 3-component normal,...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row.
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row.
A container for geometry primitives.
Definition: geom.h:54
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
virtual void ready_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
virtual void process_events()
Do whatever processing in the window thread is appropriate for this output object each frame.
void clear_render_textures()
If the GraphicsOutput is currently rendering to a texture, then all textures are dissociated from the...
int get_fb_x_size() const
Returns the internal width of the window or buffer.
set_inverted
Changes the current setting of the inverted flag.
get_pipe
Returns the GraphicsPipe that this window is associated with.
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
void add_render_texture(Texture *tex, RenderTextureMode mode, RenderTexturePlane bitplane=RTP_COUNT)
Creates a new Texture object, suitable for rendering the contents of this buffer into,...
set_active
Sets the active flag associated with the GraphicsOutput.
void set_side_by_side_stereo(bool side_by_side_stereo)
Enables side-by-side stereo mode on this particular window.
is_active
Returns true if the window is ready to be rendered into, false otherwise.
get_gsg
Returns the GSG that is associated with this window.
virtual void clear_pipe()
Sets the window's _pipe pointer to NULL; this is generally called only as a precursor to deleting the...
void remove_all_display_regions()
Removes all display regions from the window, except the default one that is created with the window.
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
virtual bool share_depth_buffer(GraphicsOutput *graphics_output)
Will attempt to use the depth buffer of the input graphics_output.
set_one_shot
Changes the current setting of the one-shot flag.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
bool get_delete_flag() const
Returns the current setting of the delete flag.
void change_scenes(DisplayRegionPipelineReader *new_dr)
Called by the GraphicsEngine when the window is about to change to another DisplayRegion.
void setup_render_texture(Texture *tex, bool allow_bind, bool to_ram)
This is a deprecated interface that made sense back when GraphicsOutputs could only render into one t...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
StereoDisplayRegion * make_stereo_display_region()
Creates a new DisplayRegion that covers the entire window.
DisplayRegion * make_mono_display_region()
Creates a new DisplayRegion that covers the entire window.
virtual void unshare_depth_buffer()
Discontinue sharing the depth buffer.
get_supports_render_texture
Returns true if this particular GraphicsOutput can render directly into a texture,...
NodePath get_texture_card()
Returns a PandaNode containing a square polygon.
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.
get_num_display_regions
Returns the number of DisplayRegions that have been created within the window, active or otherwise.
get_child_sort
Returns the sort value of future offscreen buffers created by make_texture_sort().
get_one_shot
Returns the current setting of the one-shot flag.
int get_fb_y_size() const
Returns the internal height of the window or buffer.
DisplayRegion * make_display_region()
Creates a new DisplayRegion that covers the entire window.
virtual void reset_window(bool swapchain)
Resets the window framebuffer from its derived children.
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
void set_size_and_recalc(int x, int y)
Changes the x_size and y_size, then recalculates structures that depend on size.
bool remove_display_region(DisplayRegion *display_region)
Removes the indicated DisplayRegion from the window, and destructs it if there are no other reference...
bool is_valid() const
Returns true if the output is fully created and ready for rendering, false otherwise.
virtual void request_close()
This is called by the GraphicsEngine to request that the window (or whatever) close itself or,...
int get_y_size() const
Returns the visible height of the window or buffer, if it is known.
get_name
Returns the name that was passed to the GraphicsOutput constructor.
void set_overlay_display_region(DisplayRegion *display_region)
Replaces the special "overlay" DisplayRegion that is created for each window or buffer.
virtual void select_target_tex_page(int page)
Called internally when the window is in render-to-a-texture mode and we are in the process of renderi...
GraphicsOutput * make_cube_map(const std::string &name, int size, NodePath &camera_rig, DrawMask camera_mask=PandaNode::get_all_camera_mask(), bool to_ram=false, FrameBufferProperties *fbp=nullptr)
This is similar to make_texture_buffer() in that it allocates a separate buffer suitable for renderin...
bool is_stereo() const
Returns Returns true if this window can render stereo DisplayRegions, either through red-blue stereo ...
virtual void request_open()
This is called by the GraphicsEngine to request that the window (or whatever) open itself or,...
int get_x_size() const
Returns the visible width of the window or buffer, if it is known.
virtual bool flip_ready() const
Returns true if a frame has been rendered and needs to be flipped, false otherwise.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
set_sort
Adjusts the sorting order of this particular GraphicsOutput, relative to other GraphicsOutputs.
virtual void set_close_now()
This is called by the GraphicsEngine to insist that the output be closed immediately.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Encapsulates all the communication with a particular instance of a given rendering backend.
get_max_cube_map_dimension
Returns the largest possible texture size in any one dimension for a cube map texture,...
get_supports_cube_map
Returns true if this GSG can render cube map textures.
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
Definition: indirectLess.h:25
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
Similar to MutexHolder, but for a light mutex.
static void update_type(ReferenceCount *ptr, TypeHandle type)
Associates the indicated type with the given pointer.
Definition: memoryUsage.I:55
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
void look_at(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the transform on this NodePath so that it rotates to face the indicated point in space.
Definition: nodePath.I:789
void set_texture(Texture *tex, int priority=0)
Adds the indicated texture to the list of textures that will be rendered on the default texture stage...
Definition: nodePath.cxx:2938
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:600
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: pandaNode.cxx:999
A perspective-type lens: a normal camera.
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
Definition: renderBuffer.h:27
This is a special DisplayRegion wrapper that actually includes a pair of DisplayRegions internally: t...
get_right_eye
Returns a pointer to the right DisplayRegion managed by this stereo object.
get_left_eye
Returns a pointer to the left DisplayRegion managed by this stereo object.
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
set_compression
Requests that this particular Texture be compressed when it is loaded into texture memory.
Definition: texture.h:413
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition: texture.h:370
set_match_framebuffer_format
Sets the special flag that, if true, indicates to the GSG that the Texture's format should be chosen ...
Definition: texture.h:589
set_wrap_u
This setting determines what happens when the texture is sampled with a U value outside the range 0....
Definition: texture.h:378
set_num_views
Sets the number of "views" within a texture.
Definition: texture.h:354
get_num_views
Returns the number of "views" in the texture.
Definition: texture.h:354
get_texture_type
Returns the overall interpretation of the texture.
Definition: texture.h:365
set_format
Changes the format value for the texture components.
Definition: texture.h:370
set_render_to_texture
Sets a flag on the texture that indicates whether the texture is intended to be used as a direct-rend...
Definition: texture.h:417
void setup_cube_map()
Sets the texture as an empty cube map texture with no dimensions.
Definition: texture.I:155
get_z_size
Returns the depth of the texture image in texels.
Definition: texture.h:350
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:2008
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
void clear_ram_image()
Discards the current system-RAM image.
Definition: texture.I:1439
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well.
Definition: texture.cxx:1932
set_wrap_v
This setting determines what happens when the texture is sampled with a V value outside the range 0....
Definition: texture.h:382
set_component_type
Changes the data value for the texture components.
Definition: texture.h:374
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A container for the various kinds of properties we might ask to have on a graphics window before we o...
static WindowProperties size(const LVecBase2i &size)
Returns a WindowProperties structure with only the size specified.
has_size
Returns true if the window size has been specified, false otherwise.
get_size
Returns size in pixels of the useful part of the window, not including decorations.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(DisplayRegion) GraphicsOutput
Returns the nth DisplayRegion of those that have been created within the window.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.