Panda3D
Loading...
Searching...
No Matches
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
37using std::string;
38
39TypeHandle GraphicsOutput::_type_handle;
40
41PStatCollector GraphicsOutput::_make_current_pcollector("Draw:Make current");
42PStatCollector GraphicsOutput::_copy_texture_pcollector("Draw:Copy texture");
43PStatCollector GraphicsOutput::_cull_pcollector("Cull");
44PStatCollector GraphicsOutput::_draw_pcollector("Draw");
45
46struct 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
55static 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 */
68GraphicsOutput::
69GraphicsOutput(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 */
168GraphicsOutput::
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 */
237add_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);
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 }
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);
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);
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);
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).
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 */
388setup_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 */
406set_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 */
418is_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 */
474set_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 */
488get_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 */
505set_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 */
534set_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 */
555set_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 */
574get_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 */
591set_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 */
610make_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 */
629make_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 */
647make_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.
689 right->set_clear_depth_active(true);
690 }
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 */
710remove_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()) {
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 */
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 */
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 */
789PT(DisplayRegion) GraphicsOutput::
790get_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 */
808int GraphicsOutput::
809get_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 */
821PT(DisplayRegion) GraphicsOutput::
822get_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 you pass zero as the buffer size, the buffer will have the same size as
838 * the host window, and will automatically be resized when the host window is.
839 *
840 * If tex is not NULL, it is the texture that will be set up for rendering
841 * into; otherwise, a new Texture object will be created. In either case, the
842 * target texture can be retrieved from the return value with
843 * buffer->get_texture() (assuming the return value is not NULL).
844 *
845 * If to_ram is true, the buffer will be set up to download its contents to
846 * the system RAM memory associated with the Texture object, instead of
847 * keeping it strictly within texture memory; this is much slower, but it
848 * allows using the texture with any GSG.
849 *
850 * This will attempt to be smart about maximizing render performance while
851 * minimizing framebuffer waste. It might return a GraphicsBuffer set to
852 * render directly into a texture, if possible; or it might return a
853 * ParasiteBuffer that renders into this window. The return value is NULL if
854 * the buffer could not be created for some reason.
855 *
856 * When you are done using the buffer, you should remove it with a call to
857 * GraphicsEngine::remove_window().
858 */
859GraphicsOutput *GraphicsOutput::
860make_texture_buffer(const string &name, int x_size, int y_size,
861 Texture *tex, bool to_ram, FrameBufferProperties *fbp) {
862
864 props.set_rgb_color(1);
865 props.set_color_bits(1);
866 props.set_alpha_bits(1);
867 props.set_depth_bits(1);
868
869 if (fbp == nullptr) {
870 fbp = &props;
871 }
872
873 int flags = GraphicsPipe::BF_refuse_window;
874 if (textures_power_2 != ATS_none) {
875 flags |= GraphicsPipe::BF_size_power_2;
876 }
877 if (tex != nullptr &&
878 tex->get_texture_type() == Texture::TT_cube_map) {
879 flags |= GraphicsPipe::BF_size_square;
880 }
881
882 GraphicsOutput *buffer = get_gsg()->get_engine()->
883 make_output(get_gsg()->get_pipe(),
884 name, get_child_sort(),
885 *fbp, WindowProperties::size(x_size, y_size),
886 flags, get_gsg(), get_host());
887
888 if (buffer != nullptr) {
889 if (buffer->get_gsg() == nullptr ||
890 buffer->get_gsg()->get_prepared_objects() != get_gsg()->get_prepared_objects()) {
891 // If the newly-created buffer doesn't share texture objects with the
892 // current GSG, then we will have to force the texture copy to go
893 // through RAM.
894 to_ram = true;
895 }
896
897 buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_bind_or_copy);
898 return buffer;
899 }
900
901 return nullptr;
902}
903
904/**
905 * This is similar to make_texture_buffer() in that it allocates a separate
906 * buffer suitable for rendering to a texture that can be assigned to geometry
907 * in this window, but in this case, the buffer is set up to render the six
908 * faces of a cube map.
909 *
910 * The buffer is automatically set up with six display regions and six
911 * cameras, each of which are assigned the indicated draw_mask and parented to
912 * the given camera_rig node (which you should then put in your scene to
913 * render the cube map from the appropriate point of view).
914 *
915 * You may take the texture associated with the buffer and apply it to
916 * geometry, particularly with TexGenAttrib::M_world_cube_map also in effect,
917 * to apply a reflection of everything seen by the camera rig.
918 */
920make_cube_map(const string &name, int size, NodePath &camera_rig,
921 DrawMask camera_mask, bool to_ram, FrameBufferProperties *fbp) {
922 if (!to_ram) {
923 // Check the limits imposed by the GSG. (However, if we're rendering the
924 // texture to RAM only, these limits may be irrelevant.)
926 int max_dimension = gsg->get_max_cube_map_dimension();
927 if (max_dimension == 0 || !gsg->get_supports_cube_map()) {
928 // The GSG doesn't support cube mapping; too bad for you.
929 display_cat.warning()
930 << "Cannot make dynamic cube map; GSG does not support cube maps.\n";
931 return nullptr;
932 }
933 if (max_dimension > 0) {
934 size = std::min(max_dimension, size);
935 }
936 }
937
938 // Usually, we want the whole camera_rig to keep itself unrotated with
939 // respect to the world coordinate space, so the user can apply
940 // TexGenAttrib::M_world_cube_map to the objects on which the cube map
941 // texture is applied. If for some reason the user doesn't want this
942 // behavior, he can take this effect off again.
943 camera_rig.node()->set_effect(CompassEffect::make(NodePath()));
944
945 PT(Texture) tex = new Texture(name);
946 tex->setup_cube_map();
947 tex->set_wrap_u(SamplerState::WM_clamp);
948 tex->set_wrap_v(SamplerState::WM_clamp);
949 GraphicsOutput *buffer;
950
951 buffer = make_texture_buffer(name, size, size, tex, to_ram, fbp);
952
953 // We don't need to clear the overall buffer; instead, we'll clear each
954 // display region.
955 buffer->set_clear_color_active(false);
956 buffer->set_clear_depth_active(false);
957 buffer->set_clear_stencil_active(false);
958
959 PT(Lens) lens = new PerspectiveLens(90, 90);
960
961 for (int i = 0; i < 6; i++) {
962 PT(Camera) camera = new Camera(cube_faces[i]._name);
963 camera->set_lens(lens);
964 camera->set_camera_mask(camera_mask);
965 NodePath camera_np = camera_rig.attach_new_node(camera);
966 camera_np.look_at(cube_faces[i]._look_at, cube_faces[i]._up);
967
968 DisplayRegion *dr;
969 dr = buffer->make_display_region();
970
971 dr->set_target_tex_page(i);
972 dr->copy_clear_settings(*this);
973 dr->set_camera(camera_np);
974 }
975
976 return buffer;
977}
978
979/**
980 * Returns a PandaNode containing a square polygon. The dimensions are
981 * (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of
982 * this GraphicsOutput is aligned properly to the polygon. The GraphicsOutput
983 * promises to surgically update the Geom inside the PandaNode if necessary to
984 * maintain this invariant.
985 *
986 * Each invocation of this function returns a freshly- allocated PandaNode.
987 * You can therefore safely modify the RenderAttribs of the PandaNode. The
988 * PandaNode is initially textured with the texture of this GraphicOutput.
989 */
992 if (_texture_card == nullptr) {
993 PT(GeomVertexData) vdata = create_texture_card_vdata(get_x_size(), get_y_size());
994 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
995 strip->set_shade_model(Geom::SM_uniform);
996 strip->add_next_vertices(4);
997 strip->close_primitive();
998 PT(Geom) geom = new Geom(vdata);
999 geom->add_primitive(strip);
1000 _texture_card = new GeomNode("texture card");
1001 _texture_card->add_geom(geom);
1002 }
1003
1004 NodePath path("texture card");
1005 path.node()->add_child(_texture_card);
1006
1007 // The texture card, by default, is textured with the first render-to-
1008 // texture output texture. Depth and stencil textures are ignored. The
1009 // user can freely alter the card's texture attrib.
1010 CDReader cdata(_cycler);
1011 RenderTextures::const_iterator ri;
1012 for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1013 Texture *texture = (*ri)._texture;
1014 if ((texture->get_format() != Texture::F_depth_stencil)) {
1015 path.set_texture(texture, 0);
1016 break;
1017 }
1018 }
1019
1020 return path;
1021}
1022
1023/**
1024 * Will attempt to use the depth buffer of the input graphics_output. The
1025 * buffer sizes must be exactly the same.
1026 */
1028share_depth_buffer(GraphicsOutput *graphics_output) {
1029 return false;
1030}
1031
1032/**
1033 * Discontinue sharing the depth buffer.
1034 */
1038
1039/**
1040 * Returns true if this particular GraphicsOutput can render directly into a
1041 * texture, or false if it must always copy-to-texture at the end of each
1042 * frame to achieve this effect.
1043 */
1046 return false;
1047}
1048
1049/**
1050 * Returns true if a frame has been rendered and needs to be flipped, false
1051 * otherwise.
1052 */
1054flip_ready() const {
1055 return _flip_ready;
1056}
1057
1058/**
1059 * This is normally called only from within make_texture_buffer(). When
1060 * called on a ParasiteBuffer, it returns the host of that buffer; but when
1061 * called on some other buffer, it returns the buffer itself.
1062 */
1064get_host() {
1065 return this;
1066}
1067
1068/**
1069 * This is called by the GraphicsEngine to request that the window (or
1070 * whatever) open itself or, in general, make itself valid, at the next call
1071 * to process_events().
1072 */
1076
1077/**
1078 * This is called by the GraphicsEngine to request that the window (or
1079 * whatever) close itself or, in general, make itself invalid, at the next
1080 * call to process_events(). By that time we promise the gsg pointer will be
1081 * cleared.
1082 */
1086
1087/**
1088 * This is called by the GraphicsEngine to insist that the output be closed
1089 * immediately. This is only called from the window thread.
1090 */
1094
1095/**
1096 * Resets the window framebuffer from its derived children. Does nothing
1097 * here.
1098 */
1100reset_window(bool swapchain) {
1101 display_cat.info()
1102 << "Resetting " << get_type() << "\n";
1103}
1104
1105/**
1106 * Sets the window's _pipe pointer to NULL; this is generally called only as a
1107 * precursor to deleting the window.
1108 */
1110clear_pipe() {
1111 _pipe = nullptr;
1112}
1113
1114/**
1115 * Changes the x_size and y_size, then recalculates structures that depend on
1116 * size. The recalculation currently includes:
1117 * - compute_pixels on all the graphics regions.
1118 * - updating the texture card, if one is present.
1119 */
1121set_size_and_recalc(int x, int y) {
1122 _size.set(x, y);
1123 _has_size = true;
1124
1125 _is_nonzero_size = (x > 0 && y > 0);
1126
1127 int fb_x_size = get_fb_x_size();
1128 int fb_y_size = get_fb_y_size();
1129
1130 for (DisplayRegion *dr : _total_display_regions) {
1131 dr->compute_pixels_all_stages(fb_x_size, fb_y_size);
1132 }
1133
1134 if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {
1135 _texture_card->modify_geom(0)->set_vertex_data(create_texture_card_vdata(x, y));
1136 }
1137}
1138
1139/**
1140 * Clears the entire framebuffer before rendering, according to the settings
1141 * of get_color_clear_active() and get_depth_clear_active() (inherited from
1142 * DrawableRegion).
1143 *
1144 * This function is called only within the draw thread.
1145 */
1147clear(Thread *current_thread) {
1148 if (is_any_clear_active()) {
1149 if (display_cat.is_spam()) {
1150 display_cat.spam()
1151 << "clear(): " << get_type() << " "
1152 << get_name() << " " << (void *)this << "\n";
1153 }
1154
1155 nassertv(_gsg != nullptr);
1156
1157 DisplayRegionPipelineReader dr_reader(_overlay_display_region, current_thread);
1158 _gsg->prepare_display_region(&dr_reader);
1159 _gsg->clear(this);
1160 }
1161}
1162
1163/**
1164 * This function will be called within the draw thread before beginning
1165 * rendering for a given frame. It should do whatever setup is required, and
1166 * return true if the frame should be rendered, or false if it should be
1167 * skipped.
1168 */
1170begin_frame(FrameMode mode, Thread *current_thread) {
1171 return false;
1172}
1173
1174/**
1175 * This function will be called within the draw thread after rendering is
1176 * completed for a given frame. It should do whatever finalization is
1177 * required.
1178 */
1180end_frame(FrameMode mode, Thread *current_thread) {
1181}
1182
1183/**
1184 * Called by the GraphicsEngine when the window is about to change to another
1185 * DisplayRegion. This exists mainly to provide a callback for switching the
1186 * cube map face, if we are rendering to the different faces of a cube map.
1187 */
1190 int new_target_tex_page = new_dr->get_target_tex_page();
1191
1192 if (new_target_tex_page != -1 && new_target_tex_page != _target_tex_page) {
1193
1194 if (new_target_tex_page == -1) {
1195 new_target_tex_page = 0;
1196 }
1197 int old_target_tex_page = _target_tex_page;
1198 DisplayRegion *old_page_dr = _prev_page_dr;
1199 _target_tex_page = new_target_tex_page;
1200 _prev_page_dr = new_dr->get_object();
1201
1202 CDReader cdata(_cycler);
1203 RenderTextures::const_iterator ri;
1204 for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1205 RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1206 RenderTexturePlane plane = (*ri)._plane;
1207 Texture *texture = (*ri)._texture;
1208 if (rtm_mode != RTM_none) {
1209 if (rtm_mode == RTM_bind_or_copy || rtm_mode == RTM_bind_layered) {
1210 // In render-to-texture mode, switch the rendering backend to the
1211 // new page, so that the subsequent frame will be rendered to the
1212 // correct page.
1213 select_target_tex_page(_target_tex_page);
1214
1215 } else if (old_target_tex_page != -1) {
1216 // In copy-to-texture mode, copy the just-rendered framebuffer to
1217 // the old texture page.
1218
1219 nassertv(old_page_dr != nullptr);
1220 if (display_cat.is_debug()) {
1221 display_cat.debug()
1222 << "Copying texture for " << get_name() << " at scene change.\n";
1223 display_cat.debug()
1224 << "target_tex_page = " << old_target_tex_page << "\n";
1225 }
1226 RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1228
1229 if (plane == RTP_color && _fb_properties.is_stereo()) {
1230 // We've got two texture views to copy.
1231 RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1232 RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1233
1234 if (rtm_mode == RTM_copy_ram) {
1235 _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1236 old_page_dr, left);
1237 _gsg->framebuffer_copy_to_ram(texture, 1, old_target_tex_page,
1238 old_page_dr, right);
1239 } else {
1240 _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1241 old_page_dr, left);
1242 _gsg->framebuffer_copy_to_texture(texture, 1, old_target_tex_page,
1243 old_page_dr, right);
1244 }
1245 } else {
1246 if (rtm_mode == RTM_copy_ram) {
1247 _gsg->framebuffer_copy_to_ram(texture, 0, old_target_tex_page,
1248 old_page_dr, buffer);
1249 } else {
1250 _gsg->framebuffer_copy_to_texture(texture, 0, old_target_tex_page,
1251 old_page_dr, buffer);
1252 }
1253 }
1254 }
1255 }
1256 }
1257 }
1258}
1259
1260/**
1261 * Called internally when the window is in render-to-a-texture mode and we are
1262 * in the process of rendering the six faces of a cube map, or any other
1263 * multi-page texture. This should do whatever needs to be done to switch the
1264 * buffer to the indicated page.
1265 */
1269
1270/**
1271 * This function will be called within the draw thread after end_frame() has
1272 * been called on all windows, to initiate the exchange of the front and back
1273 * buffers.
1274 *
1275 * This should instruct the window to prepare for the flip at the next video
1276 * sync, but it should not wait.
1277 *
1278 * We have the two separate functions, begin_flip() and end_flip(), to make it
1279 * easier to flip all of the windows at the same time.
1280 */
1282begin_flip() {
1283}
1284
1285/**
1286 * This function will be called within the draw thread after end_frame() has
1287 * been called on all windows, to initiate the exchange of the front and back
1288 * buffers.
1289 *
1290 * This should instruct the window to prepare for the flip when it is command
1291 * but not actually flip
1292 *
1293 */
1295ready_flip() {
1296}
1297
1298/**
1299 * This function will be called within the draw thread after begin_flip() has
1300 * been called on all windows, to finish the exchange of the front and back
1301 * buffers.
1302 *
1303 * This should cause the window to wait for the flip, if necessary.
1304 */
1306end_flip() {
1307 _flip_ready = false;
1308}
1309
1310/**
1311 * Do whatever processing in the window thread is appropriate for this output
1312 * object each frame.
1313 *
1314 * This function is called only within the window thread.
1315 */
1319
1320/**
1321 * Called internally when the pixel factor changes.
1322 */
1323void GraphicsOutput::
1324pixel_factor_changed() {
1325 if (_has_size) {
1327 }
1328}
1329
1330/**
1331 * Set the delete flag, and do the usual cleanup activities associated with
1332 * that.
1333 */
1334void GraphicsOutput::
1335prepare_for_deletion() {
1336 CDWriter cdata(_cycler, true);
1337 cdata->_active = false;
1338
1339 // If we were rendering directly to texture, we can't delete the buffer
1340 // until all the textures are gone too.
1341 RenderTextures::iterator ri;
1342 for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1343 if ((*ri)._rtm_mode == RTM_bind_or_copy || (*ri)._rtm_mode == RTM_bind_layered) {
1344 _hold_textures.push_back((*ri)._texture);
1345 }
1346 }
1347 cdata->_textures.clear();
1348
1349 _delete_flag = true;
1350
1351 // We have to be sure to remove all of the display regions immediately, so
1352 // that circular reference counts can be cleared up (each display region
1353 // keeps a pointer to a CullResult, which can hold all sorts of pointers).
1355}
1356
1357/**
1358 * If any textures are marked RTM_bind_or_copy, change them to
1359 * RTM_copy_texture. This does not change textures that are set to
1360 * RTM_bind_layered, as layered framebuffers aren't supported with
1361 * RTM_copy_texture.
1362 */
1363void GraphicsOutput::
1364promote_to_copy_texture() {
1365 CDLockedReader cdata(_cycler);
1366 RenderTextures::const_iterator ri;
1367
1368 bool any_bind = false;
1369 for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1370 if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1371 any_bind = true;
1372 break;
1373 }
1374 }
1375 if (any_bind) {
1376 CDWriter cdataw(((GraphicsOutput *)this)->_cycler, cdata, true);
1377 RenderTextures::iterator ri;
1378 for (ri = cdataw->_textures.begin(); ri != cdataw->_textures.end(); ++ri) {
1379 if ((*ri)._rtm_mode == RTM_bind_or_copy) {
1380 (*ri)._rtm_mode = RTM_copy_texture;
1381 }
1382 }
1383 }
1384}
1385
1386/**
1387 * For all textures marked RTM_copy_texture, RTM_copy_ram,
1388 * RTM_triggered_copy_texture, or RTM_triggered_copy_ram, do the necessary
1389 * copies.
1390 *
1391 * Returns true if all copies are successful, false otherwise.
1392 */
1393bool GraphicsOutput::
1394copy_to_textures() {
1395 bool okflag = true;
1396
1397 CDReader cdata(_cycler);
1398 RenderTextures::const_iterator ri;
1399 for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
1400 RenderTextureMode rtm_mode = (*ri)._rtm_mode;
1401 if ((rtm_mode == RTM_none) || (rtm_mode == RTM_bind_or_copy)) {
1402 continue;
1403 }
1404
1405 Texture *texture = (*ri)._texture;
1406 PStatTimer timer(_copy_texture_pcollector);
1407
1408 if ((rtm_mode == RTM_copy_texture)||
1409 (rtm_mode == RTM_copy_ram)||
1410 ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
1411 ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
1412 if (display_cat.is_debug()) {
1413 display_cat.debug()
1414 << "Copying texture for " << get_name() << " at frame end.\n";
1415 display_cat.debug()
1416 << "target_tex_page = " << _target_tex_page << "\n";
1417 }
1418 RenderTexturePlane plane = (*ri)._plane;
1420 if (plane == RTP_color) {
1421 buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
1423 }
1424
1425 bool copied = false;
1426 DisplayRegion *dr = _overlay_display_region;
1427 if (_prev_page_dr != nullptr) {
1428 dr = _prev_page_dr;
1429 }
1430
1431 if (plane == RTP_color && _fb_properties.is_stereo()) {
1432 // We've got two texture views to copy.
1433 RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
1434 RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
1435
1436 if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1437 copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1438 dr, left);
1439 copied = _gsg->framebuffer_copy_to_ram(texture, 1, _target_tex_page,
1440 dr, right) && copied;
1441 } else {
1442 copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1443 dr, left);
1444 copied = _gsg->framebuffer_copy_to_texture(texture, 1, _target_tex_page,
1445 dr, right) && copied;
1446 }
1447 } else {
1448 if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
1449 copied = _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
1450 dr, buffer);
1451 } else {
1452 copied = _gsg->framebuffer_copy_to_texture(texture, 0, _target_tex_page,
1453 dr, buffer);
1454 }
1455 }
1456 if (!copied) {
1457 okflag = false;
1458 }
1459 }
1460 }
1461 if (_trigger_copy != nullptr) {
1462 _trigger_copy->set_result(nullptr);
1463 _trigger_copy = nullptr;
1464 }
1465
1466 return okflag;
1467}
1468
1469/**
1470 * Generates a GeomVertexData for a texture card.
1471 */
1472PT(GeomVertexData) GraphicsOutput::
1473create_texture_card_vdata(int x, int y) {
1474 PN_stdfloat xhi = 1.0;
1475 PN_stdfloat yhi = 1.0;
1476
1477 if (Texture::get_textures_power_2() != ATS_none) {
1478 int xru = Texture::up_to_power_2(x);
1479 int yru = Texture::up_to_power_2(y);
1480 xhi = (x * 1.0f) / xru;
1481 yhi = (y * 1.0f) / yru;
1482 }
1483
1485
1486 PT(GeomVertexData) vdata = new GeomVertexData
1487 ("card", format, Geom::UH_static);
1488
1489 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
1490 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
1491 GeomVertexWriter normal(vdata, InternalName::get_normal());
1492
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 vertex.add_data3(LVertex::rfu( 1.0f, 0.0f, -1.0f));
1497
1498 texcoord.add_data2( 0.0f, yhi);
1499 texcoord.add_data2( 0.0f, 0.0f);
1500 texcoord.add_data2( xhi, yhi);
1501 texcoord.add_data2( xhi, 0.0f);
1502
1503 normal.add_data3(LVector3::back());
1504 normal.add_data3(LVector3::back());
1505 normal.add_data3(LVector3::back());
1506 normal.add_data3(LVector3::back());
1507
1508 return vdata;
1509}
1510
1511/**
1512 * Called by the DisplayRegion constructor to add the new DisplayRegion to the
1513 * list.
1514 */
1515DisplayRegion *GraphicsOutput::
1516add_display_region(DisplayRegion *display_region) {
1517 LightMutexHolder holder(_lock);
1518 CDWriter cdata(_cycler, true);
1519 cdata->_active_display_regions_stale = true;
1520
1521 _total_display_regions.push_back(display_region);
1522
1523 return display_region;
1524}
1525
1526/**
1527 * Internal implementation of remove_display_region. Assumes the lock is
1528 * already held.
1529 */
1530bool GraphicsOutput::
1531do_remove_display_region(DisplayRegion *display_region) {
1532 nassertr(display_region != _overlay_display_region, false);
1533
1534 PT(DisplayRegion) drp = display_region;
1535 TotalDisplayRegions::iterator dri =
1536 find(_total_display_regions.begin(), _total_display_regions.end(), drp);
1537 if (dri != _total_display_regions.end()) {
1538 // Let's aggressively clean up the display region too.
1539 display_region->cleanup();
1540 display_region->_window = nullptr;
1541 _total_display_regions.erase(dri);
1542
1543 OPEN_ITERATE_ALL_STAGES(_cycler) {
1544 CDStageWriter cdata(_cycler, pipeline_stage);
1545 cdata->_active_display_regions_stale = true;
1546 }
1547 CLOSE_ITERATE_ALL_STAGES(_cycler);
1548 return true;
1549 }
1550
1551 return false;
1552}
1553
1554/**
1555 * Re-sorts the list of active DisplayRegions within the window.
1556 */
1557void GraphicsOutput::
1558do_determine_display_regions(GraphicsOutput::CData *cdata) {
1559 cdata->_active_display_regions_stale = false;
1560
1561 cdata->_active_display_regions.clear();
1562 cdata->_active_display_regions.reserve(_total_display_regions.size());
1563
1564 int index = 0;
1565 TotalDisplayRegions::const_iterator dri;
1566 for (dri = _total_display_regions.begin();
1567 dri != _total_display_regions.end();
1568 ++dri) {
1569 DisplayRegion *display_region = (*dri);
1570 if (display_region->is_active()) {
1571 cdata->_active_display_regions.push_back(display_region);
1572 display_region->set_active_index(index);
1573 ++index;
1574 } else {
1575 display_region->set_active_index(-1);
1576 }
1577 }
1578
1579 std::stable_sort(cdata->_active_display_regions.begin(),
1580 cdata->_active_display_regions.end(),
1582}
1583
1584/**
1585 * Parses one of the keywords in the red-blue-stereo-colors Config.prc
1586 * variable, and returns the corresponding bitmask.
1587 *
1588 * These bitmask values are taken from ColorWriteAttrib.
1589 */
1590unsigned int GraphicsOutput::
1591parse_color_mask(const string &word) {
1592 unsigned int result = 0;
1593 vector_string components;
1594 tokenize(word, components, "|");
1595
1596 vector_string::const_iterator ci;
1597 for (ci = components.begin(); ci != components.end(); ++ci) {
1598 string w = downcase(*ci);
1599 if (w == "red" || w == "r") {
1600 result |= 0x001;
1601
1602 } else if (w == "green" || w == "g") {
1603 result |= 0x002;
1604
1605 } else if (w == "blue" || w == "b") {
1606 result |= 0x004;
1607
1608 } else if (w == "yellow" || w == "y") {
1609 result |= 0x003;
1610
1611 } else if (w == "magenta" || w == "m") {
1612 result |= 0x005;
1613
1614 } else if (w == "cyan" || w == "c") {
1615 result |= 0x006;
1616
1617 } else if (w == "alpha" || w == "a") {
1618 result |= 0x008;
1619
1620 } else if (w == "off") {
1621
1622 } else {
1623 display_cat.warning()
1624 << "Invalid color in red-blue-stereo-colors: " << (*ci) << "\n";
1625 }
1626 }
1627
1628 return result;
1629}
1630
1631/**
1632 *
1633 */
1634GraphicsOutput::CData::
1635CData() {
1636 // The default is *not* active, so the entire pipeline stage is initially
1637 // populated with inactive outputs. Pipeline stage 0 is set to active in
1638 // the constructor.
1639 _active = false;
1640 _one_shot_frame = -1;
1641 _active_display_regions_stale = false;
1642}
1643
1644/**
1645 *
1646 */
1647GraphicsOutput::CData::
1648CData(const GraphicsOutput::CData &copy) :
1649 _textures(copy._textures),
1650 _textures_seq(copy._textures_seq),
1651 _active(copy._active),
1652 _one_shot_frame(copy._one_shot_frame),
1653 _active_display_regions(copy._active_display_regions),
1654 _active_display_regions_stale(copy._active_display_regions_stale)
1655{
1656}
1657
1658/**
1659 *
1660 */
1661CycleData *GraphicsOutput::CData::
1662make_copy() const {
1663 return new CData(*this);
1664}
1665
1666/**
1667 *
1668 */
1669std::ostream &
1670operator << (std::ostream &out, GraphicsOutput::FrameMode fm) {
1671 switch (fm) {
1672 case GraphicsOutput::FM_render:
1673 return out << "render";
1674 case GraphicsOutput::FM_parasite:
1675 return out << "parasite";
1676 case GraphicsOutput::FM_refresh:
1677 return out << "refresh";
1678 }
1679
1680 return out << "(**invalid GraphicsOutput::FrameMode(" << (int)fm << ")**)";
1681}
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.
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.
is_stereo
Returns true if this is a StereoDisplayRegion, false otherwise.
set_camera
Sets the camera that is associated with this DisplayRegion.
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.
get_window
Returns the GraphicsOutput that this DisplayRegion is ultimately associated with, or NULL if no windo...
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.
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...
get_engine
Returns the graphics engine that created this output.
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.
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...
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...
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:599
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.
A perspective-type lens: a normal camera.
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
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:72
set_compression
Requests that this particular Texture be compressed when it is loaded into texture memory.
Definition texture.h:414
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition texture.h:371
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:592
set_wrap_u
This setting determines what happens when the texture is sampled with a U value outside the range 0....
Definition texture.h:379
set_num_views
Sets the number of "views" within a texture.
Definition texture.h:355
get_num_views
Returns the number of "views" in the texture.
Definition texture.h:355
get_texture_type
Returns the overall interpretation of the texture.
Definition texture.h:366
set_format
Changes the format value for the texture components.
Definition texture.h:371
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:418
void setup_cube_map()
Sets the texture as an empty cube map texture with no dimensions.
Definition texture.I:156
get_z_size
Returns the depth of the texture image in texels.
Definition texture.h:351
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:1864
void clear_ram_image()
Discards the current system-RAM image.
Definition texture.I:1440
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:383
set_component_type
Changes the data value for the texture components.
Definition texture.h:375
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.
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.