Panda3D
Loading...
Searching...
No Matches
pStatView.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 pStatView.cxx
10 * @author drose
11 * @date 2000-07-10
12 */
13
14#include "pStatView.h"
15
16#include "pStatFrameData.h"
17#include "pStatCollectorDef.h"
18
19#include <algorithm>
20#include <vector>
21
22/**
23 * This class is used within this module only--in fact, within
24 * PStatView::set_to_frame() only--to help collect event data out of the
25 * PStatFrameData object and boil it down to a list of elapsed times.
26 */
27class FrameSample {
28public:
29 void start(double time, FrameSample *started) {
30 // Keep track of nested start/stop pairs. We only consider the outer one.
31 if (_started++ > 0) {
32 return;
33 }
34
35 nassertv(!_pushed);
36 _net_time -= time;
37 push_all(time, started);
38 nassertv(_next == nullptr && _prev == nullptr);
39 _prev = started->_prev;
40 _next = started;
41 _prev->_next = this;
42 started->_prev = this;
43 }
44
45 void stop(double time, FrameSample *started) {
46 nassertv(_started > 0);
47 if (--_started > 0) {
48 return;
49 }
50
51 nassertv(_next != nullptr && _prev != nullptr);
52
53 if (_pushed) {
54 _prev->_next = _next;
55 _next->_prev = _prev;
56 _next = _prev = nullptr;
57 } else {
58 _net_time += time;
59 _prev->_next = _next;
60 _next->_prev = _prev;
61 _next = _prev = nullptr;
62 pop_one(time, started);
63 }
64 }
65
66private:
67 void push(double time) {
68 if (!_pushed) {
69 _pushed = true;
70 if (_started > 0) {
71 _net_time += time;
72 }
73 }
74 }
75
76 void pop(double time) {
77 if (_pushed) {
78 _pushed = false;
79 if (_started > 0) {
80 _net_time -= time;
81 }
82 }
83 }
84
85 void push_all(double time, FrameSample *started) {
86 for (FrameSample *sample = started->_next;
87 sample != started; sample = sample->_next) {
88 sample->push(time);
89 }
90 }
91
92 void pop_one(double time, FrameSample *started) {
93 for (FrameSample *sample = started->_prev;
94 sample != started; sample = sample->_prev) {
95 if (sample->_pushed) {
96 sample->pop(time);
97 return;
98 }
99 }
100 }
101
102public:
103 FrameSample *_next = nullptr;
104 FrameSample *_prev = nullptr;
105 double _net_time = 0.0;
106 int _started = 0;
107 int _count = 0;
108 bool _pushed = false;
109 bool _is_new = false;
110};
111
112/**
113 *
114 */
115PStatView::
116PStatView() {
117 _constraint = 0;
118 _show_level = false;
119 _all_collectors_known = false;
120 _level_index = 0;
121}
122
123/**
124 *
125 */
126PStatView::
127~PStatView() {
128 clear_levels();
129}
130
131/**
132 * Changes the focus of the View. By default, the View reports the entire
133 * time for the frame, and all of the Collectors that are directly parented to
134 * "Frame". By constraining the view to a particular collector, you cause the
135 * View to zoom in on that collector's data, reporting only the collector and
136 * its immediate parents.
137 *
138 * When you constrain the view, you may also specify whether the view should
139 * show time data or level data for the indicated collector. If level data,
140 * it reports the levels for the collector, and all of its children;
141 * otherwise, it collects the elapsed time.
142 *
143 * Changing the constraint causes the current frame's data to become
144 * invalidated; you must then call set_to_frame() again to get any useful data
145 * out.
146 */
148constrain(int collector, bool show_level) {
149 _constraint = collector;
150 _show_level = show_level;
151 clear_levels();
152}
153
154/**
155 * Restores the view to the full frame. This is equivalent to calling
156 * constrain(0).
157 */
159unconstrain() {
160 constrain(0, false);
161}
162
163/**
164 *
165 */
166void PStatView::
167set_thread_data(const PStatThreadData *thread_data) {
168 _thread_data = thread_data;
169 _client_data = thread_data->get_client_data();
170 clear_levels();
171 _all_collectors_known = false;
172}
173
174/**
175 * Supplies the View with the data for the current frame. This causes the
176 * View to update all of its internal data to reflect the frame's data,
177 * subject to the current constraint.
178 *
179 * It is possible that calling this will increase the total number of reported
180 * levels (for instance, if this frame introduced a new collector that hadn't
181 * been active previously). In this case, the caller must update its display
182 * or whatever to account for the new level.
183 */
185set_to_frame(const PStatFrameData &frame_data) {
186 nassertv(!_thread_data.is_null());
187 nassertv(!_client_data.is_null());
188
189 if (_show_level) {
190 update_level_data(frame_data);
191 } else {
192 update_time_data(frame_data);
193 }
194}
195
196
197/**
198 * After a call to set_to_frame(), this returns true if all collectors in the
199 * FrameData are known by the PStatsData object, or false if some are still
200 * unknown (even those that do not appear in the view).
201 */
203all_collectors_known() const {
204 return _all_collectors_known;
205}
206
207/**
208 * Returns the total value accounted for by the frame (or by whatever
209 * Collector we are constrained to). This is the sum of all of the individual
210 * levels' get_net_value() value.
211 */
213get_net_value() const {
214 double net = 0.0;
215 Levels::const_iterator li;
216 for (li = _levels.begin(); li != _levels.end(); ++li) {
217 net += (*li).second->_value_alone;
218 }
219
220 return net;
221}
222
223/**
224 * Returns a pointer to the level that corresponds to the Collector we've
225 * constrained to. This is the top of a graph of levels; typically the next
226 * level down--the children of this level--will be the levels you want to
227 * display to the user.
228 */
231 return get_level(_constraint);
232}
233
234/**
235 * Returns true if there is a level defined for the particular collector,
236 * false otherwise.
237 */
239has_level(int collector) const {
240 Levels::const_iterator li;
241 li = _levels.find(collector);
242 return (li != _levels.end());
243}
244
245/**
246 * Returns a pointer to the level that corresponds to the indicated Collector.
247 * If there is no such level in the view, one will be created--use with
248 * caution. Check has_level() first if you don't want this behavior.
249 */
251get_level(int collector) {
252 Levels::const_iterator li;
253 li = _levels.find(collector);
254 if (li != _levels.end()) {
255 return (*li).second;
256 }
257
258 PStatViewLevel *level = new PStatViewLevel;
259 level->_collector = collector;
260 level->_parent = nullptr;
261 _levels[collector] = level;
262
263 reset_level(level);
264 return level;
265}
266
267/**
268 * The implementation of set_to_frame() for views that show elapsed time.
269 */
270void PStatView::
271update_time_data(const PStatFrameData &frame_data) {
272 int num_events = frame_data.get_num_events();
273 int num_collectors = _client_data->get_num_collectors();
274
275 typedef std::vector<FrameSample> Samples;
276 Samples samples(num_collectors);
277
278 // Keep a linked list of started samples.
279 FrameSample started;
280 started._next = &started;
281 started._prev = &started;
282
283 _all_collectors_known = true;
284
285 int new_collectors = 0;
286 int i;
287 for (i = 0; i < num_events; i++) {
288 int collector_index = frame_data.get_time_collector(i);
289 bool is_start = frame_data.is_start(i);
290
291 if (!_client_data->has_collector(collector_index)) {
292 _all_collectors_known = false;
293
294 } else {
295 nassertv(collector_index >= 0 && collector_index < num_collectors);
296
297 if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
298 // Here's a data point we care about: anything at constraint level or
299 // below.
300 if (is_start) {
301 samples[collector_index].start(frame_data.get_time(i), &started);
302 samples[collector_index]._count++;
303 } else {
304 // A "stop" in the middle of a frame implies a "start" since time
305 // 0 (that is, since the first data point in the frame).
306 if (samples[collector_index]._started == 0) {
307 samples[collector_index].start(frame_data.get_time(0), &started);
308 }
309 samples[collector_index].stop(frame_data.get_time(i), &started);
310 }
311
312 if (!samples[collector_index]._is_new) {
313 samples[collector_index]._is_new = true;
314 ++new_collectors;
315 }
316 }
317 }
318 }
319
320 // Make sure everything is stopped.
321 Samples::iterator si;
322 for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
323 if ((*si)._started > 0) {
324 (*si).stop(frame_data.get_end(), &started);
325 }
326 }
327
328 nassertv(started._next == &started && started._prev == &started);
329
330 bool any_new_levels = false;
331
332 // Now match these samples we got up with those we already had in the
333 // levels.
334 Levels::iterator li, lnext;
335 li = _levels.begin();
336 while (li != _levels.end()) {
337 // Be careful while traversing a container and calling functions that
338 // could modify that container.
339 lnext = li;
340 ++lnext;
341
342 PStatViewLevel *level = (*li).second;
343 if (reset_level(level)) {
344 any_new_levels = true;
345 }
346
347 int collector_index = level->_collector;
348 if (samples[collector_index]._is_new) {
349 level->_value_alone = samples[collector_index]._net_time;
350 level->_count = samples[collector_index]._count;
351 samples[collector_index]._is_new = false;
352 --new_collectors;
353 }
354
355 li = lnext;
356 }
357
358 // Finally, any samples left over in the got_samples set are new collectors
359 // that we need to add to the Levels list.
360 if (new_collectors > 0) {
361 any_new_levels = true;
362
363 for (int collector_index = 0; collector_index < num_collectors; ++collector_index) {
364 if (samples[collector_index]._is_new) {
365 PStatViewLevel *level = get_level(collector_index);
366 level->_value_alone = samples[collector_index]._net_time;
367 level->_count = samples[collector_index]._count;
368 }
369 }
370 }
371
372 if (any_new_levels) {
373 _level_index++;
374 }
375}
376
377/**
378 * The implementation of set_to_frame() for views that show level values.
379 */
380void PStatView::
381update_level_data(const PStatFrameData &frame_data) {
382 _all_collectors_known = true;
383
384
385 // This tracks the set of level values we got.
386 typedef pmap<int, double> GotValues;
387 GotValues net_values;
388
389 int i;
390 int num_levels = frame_data.get_num_levels();
391 for (i = 0; i < num_levels; i++) {
392 int collector_index = frame_data.get_level_collector(i);
393 double value = frame_data.get_level(i);
394
395 if (!_client_data->has_collector(collector_index)) {
396 _all_collectors_known = false;
397
398 } else {
399 if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
400 net_values[collector_index] = value;
401 }
402 }
403 }
404
405 // Now that we've counted up the net level for each collector, compute the
406 // level for each collector alone by subtracting out each child from its
407 // parents. If a parent has no data, nothing is subtracted.
408 GotValues alone_values = net_values;
409
410 GotValues::iterator gi;
411 for (gi = net_values.begin(); gi != net_values.end(); ++gi) {
412 int collector_index = (*gi).first;
413 double value = (*gi).second;
414
415 // Walk up to the top, but stop when we find a parent with actual data.
416 while (collector_index != 0 && collector_index != _constraint) {
417 const PStatCollectorDef &def =
418 _client_data->get_collector_def(collector_index);
419 int parent_index = def._parent_index;
420 GotValues::iterator pi = alone_values.find(parent_index);
421 if (pi != alone_values.end()) {
422 // The parent has data; subtract it.
423 (*pi).second -= value;
424 break;
425 }
426 collector_index = parent_index;
427 }
428 }
429
430
431 bool any_new_levels = false;
432
433 // Now match these samples we got up with those we already had in the
434 // levels.
435 Levels::iterator li, lnext;
436 li = _levels.begin();
437 while (li != _levels.end()) {
438 // Be careful while traversing a container and calling functions that
439 // could modify that container.
440 lnext = li;
441 ++lnext;
442
443 PStatViewLevel *level = (*li).second;
444 if (reset_level(level)) {
445 any_new_levels = true;
446 }
447
448 int collector_index = level->_collector;
449 GotValues::iterator gi;
450 gi = alone_values.find(collector_index);
451 if (gi != alone_values.end()) {
452 level->_value_alone = (*gi).second;
453 alone_values.erase(gi);
454 }
455
456 li = lnext;
457 }
458
459 // Finally, any values left over in the alone_values set are new collectors
460 // that we need to add to the Levels list.
461 if (!alone_values.empty()) {
462 any_new_levels = true;
463
464 GotValues::const_iterator gi;
465 for (gi = alone_values.begin(); gi != alone_values.end(); ++gi) {
466 int collector_index = (*gi).first;
467 PStatViewLevel *level = get_level(collector_index);
468 level->_value_alone = (*gi).second;
469 }
470 }
471
472 if (any_new_levels) {
473 _level_index++;
474 }
475}
476
477/**
478 * Resets all the levels that have been defined so far.
479 */
480void PStatView::
481clear_levels() {
482 Levels::iterator li;
483 for (li = _levels.begin(); li != _levels.end(); ++li) {
484 delete (*li).second;
485 }
486 _levels.clear();
487}
488
489/**
490 * Resets the total value of the Level to zero, and also makes sure it is
491 * parented to the right Level corresponding to its Collector's parent. Since
492 * the client might change its mind from time to time about who the Collector
493 * is parented to, we have to update this dynamically.
494 *
495 * Returns true if any change was made to the level's hierarchy, false
496 * otherwise.
497 */
498bool PStatView::
499reset_level(PStatViewLevel *level) {
500 bool any_changed = false;
501 level->_value_alone = 0.0;
502 level->_count = 0;
503
504 if (level->_collector == _constraint) {
505 return false;
506 }
507
508 if (_client_data->has_collector(level->_collector)) {
509 int parent_index =
510 _client_data->get_collector_def(level->_collector)._parent_index;
511
512 if (level->_parent == nullptr) {
513 // This level didn't know its parent before, but now it does.
514 PStatViewLevel *parent_level = get_level(parent_index);
515 nassertr(parent_level != level, true);
516
517 level->_parent = parent_level;
518 parent_level->_children.push_back(level);
519 parent_level->sort_children(_client_data);
520 any_changed = true;
521
522 } else if (level->_parent->_collector != parent_index) {
523 // This level knew about its parent, but now it's something different.
524 PStatViewLevel *old_parent_level = level->_parent;
525 nassertr(old_parent_level != level, true);
526
527 if (parent_index != 0) {
528 PStatViewLevel *new_parent_level = get_level(parent_index);
529 nassertr(new_parent_level != level, true);
530 level->_parent = new_parent_level;
531 new_parent_level->_children.push_back(level);
532 new_parent_level->sort_children(_client_data);
533 } else {
534 level->_parent = nullptr;
535 }
536
537 PStatViewLevel::Children::iterator ci =
538 find(old_parent_level->_children.begin(),
539 old_parent_level->_children.end(),
540 level);
541
542 nassertr(ci != old_parent_level->_children.end(), true);
543 old_parent_level->_children.erase(ci);
544 any_changed = true;
545 }
546 }
547
548 return any_changed;
549}
Contains the raw timing and level data for a single frame.
double get_end() const
Returns the time of the last data point in the frame data.
double get_time(size_t n) const
Returns the timestamp of the nth event, in seconds elapsed since some undefined epoch (which is guara...
int get_time_collector(size_t n) const
Returns the index of the collector associated with the nth event.
size_t get_num_levels() const
Returns the number of individual level values stored in the FrameData.
bool is_start(size_t n) const
Returns true if the nth event represents a start event, or false if it represents a stop event.
size_t get_num_events() const
Returns the number of individual events stored in the FrameData.
double get_level(size_t n) const
Returns the height of the nth level value.
int get_level_collector(size_t n) const
Returns the index of the collector associated with the nth level value.
A collection of FrameData structures for recently-received frames within a particular thread.
const PStatClientData * get_client_data() const
Returns a pointer to the ClientData structure associated with this data.
This is a single level value, or band of color, within a View.
void sort_children(const PStatClientData *client_data)
Sorts the children of this view level into order as specified by the client's sort index.
double get_net_value() const
Returns the total value accounted for by the frame (or by whatever Collector we are constrained to).
const PStatViewLevel * get_top_level()
Returns a pointer to the level that corresponds to the Collector we've constrained to.
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
void unconstrain()
Restores the view to the full frame.
void constrain(int collector, bool show_level)
Changes the focus of the View.
bool all_collectors_known() const
After a call to set_to_frame(), this returns true if all collectors in the FrameData are known by the...
bool has_level(int collector) const
Returns true if there is a level defined for the particular collector, false otherwise.
PStatViewLevel * get_level(int collector)
Returns a pointer to the level that corresponds to the indicated Collector.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.