Panda3D
pStatPianoRoll.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 pStatPianoRoll.cxx
10  * @author drose
11  * @date 2000-07-18
12  */
13 
14 #include "pStatPianoRoll.h"
15 
16 #include "pStatFrameData.h"
17 #include "pStatCollectorDef.h"
18 #include "string_utils.h"
19 #include "config_pstatclient.h"
20 
21 #include <algorithm>
22 
23 /**
24  * This class is used internally to build up the set of color bars defined by
25  * a frame's worth of data.
26  */
27 PStatPianoRoll::BarBuilder::
28 BarBuilder() {
29  _is_new = true;
30 }
31 
32 /**
33  * Resets the data in the BarBuilder for a new frame.
34  */
35 void PStatPianoRoll::BarBuilder::
36 clear() {
37  _is_new = false;
38  _color_bars.clear();
39 }
40 
41 /**
42  * Adds a new data point. The first data point for a given collector turns in
43  * on (starts the bar), the second data point turns it off (ends the bar).
44  */
45 void PStatPianoRoll::BarBuilder::
46 add_data_point(double time, bool is_start) {
47  if (is_start) {
48  // This is a "start" data point: start the bar.
49  if (_color_bars.empty() || _color_bars.back()._end >= 0.0) {
50  ColorBar bar;
51  bar._start = time;
52  bar._end = -1.0;
53  _color_bars.push_back(bar);
54  }
55 
56  } else {
57  // This is a "stop" data point: end the bar.
58  if (_color_bars.empty()) {
59  // A "stop" in the middle of the frame implies a "start" at time 0.
60  ColorBar bar;
61  bar._start = 0.0;
62  bar._end = time;
63  _color_bars.push_back(bar);
64 
65  } else {
66  _color_bars.back()._end = time;
67  }
68  }
69 }
70 
71 /**
72  * Makes sure that each start-bar data point was matched by a corresponding
73  * end-bar data point.
74  */
75 void PStatPianoRoll::BarBuilder::
76 finish(double time) {
77  if (!_color_bars.empty() && _color_bars.back()._end < 0.0) {
78  _color_bars.back()._end = time;
79  }
80 }
81 
82 /**
83  *
84  */
85 PStatPianoRoll::
86 PStatPianoRoll(PStatMonitor *monitor, int thread_index, int xsize, int ysize) :
87  PStatGraph(monitor, xsize, ysize),
88  _thread_index(thread_index)
89 {
90  _time_width = 1.0 / pstats_target_frame_rate;
91  _start_time = 0.0;
92 
93  _current_frame = -1;
94  _guide_bar_units = GBU_ms | GBU_hz | GBU_show_units;
95  normal_guide_bars();
96 }
97 
98 /**
99  *
100  */
101 PStatPianoRoll::
102 ~PStatPianoRoll() {
103 }
104 
105 /**
106  * Updates the chart with the latest data.
107  */
108 void PStatPianoRoll::
110  const PStatClientData *client_data = _monitor->get_client_data();
111 
112  // Don't bother to update the thread data until we know at least something
113  // about the collectors and threads.
114  if (client_data->get_num_collectors() != 0 &&
115  client_data->get_num_threads() != 0) {
116  const PStatThreadData *thread_data =
117  client_data->get_thread_data(_thread_index);
118  if (!thread_data->is_empty()) {
119  int frame_number = thread_data->get_latest_frame_number();
120  if (frame_number != _current_frame) {
121  compute_page(thread_data->get_frame(frame_number));
122  _current_frame = frame_number;
123  force_redraw();
124  }
125  }
126  }
127 
128  idle();
129 }
130 
131 /**
132  * To be called by the user class when the widget size has changed. This
133  * updates the chart's internal data and causes it to issue redraw commands to
134  * reflect the new size.
135  */
136 void PStatPianoRoll::
137 changed_size(int xsize, int ysize) {
138  if (xsize != _xsize || ysize != _ysize) {
139  _xsize = xsize;
140  _ysize = ysize;
141 
142  normal_guide_bars();
143  force_redraw();
144  }
145 }
146 
147 /**
148  * To be called by the user class when the whole thing needs to be redrawn for
149  * some reason.
150  */
151 void PStatPianoRoll::
152 force_redraw() {
153  if (!_labels.empty()) {
154  begin_draw();
155  for (int i = 0; i < (int)_labels.size(); i++) {
156  int collector_index = _labels[i];
157  const ColorBars &bars = _page_data[collector_index]._color_bars;
158 
159  begin_row(i);
160  ColorBars::const_iterator bi;
161  for (bi = bars.begin(); bi != bars.end(); ++bi) {
162  const ColorBar &bar = (*bi);
163  draw_bar(i, timestamp_to_pixel(bar._start), timestamp_to_pixel(bar._end));
164  }
165  end_row(i);
166  }
167  end_draw();
168  }
169 }
170 
171 /**
172  * Calls update_guide_bars with parameters suitable to this kind of graph.
173  */
174 void PStatPianoRoll::
175 normal_guide_bars() {
176  // We want vaguely 100 pixels between guide bars.
177  update_guide_bars(get_xsize() / 100, _time_width);
178 }
179 
180 /**
181  * Should be overridden by the user class. This hook will be called before
182  * drawing any bars in the chart.
183  */
184 void PStatPianoRoll::
185 begin_draw() {
186 }
187 
188 /**
189  * Should be overridden by the user class. This hook will be called before
190  * drawing any one row of bars. These bars correspond to the collector whose
191  * index is get_row_collector(row), and in the color get_row_color(row).
192  */
193 void PStatPianoRoll::
194 begin_row(int) {
195 }
196 
197 /**
198  * Draws a single bar in the chart for the indicated row, in the color
199  * get_row_color(row), for the indicated horizontal pixel range.
200  */
201 void PStatPianoRoll::
202 draw_bar(int, int, int) {
203 }
204 
205 /**
206  * Should be overridden by the user class. This hook will be called after
207  * drawing a series of color bars for a single row.
208  */
209 void PStatPianoRoll::
210 end_row(int) {
211 }
212 
213 /**
214  * Should be overridden by the user class. This hook will be called after
215  * drawing a series of color bars in the chart.
216  */
217 void PStatPianoRoll::
218 end_draw() {
219 }
220 
221 /**
222  * Should be overridden by the user class to perform any other updates might
223  * be necessary after the bars have been redrawn.
224  */
225 void PStatPianoRoll::
226 idle() {
227 }
228 
229 
230 // STL function object for sorting labels in order by the collector's sort
231 // index, used in compute_page(), below.
232 class SortCollectorLabels1 {
233 public:
234  SortCollectorLabels1(const PStatClientData *client_data) :
235  _client_data(client_data) {
236  }
237  bool operator () (int a, int b) const {
238  return
239  _client_data->get_collector_def(a)._sort >
240  _client_data->get_collector_def(b)._sort;
241  }
242  const PStatClientData *_client_data;
243 };
244 
245 /**
246  * Examines the given frame data and rebuilds the _page_data to match it.
247  */
248 void PStatPianoRoll::
249 compute_page(const PStatFrameData &frame_data) {
250  _start_time = frame_data.get_start();
251 
252  // Clear out the page data and copy it to previous, so we can fill it up
253  // again and then check to see if we changed the set of bars this frame.
254  PageData previous;
255  _page_data.swap(previous);
256 
257  int num_events = frame_data.get_num_events();
258  for (int i = 0; i < num_events; i++) {
259  int collector_index = frame_data.get_time_collector(i);
260  double time = frame_data.get_time(i);
261  bool is_start = frame_data.is_start(i);
262  _page_data[collector_index].add_data_point(time, is_start);
263  }
264 
265  // Now check to see if the set of bars has changed.
266  bool changed_bars = (_page_data.size() != previous.size());
267 
268  if (!changed_bars) {
269  PageData::const_iterator ai, bi;
270  ai = _page_data.begin();
271  bi = previous.begin();
272  while (ai != _page_data.end() && !changed_bars) {
273  changed_bars = ((*ai).first == (*bi).first);
274  ++ai;
275  ++bi;
276  }
277  }
278 
279  if (changed_bars) {
280  // If we added or removed some new bars this time, we'll have to update
281  // our list.
282  const PStatClientData *client_data = _monitor->get_client_data();
283 
284  _labels.clear();
285  PageData::const_iterator pi;
286  for (pi = _page_data.begin(); pi != _page_data.end(); ++pi) {
287  int collector_index = (*pi).first;
288  if (client_data->has_collector(collector_index)) {
289  _labels.push_back(collector_index);
290  }
291  }
292 
293  SortCollectorLabels1 sort_labels(client_data);
294  sort(_labels.begin(), _labels.end(), sort_labels);
295 
296  _labels_changed = true;
297  }
298 
299  // Finally, make sure all of the bars are closed.
300  double time = frame_data.get_end();
301  PageData::iterator pi;
302  for (pi = _page_data.begin(); pi != _page_data.end(); ++pi) {
303  (*pi).second.finish(time);
304  }
305 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_xsize() const
Returns the width of the chart in pixels.
Definition: pStatGraph.I:82
int timestamp_to_pixel(double time) const
Converts a timestamp to a horizontal pixel offset.
const PStatFrameData & get_frame(int frame_number) const
Returns a FrameData structure associated with the indicated frame number.
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.
This is an abstract base class for several different kinds of graphs that have a few things in common...
Definition: pStatGraph.h:33
void update()
Updates the chart with the latest data.
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 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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Contains the raw timing and level data for a single frame.
double get_time(size_t n) const
Returns the timestamp of the nth event, in seconds elapsed since some undefined epoch (which is guara...
int get_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.
int get_time_collector(size_t n) const
Returns the index of the collector associated with the nth event.
const PStatThreadData * get_thread_data(int index) const
Returns the data associated with the indicated thread.
bool is_start(size_t n) const
Returns true if the nth event represents a start event, or false if it represents a stop event.
double get_end() const
Returns the time of the last data point in the frame data.
size_t get_num_events() const
Returns the number of individual events stored in the FrameData.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.