// Filename: pStatPianoRoll.cxx // Created by: drose (18Jul00) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "pStatPianoRoll.h" #include "pStatFrameData.h" #include "pStatCollectorDef.h" #include "string_utils.h" #include "config_pstats.h" #include //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::BarBuilder::Constructor // Access: Public // Description: This class is used internally to build up the set of // color bars defined by a frame's worth of data. //////////////////////////////////////////////////////////////////// PStatPianoRoll::BarBuilder:: BarBuilder() { _is_new = true; } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::BarBuilder::clear // Access: Public // Description: Resets the data in the BarBuilder for a new frame. //////////////////////////////////////////////////////////////////// void PStatPianoRoll::BarBuilder:: clear() { _is_new = false; _color_bars.clear(); } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::BarBuilder::add_data_point // Access: Public // Description: Adds a new data point. The first data point for a // given collector turns in on (starts the bar), the // second data point turns it off (ends the bar). //////////////////////////////////////////////////////////////////// void PStatPianoRoll::BarBuilder:: add_data_point(float time) { if (_color_bars.empty() || _color_bars.back()._end >= 0.0) { // This is an odd-numbered data point: start the bar. ColorBar bar; bar._start = time; bar._end = -1.0; _color_bars.push_back(bar); } else { // This is an even-numbered data point: end the bar. _color_bars.back()._end = time; } } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::BarBuilder::finish // Access: Public // Description: Makes sure that each start-bar data point was matched // by a corresponding end-bar data point. //////////////////////////////////////////////////////////////////// void PStatPianoRoll::BarBuilder:: finish(float time) { if (!_color_bars.empty() && _color_bars.back()._end < 0.0) { nout << "Warning: collector was left on at the end of the frame.\n"; _color_bars.back()._end = time; } } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// PStatPianoRoll:: PStatPianoRoll(PStatMonitor *monitor, int thread_index, int xsize, int ysize) : PStatGraph(monitor, xsize, ysize), _thread_index(thread_index) { _time_width = 1.0 / pstats_target_frame_rate; _start_time = 0.0; _current_frame = -1; _guide_bar_units = GBU_ms | GBU_hz | GBU_show_units; normal_guide_bars(); } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// PStatPianoRoll:: ~PStatPianoRoll() { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::update // Access: Public // Description: Updates the chart with the latest data. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: update() { const PStatClientData *client_data = _monitor->get_client_data(); // Don't bother to update the thread data until we know at least // something about the collectors and threads. if (client_data->get_num_collectors() != 0 && client_data->get_num_threads() != 0) { const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index); if (!thread_data->is_empty()) { int frame_number = thread_data->get_latest_frame_number(); if (frame_number != _current_frame) { compute_page(thread_data->get_frame(frame_number)); _current_frame = frame_number; force_redraw(); } } } idle(); } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::changed_size // Access: Protected // Description: To be called by the user class when the widget size // has changed. This updates the chart's internal data // and causes it to issue redraw commands to reflect the // new size. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: changed_size(int xsize, int ysize) { if (xsize != _xsize || ysize != _ysize) { _xsize = xsize; _ysize = ysize; normal_guide_bars(); force_redraw(); } } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::force_redraw // Access: Protected // Description: To be called by the user class when the whole thing // needs to be redrawn for some reason. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: force_redraw() { if (!_labels.empty()) { begin_draw(); for (int i = 0; i < (int)_labels.size(); i++) { int collector_index = _labels[i]; const ColorBars &bars = _page_data[collector_index]._color_bars; begin_row(i); ColorBars::const_iterator bi; for (bi = bars.begin(); bi != bars.end(); ++bi) { const ColorBar &bar = (*bi); draw_bar(i, timestamp_to_pixel(bar._start), timestamp_to_pixel(bar._end)); } end_row(i); } end_draw(); } } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::normal_guide_bars // Access: Protected, Virtual // Description: Calls update_guide_bars with parameters suitable to // this kind of graph. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: normal_guide_bars() { // We want vaguely 100 pixels between guide bars. update_guide_bars(get_xsize() / 100, _time_width); } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::begin_draw // Access: Protected, Virtual // Description: Should be overridden by the user class. This hook // will be called before drawing any bars in the chart. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: begin_draw() { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::begin_row // Access: Protected, Virtual // Description: Should be overridden by the user class. This hook // will be called before drawing any one row of bars. // These bars correspond to the collector whose index is // get_row_collector(row), and in the color // get_row_color(row). //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: begin_row(int) { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::draw_bar // Access: Protected, Virtual // Description: Draws a single bar in the chart for the indicated // row, in the color get_row_color(row), for the // indicated horizontal pixel range. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: draw_bar(int, int, int) { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::end_row // Access: Protected, Virtual // Description: Should be overridden by the user class. This hook // will be called after drawing a series of color bars // for a single row. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: end_row(int) { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::end_draw // Access: Protected, Virtual // Description: Should be overridden by the user class. This hook // will be called after drawing a series of color bars // in the chart. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: end_draw() { } //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::idle // Access: Protected, Virtual // Description: Should be overridden by the user class to perform any // other updates might be necessary after the bars have // been redrawn. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: idle() { } // STL function object for sorting labels in order by the collector's // sort index, used in compute_page(), below. class SortCollectorLabels1 { public: SortCollectorLabels1(const PStatClientData *client_data) : _client_data(client_data) { } bool operator () (int a, int b) const { return _client_data->get_collector_def(a)._sort > _client_data->get_collector_def(b)._sort; } const PStatClientData *_client_data; }; //////////////////////////////////////////////////////////////////// // Function: PStatPianoRoll::compute_page // Access: Private // Description: Examines the given frame data and rebuilds the // _page_data to match it. //////////////////////////////////////////////////////////////////// void PStatPianoRoll:: compute_page(const PStatFrameData &frame_data) { _start_time = frame_data.get_start(); // Clear out the page data and copy it to previous, so we can fill // it up again and then check to see if we changed the set of bars // this frame. PageData previous; _page_data.swap(previous); int num_events = frame_data.get_num_events(); for (int i = 0; i < num_events; i++) { int collector_index = frame_data.get_time_collector(i); float time = frame_data.get_time(i); _page_data[collector_index].add_data_point(time); } // Now check to see if the set of bars has changed. bool changed_bars = (_page_data.size() != previous.size()); if (!changed_bars) { PageData::const_iterator ai, bi; ai = _page_data.begin(); bi = previous.begin(); while (ai != _page_data.end() && !changed_bars) { changed_bars = ((*ai).first == (*bi).first); ++ai; ++bi; } } if (changed_bars) { // If we added or removed some new bars this time, we'll have to // update our list. const PStatClientData *client_data = _monitor->get_client_data(); _labels.clear(); PageData::const_iterator pi; for (pi = _page_data.begin(); pi != _page_data.end(); ++pi) { int collector_index = (*pi).first; if (client_data->has_collector(collector_index)) { _labels.push_back(collector_index); } } SortCollectorLabels1 sort_labels(client_data); sort(_labels.begin(), _labels.end(), sort_labels); _labels_changed = true; } // Finally, make sure all of the bars are closed. float time = frame_data.get_end(); PageData::iterator pi; for (pi = _page_data.begin(); pi != _page_data.end(); ++pi) { (*pi).second.finish(time); } }