Panda3D
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 
25 using std::max;
26 using std::min;
27 
28 /**
29  *
30  */
31 PStatStripChart::
32 PStatStripChart(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  */
65 PStatStripChart::
66 ~PStatStripChart() {
67 }
68 
69 /**
70  * Indicates that new data has become available.
71  */
73 new_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  */
84 update() {
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  */
127 first_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  */
136 set_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) {
190  set_vertical_scale(1.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  */
201 get_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  */
254 std::string PStatStripChart::
255 get_title_text() {
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 true if get_title_text() has never yet returned an answer, false if
288  * it has.
289  */
291 is_title_unknown() const {
292  return _title_unknown;
293 }
294 
295 /**
296  * Adds the data from additional into the data from fdata, after applying the
297  * scale weight.
298  */
299 void PStatStripChart::
300 accumulate_frame_data(FrameData &fdata, const FrameData &additional,
301  double weight) {
302  FrameData::iterator ai;
303  FrameData::const_iterator bi;
304 
305  ai = fdata.begin();
306  bi = additional.begin();
307 
308  FrameData result;
309 
310  if (fdata.size() == additional.size()) {
311  // Start out assuming that fdata and additional contain exactly the same
312  // set of collectors. If we discover otherwise, we'll have to bail at
313  // that point.
314  while (ai != fdata.end() &&
315  (*ai)._collector_index == (*bi)._collector_index) {
316  (*ai)._net_value += ((*bi)._net_value * weight);
317  ++ai;
318  ++bi;
319  }
320 
321  if (ai == fdata.end()) {
322  // If we successfully reached the end of the list, great! We're done
323  // without any merging.
324  return;
325  }
326 
327  // Otherwise, the two lists weren't identical. In that case, copy the
328  // accumulated data so far and continue from this point with the full-
329  // blown merge.
330  result.reserve(max(fdata.size(), additional.size()));
331  FrameData::const_iterator ci;
332  for (ci = fdata.begin(); ci != ai; ++ci) {
333  result.push_back(*ci);
334  }
335 
336  } else {
337  // If the two lists had different lengths, clearly they aren't identical.
338  result.reserve(max(fdata.size(), additional.size()));
339  }
340 
341  while (ai != fdata.end() && bi != additional.end()) {
342  if ((*ai)._i < (*bi)._i) {
343  // Here's a data value that's in data, but not in additional.
344  result.push_back(*ai);
345  ++ai;
346 
347  } else if ((*bi)._i < (*ai)._i) {
348  // Here's a data value that's in additional, but not in data.
349  ColorData scaled;
350  scaled._collector_index = (*bi)._collector_index;
351  scaled._i = (*bi)._i;
352  scaled._net_value = (*bi)._net_value * weight;
353  result.push_back(scaled);
354  ++bi;
355 
356  } else {
357  // Here's a data value that's in both.
358  ColorData combined;
359  combined._collector_index = (*ai)._collector_index;
360  combined._i = (*bi)._i;
361  combined._net_value = (*ai)._net_value + (*bi)._net_value * weight;
362  result.push_back(combined);
363  ++ai;
364  ++bi;
365  }
366  }
367 
368  while (ai != fdata.end()) {
369  // Here's a data value that's in data, but not in additional.
370  result.push_back(*ai);
371  ++ai;
372  }
373 
374  while (bi != additional.end()) {
375  // Here's a data value that's in additional, but not in data.
376  ColorData scaled;
377  scaled._collector_index = (*bi)._collector_index;
378  scaled._i = (*bi)._i;
379  scaled._net_value = (*bi)._net_value * weight;
380  result.push_back(scaled);
381  ++bi;
382  }
383 
384  fdata.swap(result);
385 }
386 
387 /**
388  * Applies the indicated scale to all collector values in data.
389  */
390 void PStatStripChart::
391 scale_frame_data(FrameData &fdata, double factor) {
392  FrameData::iterator fi;
393  for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
394  (*fi)._net_value *= factor;
395  }
396 }
397 
398 
399 /**
400  * Returns the cached FrameData associated with the given frame number. This
401  * describes the lengths of the color bands for a single vertical stripe in
402  * the chart.
403  */
404 const PStatStripChart::FrameData &PStatStripChart::
405 get_frame_data(int frame_number) {
406  Data::const_iterator di;
407  di = _data.find(frame_number);
408  if (di != _data.end()) {
409  return (*di).second;
410  }
411 
412  const PStatThreadData *thread_data = _view.get_thread_data();
413  _view.set_to_frame(thread_data->get_frame(frame_number));
414 
415  FrameData &fdata = _data[frame_number];
416 
417  const PStatViewLevel *level = _view.get_level(_collector_index);
418  int num_children = level->get_num_children();
419  for (int i = 0; i < num_children; i++) {
420  const PStatViewLevel *child = level->get_child(i);
421  ColorData cd;
422  cd._collector_index = (unsigned short)child->get_collector();
423  cd._i = (unsigned short)i;
424  cd._net_value = child->get_net_value();
425  if (cd._net_value != 0.0) {
426  fdata.push_back(cd);
427  }
428  }
429 
430  // Also, there might be some value in the overall Collector that wasn't
431  // included in all of the children.
432  ColorData cd;
433  cd._collector_index = (unsigned short)level->get_collector();
434  cd._i = (unsigned short)num_children;
435  cd._net_value = level->get_value_alone();
436  if (cd._net_value > 0.0) {
437  fdata.push_back(cd);
438  }
439 
440  inc_label_usage(fdata);
441 
442  return fdata;
443 }
444 
445 /**
446  * Fills the indicated FrameData structure with the color data for the
447  * indicated pixel, averaged over the past pstats_average_time seconds.
448  *
449  * now is the timestamp for which we are computing the data; then_i and now_i
450  * are the frame numbers that bound (now - pstats_average_time) and now. At
451  * function initialization time, these should be at or below the actual
452  * values; they will be incremented as needed by this function. This allows
453  * the function to be called repeatedly for successive pixels.
454  */
455 void PStatStripChart::
456 compute_average_pixel_data(PStatStripChart::FrameData &result,
457  int &then_i, int &now_i, double now) {
458  result.clear();
459 
460  const PStatThreadData *thread_data = _view.get_thread_data();
461  if (thread_data->is_empty() || thread_data->get_oldest_time() > now) {
462  // No data.
463  return;
464  }
465 
466  double then = now - pstats_average_time;
467 
468  int latest_frame = thread_data->get_latest_frame_number();
469  while (then_i <= latest_frame &&
470  thread_data->get_frame(then_i).get_end() < then) {
471  then_i++;
472  }
473  while (now_i <= latest_frame &&
474  thread_data->get_frame(now_i).get_end() < now) {
475  now_i++;
476  }
477 
478  then = max(then, thread_data->get_frame(then_i).get_start());
479 
480  // Sum up a weighted average of all of the individual frames we pass.
481 
482  // We start with just the portion of frame then_i that actually does fall
483  // within our "then to now" window.
484  accumulate_frame_data(result, get_frame_data(then_i),
485  thread_data->get_frame(then_i).get_end() - then);
486  double last = thread_data->get_frame(then_i).get_end();
487 
488  // Then we get all of each of the middle frames.
489  for (int frame_number = then_i + 1;
490  frame_number < now_i;
491  frame_number++) {
492  accumulate_frame_data(result, get_frame_data(frame_number),
493  thread_data->get_frame(frame_number).get_end() - last);
494  last = thread_data->get_frame(frame_number).get_end();
495  }
496 
497  // And finally, we get the remainder as now_i.
498  if (last <= now) {
499  accumulate_frame_data(result, get_frame_data(now_i), now - last);
500  }
501 
502  scale_frame_data(result, 1.0f / (now - then));
503 }
504 
505 /**
506  * Returns the net value of the chart's collector for the indicated frame
507  * number.
508  */
509 double PStatStripChart::
510 get_net_value(int frame_number) const {
511  const FrameData &frame =
512  ((PStatStripChart *)this)->get_frame_data(frame_number);
513 
514  double net_value = 0.0;
515  FrameData::const_iterator fi;
516  for (fi = frame.begin(); fi != frame.end(); ++fi) {
517  const ColorData &cd = (*fi);
518  net_value += cd._net_value;
519  }
520 
521  return net_value;
522 }
523 
524 /**
525  * Computes the average value of the chart's collector over the past
526  * pstats_average_time number of seconds.
527  */
528 double PStatStripChart::
529 get_average_net_value() const {
530  const PStatThreadData *thread_data = _view.get_thread_data();
531  int now_i, then_i;
532  if (!thread_data->get_elapsed_frames(then_i, now_i)) {
533  return 0.0f;
534  }
535  double now = _time_width + _start_time;
536  double then = now - pstats_average_time;
537 
538  int num_frames = now_i - then_i + 1;
539 
540  if (_collector_index == 0 && !_view.get_show_level()) {
541  // If we're showing the time for the whole frame, compute this from the
542  // total elapsed time, rather than summing up individual frames. This is
543  // more accurate and exactly matches what is reported by
544  // thread_data->get_frame_rate().
545 
546  const PStatFrameData &now_frame_data = thread_data->get_frame(now_i);
547  const PStatFrameData &then_frame_data = thread_data->get_frame(then_i);
548  double now = now_frame_data.get_end();
549  double elapsed_time = (now - then_frame_data.get_start());
550  return elapsed_time / (double)num_frames;
551 
552  } else {
553  // On the other hand, if we're showing the time for some sub-frame, we
554  // have to do it the less-accurate way of summing up individual frames,
555  // which might introduce errors if we are missing data for some frames,
556  // but what can you do?
557 
558  const PStatThreadData *thread_data = _view.get_thread_data();
559 
560  double net_value = 0.0f;
561  double net_time = 0.0f;
562 
563  // We start with just the portion of frame then_i that actually does fall
564  // within our "then to now" window (usually some portion of it will).
565  if (thread_data->get_frame(then_i).get_end() > then) {
566  double this_time = (thread_data->get_frame(then_i).get_end() - then);
567  net_value += get_net_value(then_i) * this_time;
568  net_time += this_time;
569  }
570  // Then we get all of each of the remaining frames.
571  for (int frame_number = then_i + 1;
572  frame_number <= now_i;
573  frame_number++) {
574  double this_time = thread_data->get_frame(frame_number).get_net_time();
575  net_value += get_net_value(frame_number) * this_time;
576  net_time += this_time;
577  }
578 
579  return net_value / net_time;
580  }
581 }
582 
583 /**
584  * To be called by the user class when the widget size has changed. This
585  * updates the chart's internal data and causes it to issue redraw commands to
586  * reflect the new size.
587  */
588 void PStatStripChart::
589 changed_size(int xsize, int ysize) {
590  if (xsize != _xsize || ysize != _ysize) {
591  _xsize = xsize;
592  _ysize = ysize;
593  if (_xsize > 0 && _ysize > 0) {
594  _cursor_pixel = xsize * _cursor_pixel / _xsize;
595 
596  if (!_first_data) {
597  if (_scroll_mode) {
598  draw_pixels(0, _xsize);
599 
600  } else {
601  // Redraw the stats that were there before.
602  double old_start_time = _start_time;
603 
604  // Back up a bit to draw the stuff to the right of the cursor.
605  _start_time -= _time_width;
606  draw_pixels(_cursor_pixel, _xsize);
607 
608  // Now draw the stuff to the left of the cursor.
609  _start_time = old_start_time;
610  draw_pixels(0, _cursor_pixel);
611  }
612  }
613  }
614  }
615 }
616 
617 /**
618  * To be called by the user class when the whole thing needs to be redrawn for
619  * some reason.
620  */
621 void PStatStripChart::
622 force_redraw() {
623  if (!_first_data) {
624  draw_pixels(0, _xsize);
625  }
626 }
627 
628 /**
629  * To be called by the user class to cause the chart to reset to empty and
630  * start filling again.
631  */
632 void PStatStripChart::
633 force_reset() {
634  clear_region();
635  _first_data = true;
636 }
637 
638 
639 /**
640  * Should be overridden by the user class to wipe out the entire strip chart
641  * region.
642  */
643 void PStatStripChart::
644 clear_region() {
645 }
646 
647 /**
648  * Should be overridden by the user class to copy a region of the chart from
649  * one part of the chart to another. This is used to implement scrolling.
650  */
651 void PStatStripChart::
652 copy_region(int, int, int) {
653 }
654 
655 /**
656  * Should be overridden by the user class. This hook will be called before
657  * drawing any color bars in the strip chart; it gives the pixel range that's
658  * about to be redrawn.
659  */
660 void PStatStripChart::
661 begin_draw(int, int) {
662 }
663 
664 /**
665  * Should be overridden by the user class to draw a single vertical slice in
666  * the strip chart at the indicated pixel, with the data for the indicated
667  * frame.
668  */
669 void PStatStripChart::
670 draw_slice(int, int, const PStatStripChart::FrameData &fdata) {
671 }
672 
673 /**
674  * This is similar to draw_slice(), except it should draw a vertical line of
675  * the background color to represent a portion of the chart that has no data.
676  */
677 void PStatStripChart::
678 draw_empty(int, int) {
679 }
680 
681 /**
682  * This is similar to draw_slice(), except that it should draw the black
683  * vertical stripe that represents the current position when not in scrolling
684  * mode.
685  */
686 void PStatStripChart::
687 draw_cursor(int) {
688 }
689 
690 /**
691  * Should be overridden by the user class. This hook will be called after
692  * drawing a series of color bars in the strip chart; it gives the pixel range
693  * that was just redrawn.
694  */
695 void PStatStripChart::
696 end_draw(int, int) {
697 }
698 
699 /**
700  * Should be overridden by the user class to perform any other updates might
701  * be necessary after the color bars have been redrawn. For instance, it
702  * could check the state of _labels_changed, and redraw the labels if it is
703  * true.
704  */
705 void PStatStripChart::
706 idle() {
707 }
708 
709 
710 // STL function object for sorting labels in order by the collector's sort
711 // index, used in update_labels(), below.
712 class SortCollectorLabels2 {
713 public:
714  SortCollectorLabels2(const PStatClientData *client_data) :
715  _client_data(client_data) {
716  }
717  bool operator () (int a, int b) const {
718  return
719  _client_data->get_collector_def(a)._sort >
720  _client_data->get_collector_def(b)._sort;
721  }
722  const PStatClientData *_client_data;
723 };
724 
725 /**
726  * Resets the list of labels.
727  */
728 void PStatStripChart::
729 update_labels() {
730  const PStatViewLevel *level = _view.get_level(_collector_index);
731  _labels.clear();
732 
733  int num_children = level->get_num_children();
734  for (int i = 0; i < num_children; i++) {
735  const PStatViewLevel *child = level->get_child(i);
736  int collector_index = child->get_collector();
737  if (is_label_used(collector_index)) {
738  _labels.push_back(collector_index);
739  }
740  }
741 
742  SortCollectorLabels2 sort_labels(get_monitor()->get_client_data());
743  sort(_labels.begin(), _labels.end(), sort_labels);
744 
745  int collector_index = level->get_collector();
746  _labels.push_back(collector_index);
747 
748  _labels_changed = true;
749  _level_index = _view.get_level_index();
750 }
751 
752 /**
753  * Calls update_guide_bars with parameters suitable to this kind of graph.
754  */
755 void PStatStripChart::
756 normal_guide_bars() {
757  update_guide_bars(4, _value_height);
758 }
759 
760 
761 /**
762  * Draws the levels for the indicated frame range.
763  */
764 void PStatStripChart::
765 draw_frames(int first_frame, int last_frame) {
766  const PStatThreadData *thread_data = _view.get_thread_data();
767 
768  last_frame = min(last_frame, thread_data->get_latest_frame_number());
769 
770  if (_first_data) {
771  if (_scroll_mode) {
772  _start_time =
773  thread_data->get_frame(last_frame).get_start() - _time_width;
774  } else {
775  _start_time = thread_data->get_frame(first_frame).get_start();
776  _cursor_pixel = 0;
777  }
778  }
779 
780  int first_pixel;
781  if (thread_data->has_frame(first_frame)) {
782  first_pixel =
783  timestamp_to_pixel(thread_data->get_frame(first_frame).get_start());
784  } else {
785  first_pixel = 0;
786  }
787 
788  int last_pixel =
789  timestamp_to_pixel(thread_data->get_frame(last_frame).get_start());
790 
791  if (_first_data && !_scroll_mode) {
792  first_pixel = min(_cursor_pixel, first_pixel);
793  }
794  _first_data = false;
795 
796  if (last_pixel - first_pixel >= _xsize) {
797  // If we're drawing the whole thing all in this one swoop, just start
798  // over.
799  _start_time = thread_data->get_frame(last_frame).get_start() - _time_width;
800  first_pixel = 0;
801  last_pixel = _xsize;
802  }
803 
804  if (last_pixel <= _xsize) {
805  // It all fits in one block.
806  _cursor_pixel = last_pixel;
807  draw_pixels(first_pixel, last_pixel);
808 
809  } else {
810  if (_scroll_mode) {
811  // In scrolling mode, slide the world back.
812  int slide_pixels = last_pixel - _xsize;
813  copy_region(slide_pixels, first_pixel, 0);
814  first_pixel -= slide_pixels;
815  last_pixel -= slide_pixels;
816  _start_time += (double)slide_pixels / (double)_xsize * _time_width;
817  draw_pixels(first_pixel, last_pixel);
818 
819  } else {
820  // In wrapping mode, do it in two blocks.
821  _cursor_pixel = -1;
822  draw_pixels(first_pixel, _xsize);
823  _start_time = pixel_to_timestamp(_xsize);
824  last_pixel -= _xsize;
825  _cursor_pixel = last_pixel;
826  draw_pixels(0, last_pixel);
827  }
828  }
829 }
830 
831 /**
832  * Draws the levels for the indicated pixel range.
833  */
834 void PStatStripChart::
835 draw_pixels(int first_pixel, int last_pixel) {
836  begin_draw(first_pixel, last_pixel);
837  const PStatThreadData *thread_data = _view.get_thread_data();
838 
839  if (_average_mode && !thread_data->is_empty()) {
840  // In average mode, we have to calculate the average value for each pixel.
841  double start_time = pixel_to_timestamp(first_pixel);
842  int then_i = thread_data->get_frame_number_at_time(start_time - pstats_average_time);
843  int now_i = thread_data->get_frame_number_at_time(start_time, then_i);
844  for (int x = first_pixel; x <= last_pixel; x++) {
845  if (x == _cursor_pixel && !_scroll_mode) {
846  draw_cursor(x);
847  } else {
848  FrameData fdata;
849  compute_average_pixel_data(fdata, then_i, now_i, pixel_to_timestamp(x));
850  draw_slice(x, 1, fdata);
851  }
852  }
853 
854  } else {
855  // When average mode is false, we are in frame mode; just show the actual
856  // frame data.
857  int frame_number = -1;
858  int x = first_pixel;
859  while (x <= last_pixel) {
860  if (x == _cursor_pixel && !_scroll_mode) {
861  draw_cursor(x);
862  x++;
863 
864  } else {
865  double time = pixel_to_timestamp(x);
866  frame_number = thread_data->get_frame_number_at_time(time, frame_number);
867  int w = 1;
868  int stop_pixel = last_pixel;
869  if (!_scroll_mode) {
870  stop_pixel = min(stop_pixel, _cursor_pixel);
871  }
872  while (x + w < stop_pixel &&
873  thread_data->get_frame_number_at_time(pixel_to_timestamp(x + w), frame_number) == frame_number) {
874  w++;
875  }
876  if (thread_data->has_frame(frame_number)) {
877  draw_slice(x, w, get_frame_data(frame_number));
878  } else {
879  draw_empty(x, w);
880  }
881  x += w;
882  }
883  }
884  }
885 
886  end_draw(first_pixel, last_pixel);
887 }
888 
889 /**
890  * Erases all elements from the label usage data.
891  */
892 void PStatStripChart::
893 clear_label_usage() {
894  _label_usage.clear();
895 }
896 
897 /**
898  * Erases the indicated frame data from the current label usage. This
899  * indicates that the given FrameData has fallen off the end of the chart.
900  * This must have been proceeded by an earlier call to inc_label_usage() for
901  * the same FrameData
902  */
903 void PStatStripChart::
904 dec_label_usage(const FrameData &fdata) {
905  FrameData::const_iterator fi;
906  for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
907  const ColorData &cd = (*fi);
908  nassertv(cd._collector_index < (int)_label_usage.size());
909  nassertv(_label_usage[cd._collector_index] > 0);
910  _label_usage[cd._collector_index]--;
911  if (_label_usage[cd._collector_index] == 0) {
912  // If a label drops out of usage, it's time to regenerate labels.
913  _level_index = -1;
914  }
915  }
916 }
917 
918 /**
919  * Records the labels named in the indicated FrameData in the table of current
920  * labels in use. This should be called when the given FrameData has been
921  * added to the chart; it will increment the reference count for each
922  * collector named in the FrameData. The reference count will eventually be
923  * decremented when dec_label_usage() is called later.
924  */
925 void PStatStripChart::
926 inc_label_usage(const FrameData &fdata) {
927  FrameData::const_iterator fi;
928  for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
929  const ColorData &cd = (*fi);
930  while (cd._collector_index >= (int)_label_usage.size()) {
931  _label_usage.push_back(0);
932  }
933  nassertv(_label_usage[cd._collector_index] >= 0);
934  _label_usage[cd._collector_index]++;
935  if (_label_usage[cd._collector_index] == 1) {
936  // If a label appears for the first time, it's time to regenerate
937  // labels.
938  _level_index = -1;
939  }
940  }
941 }
pStatClientData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatThreadData::get_elapsed_frames
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...
Definition: pStatThreadData.cxx:203
PStatView::set_to_frame
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
Definition: pStatView.cxx:194
pStatStripChart.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatClientData::has_collector
bool has_collector(int index) const
Returns true if the indicated collector has been defined by the client already, false otherwise.
Definition: pStatClientData.cxx:81
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PStatThreadData::get_frame
const PStatFrameData & get_frame(int frame_number) const
Returns a FrameData structure associated with the indicated frame number.
Definition: pStatThreadData.cxx:87
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatView::get_thread_data
const PStatThreadData * get_thread_data()
Returns the current PStatThreadData associated with the view.
Definition: pStatView.I:19
PStatClientData::get_collector_def
const PStatCollectorDef & get_collector_def(int index) const
Returns the nth collector definition.
Definition: pStatClientData.cxx:90
PStatStripChart::get_collector_under_pixel
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...
Definition: pStatStripChart.cxx:201
PStatViewLevel::get_collector
int get_collector() const
Returns the Collector index associated with this level.
Definition: pStatViewLevel.I:18
PStatStripChart
This is an abstract class that presents the interface for drawing a basic strip-chart,...
Definition: pStatStripChart.h:38
PStatCollectorDef
Defines the details about the Collectors: the name, the suggested color, etc.
Definition: pStatCollectorDef.h:29
PStatView::get_level_index
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
PStatClientData::get_num_threads
int get_num_threads() const
Returns the total number of threads the Data knows about.
Definition: pStatClientData.cxx:194
PStatThreadData::get_oldest_time
double get_oldest_time() const
Returns the timestamp (in seconds elapsed since connection) of the oldest available frame.
Definition: pStatThreadData.cxx:132
PStatClientData::get_thread_name
std::string get_thread_name(int index) const
Returns the name of the indicated thread.
Definition: pStatClientData.cxx:213
PStatClientData::get_num_collectors
int get_num_collectors() const
Returns the total number of collectors the Data knows about.
Definition: pStatClientData.cxx:71
PStatClientData
The data associated with a particular client, but not with any one particular frame or thread: the li...
Definition: pStatClientData.h:36
PStatThreadData::is_empty
bool is_empty() const
Returns true if the structure contains no frames, false otherwise.
Definition: pStatThreadData.cxx:47
PStatView
A View boils down the frame data to a linear list of times spent in a number of different Collectors,...
Definition: pStatView.h:31
PStatViewLevel::get_child
const PStatViewLevel * get_child(int n) const
Returns the nth child of this Level/Collector.
Definition: pStatViewLevel.cxx:79
PStatView::get_show_level
bool get_show_level() const
Returns true if we are showing level data, false if time data.
Definition: pStatView.I:57
PStatStripChart::first_data
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.
Definition: pStatStripChart.cxx:127
PStatStripChart::update
void update()
Updates the chart with the latest data.
Definition: pStatStripChart.cxx:84
PStatThreadData::get_latest_frame_number
int get_latest_frame_number() const
Returns the frame number of the most recent frame stored in the data.
Definition: pStatThreadData.cxx:55
PStatGraph::get_ysize
int get_ysize() const
Returns the height of the chart in pixels.
Definition: pStatGraph.I:90
PStatStripChart::timestamp_to_pixel
int timestamp_to_pixel(double time) const
Converts a timestamp to a horizontal pixel offset.
Definition: pStatStripChart.I:129
PStatStripChart::set_default_vertical_scale
void set_default_vertical_scale()
Sets the vertical scale according to the suggested scale of the base collector, if any,...
Definition: pStatStripChart.cxx:152
PStatStripChart::height_to_pixel
int height_to_pixel(double value) const
Converts a value (i.e.
Definition: pStatStripChart.I:146
PStatFrameData::get_end
double get_end() const
Returns the time of the last data point in the frame data.
Definition: pStatFrameData.I:120
PStatThreadData::has_frame
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...
Definition: pStatThreadData.cxx:74
PStatStripChart::is_title_unknown
bool is_title_unknown() const
Returns true if get_title_text() has never yet returned an answer, false if it has.
Definition: pStatStripChart.cxx:291
PStatMonitor::get_client_data
const PStatClientData * get_client_data() const
Returns the client data associated with this monitor.
Definition: pStatMonitor.I:26
PStatStripChart::set_vertical_scale
void set_vertical_scale(double value_height)
Changes the value the height of the vertical axis represents.
Definition: pStatStripChart.I:60
PStatThreadData
A collection of FrameData structures for recently-received frames within a particular thread.
Definition: pStatThreadData.h:34
PStatGraph
This is an abstract base class for several different kinds of graphs that have a few things in common...
Definition: pStatGraph.h:33
config_pstatclient.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatFrameData::get_net_time
double get_net_time() const
Returns the total time elapsed for the frame.
Definition: pStatFrameData.I:130
PStatClientData::has_thread
bool has_thread(int index) const
Returns true if the indicated thread has been defined by the client already, false otherwise.
Definition: pStatClientData.cxx:204
PStatStripChart::set_auto_vertical_scale
void set_auto_vertical_scale()
Sets the vertical scale to make all the data visible.
Definition: pStatStripChart.cxx:170
pStatMonitor.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatThreadData::get_frame_number_at_time
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.
Definition: pStatThreadData.cxx:154
PStatClientData::get_collector_fullname
std::string get_collector_fullname(int index) const
Returns the "full name" of the indicated collector.
Definition: pStatClientData.cxx:115
PStatGraph::get_monitor
PStatMonitor * get_monitor() const
Returns the monitor associated with this chart.
Definition: pStatGraph.I:18
PStatFrameData::get_start
double get_start() const
Returns the time of the first data point in the frame data.
Definition: pStatFrameData.I:107
PStatViewLevel::get_net_value
double get_net_value() const
Returns the total level value (or elapsed time) represented by this Collector, including all values i...
Definition: pStatViewLevel.cxx:27
pStatFrameData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatViewLevel::get_value_alone
double get_value_alone() const
Returns the total level value (or elapsed time value) for this Collector, not including any values ac...
Definition: pStatViewLevel.I:27
PStatViewLevel
This is a single level value, or band of color, within a View.
Definition: pStatViewLevel.h:29
pStatCollectorDef.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatStripChart::set_collector_index
void set_collector_index(int collector_index)
Changes the collector represented by this strip chart.
Definition: pStatStripChart.cxx:136
PStatView::get_level
PStatViewLevel * get_level(int collector)
Returns a pointer to the level that corresponds to the indicated Collector.
Definition: pStatView.cxx:260
PStatMonitor
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:39
PStatGraph::get_target_frame_rate
double get_target_frame_rate() const
Returns the indicated target frame rate in Hz.
Definition: pStatGraph.I:74
PStatViewLevel::get_num_children
int get_num_children() const
Returns the number of children of this Level/Collector.
Definition: pStatViewLevel.cxx:71
PStatStripChart::pixel_to_timestamp
double pixel_to_timestamp(int x) const
Converts a horizontal pixel offset to a timestamp.
Definition: pStatStripChart.I:137
PStatStripChart::get_title_text
std::string get_title_text()
Returns the text suitable for the title label on the top line.
Definition: pStatStripChart.cxx:255
PStatFrameData
Contains the raw timing and level data for a single frame.
Definition: pStatFrameData.h:33
PStatStripChart::new_data
void new_data(int frame_number)
Indicates that new data has become available.
Definition: pStatStripChart.cxx:73