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}
Defines the details about the Collectors: the name, the suggested color, etc.
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.
This is our own Panda specialization on the default STL map.
Definition pmap.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.