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