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