Panda3D
 All Classes Functions Variables Enumerations
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 }
double get_end() const
Returns the time of the last data point in the frame data.
bool is_empty() const
Returns true if the structure contains no frames, false otherwise.
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
int get_num_collectors() const
Returns the total number of collectors the Data knows about.
void update()
Updates the chart with the latest data.
int get_time_collector(int n) const
Returns the index of the collector associated with the nth event.
bool has_collector(int index) const
Returns true if the indicated collector has been defined by the client already, false otherwise...
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:43
bool is_start(int n) const
Returns true if the nth event represents a start event, or false if it represents a stop event...
int get_num_events() const
Returns the number of individual events stored in the FrameData.
double get_start() const
Returns the time of the first data point in the frame 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.
A collection of FrameData structures for recently-received frames within a particular thread...
int get_xsize() const
Returns the width of the chart in pixels.
Definition: pStatGraph.I:103
const PStatThreadData * get_thread_data(int index) const
Returns the data associated with the indicated thread.
int timestamp_to_pixel(double time) const
Converts a timestamp to a horizontal pixel offset.
double get_time(int n) const
Returns the timestamp of the nth event, in seconds elapsed since some undefined epoch (which is guara...
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.
int get_latest_frame_number() const
Returns the frame number of the most recent frame stored in the data.