panda3d/pandatool/src/pstatserver/pStatPianoRoll.cxx
2004-09-16 17:27:07 +00:00

345 lines
12 KiB
C++

// 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 <algorithm>
////////////////////////////////////////////////////////////////////
// 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);
}
}