Panda3D
Loading...
Searching...
No Matches
pStatStripChart.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 pStatStripChart.cxx
10 * @author drose
11 * @date 2000-07-15
12 */
13
14#include "pStatStripChart.h"
15#include "pStatClientData.h"
16#include "pStatMonitor.h"
17
18#include "pStatFrameData.h"
19#include "pStatCollectorDef.h"
20#include "string_utils.h"
21#include "config_pstatclient.h"
22
23#include <algorithm>
24
25using std::max;
26using std::min;
27
28/**
29 *
30 */
31PStatStripChart::
32PStatStripChart(PStatMonitor *monitor, PStatView &view,
33 int thread_index, int collector_index, int xsize, int ysize) :
34 PStatGraph(monitor, xsize, ysize),
35 _thread_index(thread_index),
36 _view(view),
37 _collector_index(collector_index)
38{
39 _scroll_mode = pstats_scroll_mode;
40 _average_mode = false;
41
42 _next_frame = 0;
43 _first_data = true;
44 _cursor_pixel = 0;
45
46 _time_width = 20.0;
47 _value_height = 1.0/10.0;
48 _start_time = 0.0;
49
50 _level_index = -1;
51 _title_unknown = true;
52
53 const PStatClientData *client_data = _monitor->get_client_data();
54 if (client_data->has_collector(_collector_index)) {
55 const PStatCollectorDef &def = client_data->get_collector_def(_collector_index);
56 _unit_name = def._level_units;
57 }
58
59 set_default_vertical_scale();
60}
61
62/**
63 *
64 */
65PStatStripChart::
66~PStatStripChart() {
67}
68
69/**
70 * Indicates that new data has become available.
71 */
73new_data(int frame_number) {
74 // If the new frame is older than the last one we've drawn, we'll need to
75 // back up and redraw it. This can happen when frames arrive out of order
76 // from the client.
77 _next_frame = min(frame_number, _next_frame);
78}
79
80/**
81 * Updates the chart with the latest data.
82 */
84update() {
85 const PStatClientData *client_data = get_monitor()->get_client_data();
86
87 // Don't bother to update the thread data until we know at least something
88 // about the collectors and threads.
89 if (client_data->get_num_collectors() != 0 &&
90 client_data->get_num_threads() != 0) {
91 const PStatThreadData *thread_data = _view.get_thread_data();
92 if (!thread_data->is_empty()) {
93 int latest = thread_data->get_latest_frame_number();
94
95 if (latest > _next_frame) {
96 draw_frames(_next_frame, latest);
97 }
98 _next_frame = latest;
99
100 // Clean out the old data.
101 double oldest_time =
102 thread_data->get_frame(latest).get_start() - _time_width;
103
104 Data::iterator di;
105 di = _data.begin();
106 while (di != _data.end() &&
107 thread_data->get_frame((*di).first).get_start() < oldest_time) {
108 dec_label_usage((*di).second);
109 _data.erase(di);
110 di = _data.begin();
111 }
112 }
113 }
114
115 if (_level_index != _view.get_level_index()) {
116 update_labels();
117 }
118
119 idle();
120}
121
122/**
123 * Returns true if the chart has seen its first data appear on it, false if it
124 * is still a virgin chart.
125 */
127first_data() const {
128 return _first_data;
129}
130
131/**
132 * Changes the collector represented by this strip chart. This may force a
133 * redraw.
134 */
136set_collector_index(int collector_index) {
137 if (_collector_index != collector_index) {
138 _collector_index = collector_index;
139 _title_unknown = true;
140 _data.clear();
141 clear_label_usage();
142 force_redraw();
143 update_labels();
144 }
145}
146
147/**
148 * Sets the vertical scale according to the suggested scale of the base
149 * collector, if any, or to center the target frame rate bar otherwise.
150 */
153 const PStatClientData *client_data = _monitor->get_client_data();
154 if (client_data->has_collector(_collector_index)) {
155 const PStatCollectorDef &def =
156 client_data->get_collector_def(_collector_index);
157 if (def._suggested_scale != 0.0) {
158 set_vertical_scale(def._suggested_scale);
159 return;
160 }
161 }
162
164}
165
166/**
167 * Sets the vertical scale to make all the data visible.
168 */
171 const PStatThreadData *thread_data = _view.get_thread_data();
172
173 double max_value = 0.0;
174
175 int frame_number = -1;
176 for (int x = 0; x <= _xsize; x++) {
177 double time = pixel_to_timestamp(x);
178 frame_number =
179 thread_data->get_frame_number_at_time(time, frame_number);
180
181 if (thread_data->has_frame(frame_number)) {
182 double net_value = get_net_value(frame_number);
183 max_value = max(max_value, net_value);
184 }
185 }
186
187 // Ok, now we know what the max value visible in the chart is. Choose a
188 // scale that will show all of this sensibly.
189 if (max_value == 0.0) {
191 } else {
192 set_vertical_scale(max_value * 1.1);
193 }
194}
195
196/**
197 * Return the collector index associated with the particular band of color at
198 * the indicated pixel location, or -1 if no band of color was at the pixel.
199 */
201get_collector_under_pixel(int xpoint, int ypoint) {
202 // First, we need to know what frame it was; to know that, we need to
203 // determine the time corresponding to the x pixel.
204 double time = pixel_to_timestamp(xpoint);
205
206 // Now use that time to determine the frame.
207 const PStatThreadData *thread_data = _view.get_thread_data();
208
209 // And now we can determine which collector within the frame, based on the
210 // value height.
211 if (_average_mode) {
212 double start_time = pixel_to_timestamp(xpoint);
213 int then_i = thread_data->get_frame_number_at_time(start_time - pstats_average_time);
214 int now_i = thread_data->get_frame_number_at_time(start_time, then_i);
215
216 FrameData fdata;
217 compute_average_pixel_data(fdata, then_i, now_i, start_time);
218 double overall_value = 0.0;
219 int y = get_ysize();
220
221 FrameData::const_iterator fi;
222 for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
223 const ColorData &cd = (*fi);
224 overall_value += cd._net_value;
225 y = height_to_pixel(overall_value);
226 if (y <= ypoint) {
227 return cd._collector_index;
228 }
229 }
230
231 } else {
232 int frame_number = thread_data->get_frame_number_at_time(time);
233 const FrameData &fdata = get_frame_data(frame_number);
234 double overall_value = 0.0;
235 int y = get_ysize();
236
237 FrameData::const_iterator fi;
238 for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
239 const ColorData &cd = (*fi);
240 overall_value += cd._net_value;
241 y = height_to_pixel(overall_value);
242 if (y <= ypoint) {
243 return cd._collector_index;
244 }
245 }
246 }
247
248 return -1;
249}
250
251/**
252 * Returns the text suitable for the title label on the top line.
253 */
256 std::string text;
257
258 _title_unknown = false;
259
260 const PStatClientData *client_data = _monitor->get_client_data();
261 if (client_data->has_collector(_collector_index)) {
262 text = client_data->get_collector_fullname(_collector_index);
263 const PStatCollectorDef &def = client_data->get_collector_def(_collector_index);
264 if (_view.get_show_level()) {
265 if (!def._level_units.empty()) {
266 text += " (" + def._level_units + ")";
267 }
268 } else {
269 text += " time";
270 }
271 } else {
272 _title_unknown = true;
273 }
274
275 if (_thread_index != 0) {
276 if (client_data->has_thread(_thread_index)) {
277 text += " (" + client_data->get_thread_name(_thread_index) + " thread)";
278 } else {
279 _title_unknown = true;
280 }
281 }
282
283 return text;
284}
285
286/**
287 * Returns the text suitable for the total label above the graph.
288 */
291 std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
292 if (get_collector_index() != 0 && !_view.get_show_level()) {
293 const PStatViewLevel *level = _view.get_level(get_collector_index());
294 if (level != nullptr && level->get_count() > 0) {
295 text += " / " + format_string(level->get_count()) + "x";
296 }
297 }
298 return text;
299}
300
301/**
302 * Adds the data from additional into the data from fdata, after applying the
303 * scale weight.
304 */
305void PStatStripChart::
306accumulate_frame_data(FrameData &fdata, const FrameData &additional,
307 double weight) {
308 FrameData::iterator ai;
309 FrameData::const_iterator bi;
310
311 ai = fdata.begin();
312 bi = additional.begin();
313
314 FrameData result;
315
316 if (fdata.size() == additional.size()) {
317 // Start out assuming that fdata and additional contain exactly the same
318 // set of collectors. If we discover otherwise, we'll have to bail at
319 // that point.
320 while (ai != fdata.end() &&
321 (*ai)._collector_index == (*bi)._collector_index) {
322 (*ai)._net_value += ((*bi)._net_value * weight);
323 ++ai;
324 ++bi;
325 }
326
327 if (ai == fdata.end()) {
328 // If we successfully reached the end of the list, great! We're done
329 // without any merging.
330 return;
331 }
332
333 // Otherwise, the two lists weren't identical. In that case, copy the
334 // accumulated data so far and continue from this point with the full-
335 // blown merge.
336 result.reserve(max(fdata.size(), additional.size()));
337 FrameData::const_iterator ci;
338 for (ci = fdata.begin(); ci != ai; ++ci) {
339 result.push_back(*ci);
340 }
341
342 } else {
343 // If the two lists had different lengths, clearly they aren't identical.
344 result.reserve(max(fdata.size(), additional.size()));
345 }
346
347 while (ai != fdata.end() && bi != additional.end()) {
348 if ((*ai)._i < (*bi)._i) {
349 // Here's a data value that's in data, but not in additional.
350 result.push_back(*ai);
351 ++ai;
352
353 } else if ((*bi)._i < (*ai)._i) {
354 // Here's a data value that's in additional, but not in data.
355 ColorData scaled;
356 scaled._collector_index = (*bi)._collector_index;
357 scaled._i = (*bi)._i;
358 scaled._net_value = (*bi)._net_value * weight;
359 result.push_back(scaled);
360 ++bi;
361
362 } else {
363 // Here's a data value that's in both.
364 ColorData combined;
365 combined._collector_index = (*ai)._collector_index;
366 combined._i = (*bi)._i;
367 combined._net_value = (*ai)._net_value + (*bi)._net_value * weight;
368 result.push_back(combined);
369 ++ai;
370 ++bi;
371 }
372 }
373
374 while (ai != fdata.end()) {
375 // Here's a data value that's in data, but not in additional.
376 result.push_back(*ai);
377 ++ai;
378 }
379
380 while (bi != additional.end()) {
381 // Here's a data value that's in additional, but not in data.
382 ColorData scaled;
383 scaled._collector_index = (*bi)._collector_index;
384 scaled._i = (*bi)._i;
385 scaled._net_value = (*bi)._net_value * weight;
386 result.push_back(scaled);
387 ++bi;
388 }
389
390 fdata.swap(result);
391}
392
393/**
394 * Applies the indicated scale to all collector values in data.
395 */
396void PStatStripChart::
397scale_frame_data(FrameData &fdata, double factor) {
398 FrameData::iterator fi;
399 for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
400 (*fi)._net_value *= factor;
401 }
402}
403
404
405/**
406 * Returns the cached FrameData associated with the given frame number. This
407 * describes the lengths of the color bands for a single vertical stripe in
408 * the chart.
409 */
410const PStatStripChart::FrameData &PStatStripChart::
411get_frame_data(int frame_number) {
412 Data::const_iterator di;
413 di = _data.find(frame_number);
414 if (di != _data.end()) {
415 return (*di).second;
416 }
417
418 const PStatThreadData *thread_data = _view.get_thread_data();
419 _view.set_to_frame(thread_data->get_frame(frame_number));
420
421 FrameData &fdata = _data[frame_number];
422
423 const PStatViewLevel *level = _view.get_level(_collector_index);
424 int num_children = level->get_num_children();
425 for (int i = 0; i < num_children; i++) {
426 const PStatViewLevel *child = level->get_child(i);
427 ColorData cd;
428 cd._collector_index = (unsigned short)child->get_collector();
429 cd._i = (unsigned short)i;
430 cd._net_value = child->get_net_value();
431 if (cd._net_value != 0.0) {
432 fdata.push_back(cd);
433 }
434 }
435
436 // Also, there might be some value in the overall Collector that wasn't
437 // included in all of the children.
438 ColorData cd;
439 cd._collector_index = (unsigned short)level->get_collector();
440 cd._i = (unsigned short)num_children;
441 cd._net_value = level->get_value_alone();
442 if (cd._net_value > 0.0) {
443 fdata.push_back(cd);
444 }
445
446 inc_label_usage(fdata);
447
448 return fdata;
449}
450
451/**
452 * Fills the indicated FrameData structure with the color data for the
453 * indicated pixel, averaged over the past pstats_average_time seconds.
454 *
455 * now is the timestamp for which we are computing the data; then_i and now_i
456 * are the frame numbers that bound (now - pstats_average_time) and now. At
457 * function initialization time, these should be at or below the actual
458 * values; they will be incremented as needed by this function. This allows
459 * the function to be called repeatedly for successive pixels.
460 */
461void PStatStripChart::
462compute_average_pixel_data(PStatStripChart::FrameData &result,
463 int &then_i, int &now_i, double now) {
464 result.clear();
465
466 const PStatThreadData *thread_data = _view.get_thread_data();
467 if (thread_data->is_empty() || thread_data->get_oldest_time() > now) {
468 // No data.
469 return;
470 }
471
472 double then = now - pstats_average_time;
473
474 int latest_frame = thread_data->get_latest_frame_number();
475 while (then_i <= latest_frame &&
476 thread_data->get_frame(then_i).get_end() < then) {
477 then_i++;
478 }
479 while (now_i <= latest_frame &&
480 thread_data->get_frame(now_i).get_end() < now) {
481 now_i++;
482 }
483
484 then = max(then, thread_data->get_frame(then_i).get_start());
485
486 // Sum up a weighted average of all of the individual frames we pass.
487
488 // We start with just the portion of frame then_i that actually does fall
489 // within our "then to now" window.
490 accumulate_frame_data(result, get_frame_data(then_i),
491 thread_data->get_frame(then_i).get_end() - then);
492 double last = thread_data->get_frame(then_i).get_end();
493
494 // Then we get all of each of the middle frames.
495 for (int frame_number = then_i + 1;
496 frame_number < now_i;
497 frame_number++) {
498 accumulate_frame_data(result, get_frame_data(frame_number),
499 thread_data->get_frame(frame_number).get_end() - last);
500 last = thread_data->get_frame(frame_number).get_end();
501 }
502
503 // And finally, we get the remainder as now_i.
504 if (last <= now) {
505 accumulate_frame_data(result, get_frame_data(now_i), now - last);
506 }
507
508 scale_frame_data(result, 1.0f / (now - then));
509}
510
511/**
512 * Returns the net value of the chart's collector for the indicated frame
513 * number.
514 */
515double PStatStripChart::
516get_net_value(int frame_number) const {
517 const FrameData &frame =
518 ((PStatStripChart *)this)->get_frame_data(frame_number);
519
520 double net_value = 0.0;
521 FrameData::const_iterator fi;
522 for (fi = frame.begin(); fi != frame.end(); ++fi) {
523 const ColorData &cd = (*fi);
524 net_value += cd._net_value;
525 }
526
527 return net_value;
528}
529
530/**
531 * Computes the average value of the chart's collector over the past
532 * pstats_average_time number of seconds.
533 */
534double PStatStripChart::
535get_average_net_value() const {
536 const PStatThreadData *thread_data = _view.get_thread_data();
537 int now_i, then_i;
538 if (!thread_data->get_elapsed_frames(then_i, now_i)) {
539 return 0.0f;
540 }
541 double now = _time_width + _start_time;
542 double then = now - pstats_average_time;
543
544 int num_frames = now_i - then_i + 1;
545
546 if (_collector_index == 0 && !_view.get_show_level()) {
547 // If we're showing the time for the whole frame, compute this from the
548 // total elapsed time, rather than summing up individual frames. This is
549 // more accurate and exactly matches what is reported by
550 // thread_data->get_frame_rate().
551
552 const PStatFrameData &now_frame_data = thread_data->get_frame(now_i);
553 const PStatFrameData &then_frame_data = thread_data->get_frame(then_i);
554 double now = now_frame_data.get_end();
555 double elapsed_time = (now - then_frame_data.get_start());
556 return elapsed_time / (double)num_frames;
557
558 } else {
559 // On the other hand, if we're showing the time for some sub-frame, we
560 // have to do it the less-accurate way of summing up individual frames,
561 // which might introduce errors if we are missing data for some frames,
562 // but what can you do?
563
564 const PStatThreadData *thread_data = _view.get_thread_data();
565
566 double net_value = 0.0f;
567 double net_time = 0.0f;
568
569 // We start with just the portion of frame then_i that actually does fall
570 // within our "then to now" window (usually some portion of it will).
571 if (thread_data->get_frame(then_i).get_end() > then) {
572 double this_time = (thread_data->get_frame(then_i).get_end() - then);
573 net_value += get_net_value(then_i) * this_time;
574 net_time += this_time;
575 }
576 // Then we get all of each of the remaining frames.
577 for (int frame_number = then_i + 1;
578 frame_number <= now_i;
579 frame_number++) {
580 double this_time = thread_data->get_frame(frame_number).get_net_time();
581 net_value += get_net_value(frame_number) * this_time;
582 net_time += this_time;
583 }
584
585 return net_value / net_time;
586 }
587}
588
589/**
590 * To be called by the user class when the widget size has changed. This
591 * updates the chart's internal data and causes it to issue redraw commands to
592 * reflect the new size.
593 */
594void PStatStripChart::
595changed_size(int xsize, int ysize) {
596 if (xsize != _xsize || ysize != _ysize) {
597 _xsize = xsize;
598 _ysize = ysize;
599 if (_xsize > 0 && _ysize > 0) {
600 _cursor_pixel = xsize * _cursor_pixel / _xsize;
601
602 if (!_first_data) {
603 if (_scroll_mode) {
604 draw_pixels(0, _xsize);
605
606 } else {
607 // Redraw the stats that were there before.
608 double old_start_time = _start_time;
609
610 // Back up a bit to draw the stuff to the right of the cursor.
611 _start_time -= _time_width;
612 draw_pixels(_cursor_pixel, _xsize);
613
614 // Now draw the stuff to the left of the cursor.
615 _start_time = old_start_time;
616 draw_pixels(0, _cursor_pixel);
617 }
618 }
619 }
620 }
621}
622
623/**
624 * To be called by the user class when the whole thing needs to be redrawn for
625 * some reason.
626 */
627void PStatStripChart::
628force_redraw() {
629 if (!_first_data) {
630 draw_pixels(0, _xsize);
631 }
632}
633
634/**
635 * To be called by the user class to cause the chart to reset to empty and
636 * start filling again.
637 */
638void PStatStripChart::
639force_reset() {
640 clear_region();
641 _first_data = true;
642}
643
644
645/**
646 * Should be overridden by the user class to wipe out the entire strip chart
647 * region.
648 */
649void PStatStripChart::
650clear_region() {
651}
652
653/**
654 * Should be overridden by the user class to copy a region of the chart from
655 * one part of the chart to another. This is used to implement scrolling.
656 */
657void PStatStripChart::
658copy_region(int, int, int) {
659}
660
661/**
662 * Should be overridden by the user class. This hook will be called before
663 * drawing any color bars in the strip chart; it gives the pixel range that's
664 * about to be redrawn.
665 */
666void PStatStripChart::
667begin_draw(int, int) {
668}
669
670/**
671 * Should be overridden by the user class to draw a single vertical slice in
672 * the strip chart at the indicated pixel, with the data for the indicated
673 * frame.
674 */
675void PStatStripChart::
676draw_slice(int, int, const PStatStripChart::FrameData &fdata) {
677}
678
679/**
680 * This is similar to draw_slice(), except it should draw a vertical line of
681 * the background color to represent a portion of the chart that has no data.
682 */
683void PStatStripChart::
684draw_empty(int, int) {
685}
686
687/**
688 * This is similar to draw_slice(), except that it should draw the black
689 * vertical stripe that represents the current position when not in scrolling
690 * mode.
691 */
692void PStatStripChart::
693draw_cursor(int) {
694}
695
696/**
697 * Should be overridden by the user class. This hook will be called after
698 * drawing a series of color bars in the strip chart; it gives the pixel range
699 * that was just redrawn.
700 */
701void PStatStripChart::
702end_draw(int, int) {
703}
704
705/**
706 * Should be overridden by the user class to perform any other updates might
707 * be necessary after the color bars have been redrawn. For instance, it
708 * could check the state of _labels_changed, and redraw the labels if it is
709 * true.
710 */
711void PStatStripChart::
712idle() {
713}
714
715
716// STL function object for sorting labels in order by the collector's sort
717// index, used in update_labels(), below.
718class SortCollectorLabels2 {
719public:
720 SortCollectorLabels2(const PStatClientData *client_data) :
721 _client_data(client_data) {
722 }
723 bool operator () (int a, int b) const {
724 return
725 _client_data->get_collector_def(a)._sort >
726 _client_data->get_collector_def(b)._sort;
727 }
728 const PStatClientData *_client_data;
729};
730
731/**
732 * Resets the list of labels.
733 */
734void PStatStripChart::
735update_labels() {
736 const PStatViewLevel *level = _view.get_level(_collector_index);
737 _labels.clear();
738
739 int num_children = level->get_num_children();
740 for (int i = 0; i < num_children; i++) {
741 const PStatViewLevel *child = level->get_child(i);
742 int collector_index = child->get_collector();
743 if (is_label_used(collector_index)) {
744 _labels.push_back(collector_index);
745 }
746 }
747
748 SortCollectorLabels2 sort_labels(get_monitor()->get_client_data());
749 sort(_labels.begin(), _labels.end(), sort_labels);
750
751 int collector_index = level->get_collector();
752 _labels.push_back(collector_index);
753
754 _labels_changed = true;
755 _level_index = _view.get_level_index();
756}
757
758/**
759 * Calls update_guide_bars with parameters suitable to this kind of graph.
760 */
761void PStatStripChart::
762normal_guide_bars() {
763 update_guide_bars(4, _value_height);
764}
765
766
767/**
768 * Draws the levels for the indicated frame range.
769 */
770void PStatStripChart::
771draw_frames(int first_frame, int last_frame) {
772 const PStatThreadData *thread_data = _view.get_thread_data();
773
774 last_frame = min(last_frame, thread_data->get_latest_frame_number());
775
776 if (_first_data) {
777 if (_scroll_mode) {
778 _start_time =
779 thread_data->get_frame(last_frame).get_start() - _time_width;
780 } else {
781 _start_time = thread_data->get_frame(first_frame).get_start();
782 _cursor_pixel = 0;
783 }
784 }
785
786 int first_pixel;
787 if (thread_data->has_frame(first_frame)) {
788 first_pixel =
789 timestamp_to_pixel(thread_data->get_frame(first_frame).get_start());
790 } else {
791 first_pixel = 0;
792 }
793
794 int last_pixel =
795 timestamp_to_pixel(thread_data->get_frame(last_frame).get_start());
796
797 if (_first_data && !_scroll_mode) {
798 first_pixel = min(_cursor_pixel, first_pixel);
799 }
800 _first_data = false;
801
802 if (last_pixel - first_pixel >= _xsize) {
803 // If we're drawing the whole thing all in this one swoop, just start
804 // over.
805 _start_time = thread_data->get_frame(last_frame).get_start() - _time_width;
806 first_pixel = 0;
807 last_pixel = _xsize;
808 }
809
810 if (last_pixel <= _xsize) {
811 // It all fits in one block.
812 _cursor_pixel = last_pixel;
813 draw_pixels(first_pixel, last_pixel);
814
815 } else {
816 if (_scroll_mode) {
817 // In scrolling mode, slide the world back.
818 int slide_pixels = last_pixel - _xsize;
819 copy_region(slide_pixels, first_pixel, 0);
820 first_pixel -= slide_pixels;
821 last_pixel -= slide_pixels;
822 _start_time += (double)slide_pixels / (double)_xsize * _time_width;
823 draw_pixels(first_pixel, last_pixel);
824
825 } else {
826 // In wrapping mode, do it in two blocks.
827 _cursor_pixel = -1;
828 draw_pixels(first_pixel, _xsize);
829 _start_time = pixel_to_timestamp(_xsize);
830 last_pixel -= _xsize;
831 _cursor_pixel = last_pixel;
832 draw_pixels(0, last_pixel);
833 }
834 }
835}
836
837/**
838 * Draws the levels for the indicated pixel range.
839 */
840void PStatStripChart::
841draw_pixels(int first_pixel, int last_pixel) {
842 begin_draw(first_pixel, last_pixel);
843 const PStatThreadData *thread_data = _view.get_thread_data();
844
845 if (_average_mode && !thread_data->is_empty()) {
846 // In average mode, we have to calculate the average value for each pixel.
847 double start_time = pixel_to_timestamp(first_pixel);
848 int then_i = thread_data->get_frame_number_at_time(start_time - pstats_average_time);
849 int now_i = thread_data->get_frame_number_at_time(start_time, then_i);
850 for (int x = first_pixel; x <= last_pixel; x++) {
851 if (x == _cursor_pixel && !_scroll_mode) {
852 draw_cursor(x);
853 } else {
854 FrameData fdata;
855 compute_average_pixel_data(fdata, then_i, now_i, pixel_to_timestamp(x));
856 draw_slice(x, 1, fdata);
857 }
858 }
859
860 } else {
861 // When average mode is false, we are in frame mode; just show the actual
862 // frame data.
863 int frame_number = -1;
864 int x = first_pixel;
865 while (x <= last_pixel) {
866 if (x == _cursor_pixel && !_scroll_mode) {
867 draw_cursor(x);
868 x++;
869
870 } else {
871 double time = pixel_to_timestamp(x);
872 frame_number = thread_data->get_frame_number_at_time(time, frame_number);
873 int w = 1;
874 int stop_pixel = last_pixel;
875 if (!_scroll_mode) {
876 stop_pixel = min(stop_pixel, _cursor_pixel);
877 }
878 while (x + w < stop_pixel &&
879 thread_data->get_frame_number_at_time(pixel_to_timestamp(x + w), frame_number) == frame_number) {
880 w++;
881 }
882 if (thread_data->has_frame(frame_number)) {
883 draw_slice(x, w, get_frame_data(frame_number));
884 } else {
885 draw_empty(x, w);
886 }
887 x += w;
888 }
889 }
890 }
891
892 end_draw(first_pixel, last_pixel);
893}
894
895/**
896 * Erases all elements from the label usage data.
897 */
898void PStatStripChart::
899clear_label_usage() {
900 _label_usage.clear();
901}
902
903/**
904 * Erases the indicated frame data from the current label usage. This
905 * indicates that the given FrameData has fallen off the end of the chart.
906 * This must have been proceeded by an earlier call to inc_label_usage() for
907 * the same FrameData
908 */
909void PStatStripChart::
910dec_label_usage(const FrameData &fdata) {
911 FrameData::const_iterator fi;
912 for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
913 const ColorData &cd = (*fi);
914 nassertv(cd._collector_index < (int)_label_usage.size());
915 nassertv(_label_usage[cd._collector_index] > 0);
916 _label_usage[cd._collector_index]--;
917 if (_label_usage[cd._collector_index] == 0) {
918 // If a label drops out of usage, it's time to regenerate labels.
919 _level_index = -1;
920 }
921 }
922}
923
924/**
925 * Records the labels named in the indicated FrameData in the table of current
926 * labels in use. This should be called when the given FrameData has been
927 * added to the chart; it will increment the reference count for each
928 * collector named in the FrameData. The reference count will eventually be
929 * decremented when dec_label_usage() is called later.
930 */
931void PStatStripChart::
932inc_label_usage(const FrameData &fdata) {
933 FrameData::const_iterator fi;
934 for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
935 const ColorData &cd = (*fi);
936 while (cd._collector_index >= (int)_label_usage.size()) {
937 _label_usage.push_back(0);
938 }
939 nassertv(_label_usage[cd._collector_index] >= 0);
940 _label_usage[cd._collector_index]++;
941 if (_label_usage[cd._collector_index] == 1) {
942 // If a label appears for the first time, it's time to regenerate
943 // labels.
944 _level_index = -1;
945 }
946 }
947}
The data associated with a particular client, but not with any one particular frame or thread: the li...
std::string get_collector_fullname(int index) const
Returns the "full name" of the indicated collector.
bool has_thread(int index) const
Returns true if the indicated thread has been defined by the client already, false otherwise.
const PStatCollectorDef & get_collector_def(int index) const
Returns the nth collector definition.
int get_num_threads() const
Returns the total number of threads the Data knows about.
std::string get_thread_name(int index) const
Returns the name of the indicated thread.
int get_num_collectors() const
Returns the total number of collectors the Data knows about.
bool has_collector(int index) const
Returns true if the indicated collector has been defined by the client already, false otherwise.
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_net_time() const
Returns the total time elapsed for the frame.
double get_start() const
Returns the time of the first data point in the frame data.
This is an abstract base class for several different kinds of graphs that have a few things in common...
Definition pStatGraph.h:33
const std::string & get_guide_bar_unit_name() const
Returns the name of the units to be used for the guide bars if the units type is set to GBU_named | G...
Definition pStatGraph.I:129
int get_guide_bar_units() const
Returns the units that are displayed for the guide bar labels.
Definition pStatGraph.I:111
double get_target_frame_rate() const
Returns the indicated target frame rate in Hz.
Definition pStatGraph.I:74
PStatMonitor * get_monitor() const
Returns the monitor associated with this chart.
Definition pStatGraph.I:18
static std::string format_number(double value)
Returns a string representing the value nicely formatted for its range.
int get_ysize() const
Returns the height of the chart in pixels.
Definition pStatGraph.I:90
This is an abstract class that presents the interface to any number of different front-ends for the s...
const PStatClientData * get_client_data() const
Returns the client data associated with this monitor.
This is an abstract class that presents the interface for drawing a basic strip-chart,...
std::string get_title_text()
Returns the text suitable for the title label on the top line.
int height_to_pixel(double value) const
Converts a value (i.e.
void set_auto_vertical_scale()
Sets the vertical scale to make all the data visible.
void update()
Updates the chart with the latest data.
int get_collector_under_pixel(int xpoint, int ypoint)
Return the collector index associated with the particular band of color at the indicated pixel locati...
std::string get_total_text()
Returns the text suitable for the total label above the graph.
void set_collector_index(int collector_index)
Changes the collector represented by this strip chart.
double pixel_to_timestamp(int x) const
Converts a horizontal pixel offset to a timestamp.
void set_default_vertical_scale()
Sets the vertical scale according to the suggested scale of the base collector, if any,...
void new_data(int frame_number)
Indicates that new data has become available.
int timestamp_to_pixel(double time) const
Converts a timestamp to a horizontal pixel offset.
bool first_data() const
Returns true if the chart has seen its first data appear on it, false if it is still a virgin chart.
int get_collector_index() const
Returns the particular collector whose data this strip chart reflects.
void set_vertical_scale(double value_height)
Changes the value the height of the vertical axis represents.
A collection of FrameData structures for recently-received frames within a particular thread.
bool get_elapsed_frames(int &then_i, int &now_i) const
Computes the oldest frame number not older than pstats_average_time seconds, and the newest frame num...
int get_frame_number_at_time(double time, int hint=-1) const
Returns the frame number of the latest frame not later than the indicated time.
bool is_empty() const
Returns true if the structure contains no frames, false otherwise.
bool has_frame(int frame_number) const
Returns true if we have received data for the indicated frame number from the client and we still hav...
const PStatFrameData & get_frame(int frame_number) const
Returns a FrameData structure associated with the indicated frame number.
int get_latest_frame_number() const
Returns the frame number of the most recent frame stored in the data.
double get_oldest_time() const
Returns the timestamp (in seconds elapsed since connection) of the oldest available frame.
This is a single level value, or band of color, within a View.
const PStatViewLevel * get_child(int n) const
Returns the nth child of this Level/Collector.
double get_net_value() const
Returns the total level value (or elapsed time) represented by this Collector, including all values i...
int get_num_children() const
Returns the number of children of this Level/Collector.
int get_collector() const
Returns the Collector index associated with this level.
int get_count() const
Returns the number of start/stop pairs for this collector.
double get_value_alone() const
Returns the total level value (or elapsed time value) for this Collector, not including any values ac...
A View boils down the frame data to a linear list of times spent in a number of different Collectors,...
Definition pStatView.h:31
int get_level_index() const
Returns an index number that can be used to determine when the set of known levels has changed.
Definition pStatView.I:68
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
bool get_show_level() const
Returns true if we are showing level data, false if time data.
Definition pStatView.I:57
const PStatThreadData * get_thread_data()
Returns the current PStatThreadData associated with the view.
Definition pStatView.I:19
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 vector.
Definition pvector.h:42
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.