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::
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  */
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 }
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_auto_vertical_scale()
Sets the vertical scale to make all the data visible.
int get_ysize() const
Returns the height of the chart in pixels.
Definition: pStatGraph.I:90
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
Definition: pStatView.cxx:194
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.
const PStatViewLevel * get_child(int n) const
Returns the nth child of this Level/Collector.
const PStatFrameData & get_frame(int frame_number) const
Returns a FrameData structure associated with the indicated frame number.
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 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...
The data associated with a particular client, but not with any one particular frame or thread: the li...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_vertical_scale(double value_height)
Changes the value the height of the vertical axis represents.
double get_value_alone() const
Returns the total level value (or elapsed time value) for this Collector, not including any values ac...
This is an abstract base class for several different kinds of graphs that have a few things in common...
Definition: pStatGraph.h:33
const PStatCollectorDef & get_collector_def(int index) const
Returns the nth collector definition.
std::string get_collector_fullname(int index) const
Returns the "full name" of the indicated collector.
double get_target_frame_rate() const
Returns the indicated target frame rate in Hz.
Definition: pStatGraph.I:74
int get_num_threads() const
Returns the total number of threads the Data knows about.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract class that presents the interface for drawing a basic strip-chart,...
double get_oldest_time() const
Returns the timestamp (in seconds elapsed since connection) of the oldest available frame.
PStatMonitor * get_monitor() const
Returns the monitor associated with this chart.
Definition: pStatGraph.I:18
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
This is a single level value, or band of color, within a View.
double get_net_value() const
Returns the total level value (or elapsed time) represented by this Collector, including all values i...
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:39
bool has_collector(int index) const
Returns true if the indicated collector has been defined by the client already, false otherwise.
void update()
Updates the chart with the latest data.
int height_to_pixel(double value) const
Converts a value (i.e.
bool has_thread(int index) const
Returns true if the indicated thread has been defined by the client already, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Contains the raw timing and level data for a single frame.
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_num_collectors() const
Returns the total number of collectors the Data knows about.
A collection of FrameData structures for recently-received frames within a particular thread.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_title_unknown() const
Returns true if get_title_text() has never yet returned an answer, false if it has.
const PStatThreadData * get_thread_data()
Returns the current PStatThreadData associated with the view.
Definition: pStatView.I:19
double pixel_to_timestamp(int x) const
Converts a horizontal pixel offset to a timestamp.
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...
void set_collector_index(int collector_index)
Changes the collector represented by this strip chart.
int get_collector() const
Returns the Collector index associated with this level.
double get_end() const
Returns the time of the last data point in the frame data.
void set_default_vertical_scale()
Sets the vertical scale according to the suggested scale of the base collector, if any,...
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
const PStatClientData * get_client_data() const
Returns the client data associated with this monitor.
Definition: pStatMonitor.I:26
int get_num_children() const
Returns the number of children of this Level/Collector.
Defines the details about the Collectors: the name, the suggested color, etc.
bool get_show_level() const
Returns true if we are showing level data, false if time data.
Definition: pStatView.I:57
bool is_empty() const
Returns true if the structure contains no frames, false otherwise.
double get_start() const
Returns the time of the first data point in the frame data.
int get_latest_frame_number() const
Returns the frame number of the most recent frame stored in the data.
PStatViewLevel * get_level(int collector)
Returns a pointer to the level that corresponds to the indicated Collector.
Definition: pStatView.cxx:260
double get_net_time() const
Returns the total time elapsed for the frame.
std::string get_thread_name(int index) const
Returns the name of the indicated thread.
std::string get_title_text()
Returns the text suitable for the title label on the top line.