Revamping PStats to support texture memory, etc.

This commit is contained in:
David Rose 2001-05-18 15:53:05 +00:00
parent f2009437ef
commit e50b9eb36c
64 changed files with 1873 additions and 694 deletions

View File

@ -20,7 +20,7 @@
TypeHandle Character::_type_handle;
#ifndef CPPPARSER
PStatCollector Character::_anim_pcollector("App:Animation", RGBColorf(1,0,1), 30);
PStatCollector Character::_anim_pcollector("App:Animation");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -19,7 +19,7 @@
#include <pStatTimer.h>
#ifndef CPPPARSER
PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions", RGBColorf(1,0.5,0), 40);
PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -17,7 +17,6 @@
#include <pointerTo.h>
#include <renderRelation.h>
#include <pointerTo.h>
#include <pStatCollector.h>
#include <set>

View File

@ -29,8 +29,8 @@
TypeHandle CullTraverser::_type_handle;
#ifndef CPPPARSER
PStatCollector CullTraverser::_cull_pcollector("Cull", RGBColorf(0,1,0), 10);
PStatCollector CullTraverser::_draw_pcollector("Draw", RGBColorf(1,0,0), 20);
PStatCollector CullTraverser::_cull_pcollector("Cull");
PStatCollector CullTraverser::_draw_pcollector("Draw");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -9,7 +9,7 @@
#include <pStatCollector.h>
#ifndef CPPPARSER
PStatCollector _dgraph_pcollector("App:Data Graph", RGBColorf(0.5,0.8,0.4), 30);
PStatCollector _dgraph_pcollector("App:Data graph");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -6,12 +6,21 @@
#include "graphicsStateGuardian.h"
#include "renderBuffer.h"
#include "config_display.h"
#include "textureContext.h"
#include <clockObject.h>
#include <geomNode.h>
#include <algorithm>
#ifndef CPPPARSER
PStatCollector GraphicsStateGuardian::_total_texusage_pcollector("Texture usage");
PStatCollector GraphicsStateGuardian::_active_texusage_pcollector("Texture usage:Active");
PStatCollector GraphicsStateGuardian::_total_texmem_pcollector("Texture memory");
PStatCollector GraphicsStateGuardian::_used_texmem_pcollector("Texture memory:In use");
#endif
TypeHandle GraphicsStateGuardian::_type_handle;
TypeHandle GraphicsStateGuardian::GsgWindow::_type_handle;
@ -420,7 +429,13 @@ end_decal(GeomNode *) {
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
mark_prepared_texture(TextureContext *tc) {
return _prepared_textures.insert(tc).second;
bool prepared = _prepared_textures.insert(tc).second;
#ifdef DO_PSTATS
if (prepared) {
_total_texusage_pcollector.add_level(tc->estimate_texture_memory() / 1048576.0);
}
#endif
return prepared;
}
////////////////////////////////////////////////////////////////////
@ -428,22 +443,62 @@ mark_prepared_texture(TextureContext *tc) {
// Access: Protected
// Description: This is intended to be called from within
// release_texture(). It removes the indicated
// TextureContext pointer from the _prepared_textures set,
// and returns true if it was successfully removed
// TextureContext pointer from the _prepared_textures
// set, and returns true if it was successfully removed
// (i.e. it had been in the set).
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
unmark_prepared_texture(TextureContext *tc) {
return (_prepared_textures.erase(tc) != 0);
bool removed = (_prepared_textures.erase(tc) != 0);
#ifdef DO_PSTATS
if (removed) {
_total_texusage_pcollector.add_level(-(tc->estimate_texture_memory() / 1048576.0));
}
#endif
return removed;
}
void GraphicsStateGuardian::traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
for (Textures::const_iterator ti = _prepared_textures.begin(); ti != _prepared_textures.end();
++ti) {
bool bResult=(*pertex_callbackfn)(*ti,callback_arg);
if(!bResult)
return;
}
#ifdef DO_PSTATS
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::add_to_texture_record
// Access: Protected
// Description: Records that the indicated texture has been applied
// this frame, and thus must be present in current
// texture memory. This function is only used to update
// the PStats current_texmem collector; it gets compiled
// out if we aren't using PStats.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
add_to_texture_record(TextureContext *tc) {
if (_current_textures.insert(tc).second) {
_active_texusage_pcollector.add_level(tc->estimate_texture_memory() / 1048576.0);
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::clear_texture_record
// Access: Protected
// Description: Empties the texture record at the beginning of the
// frame, in preparation for calling
// add_to_texture_record() each time a texture is
// applied.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
clear_texture_record() {
_current_textures.clear();
_active_texusage_pcollector.set_level(0);
}
#endif // DO_PSTATS
void GraphicsStateGuardian::
traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
for (Textures::const_iterator ti = _prepared_textures.begin(); ti != _prepared_textures.end();
++ti) {
bool bResult=(*pertex_callbackfn)(*ti,callback_arg);
if(!bResult)
return;
}
}
////////////////////////////////////////////////////////////////////
@ -461,8 +516,8 @@ get_factory() {
return (*_factory);
}
void GraphicsStateGuardian::read_priorities(void)
{
void GraphicsStateGuardian::
read_priorities(void) {
GsgFactory &factory = get_factory();
if (factory.get_num_preferred() == 0) {
Config::ConfigTable::Symbol::iterator i;

View File

@ -18,6 +18,7 @@
#include <coordinateSystem.h>
#include <factory.h>
#include <renderTraverser.h>
#include <pStatCollector.h>
#include <list>
@ -133,6 +134,17 @@ protected:
bool mark_prepared_texture(TextureContext *tc);
bool unmark_prepared_texture(TextureContext *tc);
#ifdef DO_PSTATS
// These functions are used to update the current texture memory
// usage record in Pstats.
void add_to_texture_record(TextureContext *tc);
void clear_texture_record();
set<TextureContext *> _current_textures;
#else
INLINE void add_to_texture_record(TextureContext *) { }
INLINE void clear_texture_record() { }
#endif
protected:
int _buffer_mask;
NodeAttributes _state;
@ -158,6 +170,12 @@ protected:
CoordinateSystem _coordinate_system;
// Statistics
static PStatCollector _total_texusage_pcollector;
static PStatCollector _active_texusage_pcollector;
static PStatCollector _total_texmem_pcollector;
static PStatCollector _used_texmem_pcollector;
private:
typedef set<TextureContext *> Textures;
Textures _prepared_textures; // NOTE: on win32 another DLL (e.g. libpandadx.dll) cannot access set directly due to exported template issue

View File

@ -25,12 +25,12 @@ TypeHandle GraphicsWindow::WindowPipe::_type_handle;
GraphicsWindow::WindowFactory *GraphicsWindow::_factory = NULL;
#ifndef CPPPARSER
PStatCollector GraphicsWindow::_app_pcollector("App", RGBColorf(0,1,1));
PStatCollector GraphicsWindow::_show_code_pcollector("App:Show Code", RGBColorf(0.8,0.2,1));
PStatCollector GraphicsWindow::_swap_pcollector("Draw:Swap Buffers", RGBColorf(0.5,1,0.8));
PStatCollector GraphicsWindow::_clear_pcollector("Draw:Clear", RGBColorf(0.5,0.7,0.7));
PStatCollector GraphicsWindow::_show_fps_pcollector("Draw:Show fps", RGBColorf(0.5,0.8,1));
PStatCollector GraphicsWindow::_make_current_pcollector("Draw:Make Current", RGBColorf(1,0.6,0.3));
PStatCollector GraphicsWindow::_app_pcollector("App");
PStatCollector GraphicsWindow::_show_code_pcollector("App:Show code");
PStatCollector GraphicsWindow::_swap_pcollector("Draw:Swap buffers");
PStatCollector GraphicsWindow::_clear_pcollector("Draw:Clear");
PStatCollector GraphicsWindow::_show_fps_pcollector("Draw:Show fps");
PStatCollector GraphicsWindow::_make_current_pcollector("Draw:Make current");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -5,4 +5,81 @@
#include "textureContext.h"
#include <texture.h>
#include <pixelBuffer.h>
TypeHandle TextureContext::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: TextureContext::estimate_texture_memory
// Access: Public, Virtual
// Description: Estimates the amount of texture memory that will be
// consumed by loading this texture. This is mainly
// useful for debugging and reporting purposes.
//
// Returns a value in bytes.
////////////////////////////////////////////////////////////////////
size_t TextureContext::
estimate_texture_memory() {
PixelBuffer *pb = _texture->_pbuffer;
size_t pixels = pb->get_xsize() * pb->get_ysize();
size_t bpp = 1;
switch (pb->get_format()) {
case PixelBuffer::F_rgb332:
case PixelBuffer::F_alpha:
case PixelBuffer::F_red:
case PixelBuffer::F_green:
case PixelBuffer::F_blue:
case PixelBuffer::F_luminance:
case PixelBuffer::F_luminance_alpha:
case PixelBuffer::F_luminance_alphamask:
case PixelBuffer::F_color_index:
case PixelBuffer::F_stencil_index:
case PixelBuffer::F_depth_component:
bpp = 1;
break;
case PixelBuffer::F_rgba:
case PixelBuffer::F_rgba4:
case PixelBuffer::F_rgbm:
case PixelBuffer::F_rgb:
case PixelBuffer::F_rgb5:
case PixelBuffer::F_rgba5:
bpp = 2;
break;
case PixelBuffer::F_rgb8:
case PixelBuffer::F_rgba8:
bpp = 4;
break;
case PixelBuffer::F_rgba12:
case PixelBuffer::F_rgb12:
bpp = 6;
break;
}
size_t bytes = pixels * bpp;
bool use_mipmaps;
switch (_texture->get_minfilter()) {
case Texture::FT_nearest_mipmap_nearest:
case Texture::FT_linear_mipmap_nearest:
case Texture::FT_nearest_mipmap_linear:
case Texture::FT_linear_mipmap_linear:
use_mipmaps = true;
break;
default:
use_mipmaps = false;
break;
}
if (use_mipmaps) {
bytes += bytes / 3;
}
return bytes;
}

View File

@ -29,6 +29,8 @@ class EXPCL_PANDA TextureContext : public TypedObject {
public:
INLINE TextureContext(Texture *tex);
virtual size_t estimate_texture_memory();
// This cannot be a PT(Texture), because the texture and the GSG
// both own their TextureContexts! That would create a circular
// reference count.

View File

@ -115,7 +115,7 @@ issue_texcoord_gl(const Geom *geom, Geom::TexCoordIterator &tciterator) {
static void
issue_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
const GraphicsStateGuardianBase *) {
const GraphicsStateGuardianBase *) {
const Colorf &color = geom->get_next_color(citerator);
// glgsg_cat.debug() << "Issuing color " << color << "\n";
glColor4fv(color.get_data());
@ -123,7 +123,7 @@ issue_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
static void
issue_transformed_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
const GraphicsStateGuardianBase *gsg) {
const GraphicsStateGuardianBase *gsg) {
const GLGraphicsStateGuardian *glgsg = DCAST(GLGraphicsStateGuardian, gsg);
const Colorf &color = geom->get_next_color(citerator);
@ -468,11 +468,24 @@ render_frame(const AllAttributesWrapper &initial_state) {
<< " --------------------------------------------" << endl;
#endif
_lighting_enabled_this_frame = false;
_win->begin_frame();
report_errors();
_decal_level = 0;
_lighting_enabled_this_frame = false;
#ifdef DO_PSTATS
// For Pstats to track our current texture memory usage, we have to
// reset the set of current textures each frame.
clear_texture_record();
// But since we don't get sent a new issue_texture() unless our
// texture state has changed, we have to be sure to clear the
// current texture state now. A bit unfortunate, but probably not
// measurably expensive.
NodeAttributes state;
state.set_attribute(TextureTransition::get_class_type(), new TextureAttribute);
set_state(state, false);
#endif
if (_clear_buffer_type != 0) {
// First, clear the entire window.
@ -723,10 +736,10 @@ draw_point(const GeomPoint *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// Draw overall
issuer.issue_color(G_OVERALL, ci);
@ -1224,10 +1237,10 @@ draw_polygon(const GeomPolygon *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
@ -1295,10 +1308,10 @@ draw_tri(const GeomTri *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
@ -1362,10 +1375,10 @@ draw_quad(const GeomQuad *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
@ -1430,10 +1443,10 @@ draw_tristrip(const GeomTristrip *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
@ -1519,10 +1532,10 @@ draw_trifan(const GeomTrifan *geom) {
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
issue_vertex_gl,
issue_normal_gl,
issue_texcoord_gl,
issue_color);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
@ -1700,6 +1713,7 @@ prepare_texture(Texture *tex) {
void GLGraphicsStateGuardian::
apply_texture(TextureContext *tc) {
// activate();
add_to_texture_record(tc);
bind_texture(tc);
/*
@ -1754,7 +1768,7 @@ static int binary_log_cap(const int x) {
// Function: GLGraphicsStateGuardian::copy_texture
// Access: Public, Virtual
// Description: Copy the pixel region indicated by the display
// region from the framebuffer into texture memory
// region from the framebuffer into texture memory
////////////////////////////////////////////////////////////////////
void GLGraphicsStateGuardian::
copy_texture(TextureContext *tc, const DisplayRegion *dr) {
@ -3496,20 +3510,20 @@ bind_texture(TextureContext *tc) {
void GLGraphicsStateGuardian::
specify_texture(Texture *tex) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
get_texture_wrap_mode(tex->get_wrapu()));
get_texture_wrap_mode(tex->get_wrapu()));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
get_texture_wrap_mode(tex->get_wrapv()));
get_texture_wrap_mode(tex->get_wrapv()));
if (gl_force_mipmaps) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
get_texture_filter_type(tex->get_minfilter()));
get_texture_filter_type(tex->get_minfilter()));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
get_texture_filter_type(tex->get_magfilter()));
get_texture_filter_type(tex->get_magfilter()));
}
report_errors();
}
@ -3554,20 +3568,20 @@ apply_texture_immediate(Texture *tex) {
if (use_mipmaps || gl_force_mipmaps) {
#ifndef NDEBUG
if (gl_show_mipmaps) {
build_phony_mipmaps(tex);
return;
build_phony_mipmaps(tex);
return;
}
#endif
gluBuild2DMipmaps(GL_TEXTURE_2D, internal_format,
pb->get_xsize(), pb->get_ysize(),
external_format, type, pb->_image);
pb->get_xsize(), pb->get_ysize(),
external_format, type, pb->_image);
return;
}
}
glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
pb->get_xsize(), pb->get_ysize(), pb->get_border(),
external_format, type, pb->_image );
pb->get_xsize(), pb->get_ysize(), pb->get_border(),
external_format, type, pb->_image );
report_errors();
}

View File

@ -14,7 +14,8 @@
pStatCollector.I pStatCollector.h \
pStatCollectorDef.cxx \
pStatCollectorDef.h pStatFrameData.I pStatFrameData.cxx \
pStatFrameData.h pStatServerControlMessage.cxx \
pStatFrameData.h pStatProperties.cxx pStatProperties.h \
pStatServerControlMessage.cxx \
pStatServerControlMessage.h \
pStatThread.I pStatThread.h \
pStatTimer.I pStatTimer.h
@ -31,6 +32,11 @@
#end lib_target
#begin test_bin_target
#define LOCAL_LIBS \
pstatclient
#define OTHER_LIBS \
pystub
#define TARGET test_client
#define SOURCES \

View File

@ -18,18 +18,18 @@ string get_pstats_name() {
return config_pstats.GetString("pstats-name", "Panda Stats");
}
double get_pstats_max_rate() {
return config_pstats.GetDouble("pstats-max-rate", 30.0);
float get_pstats_max_rate() {
return config_pstats.GetFloat("pstats-max-rate", 30.0);
}
const string pstats_host = config_pstats.GetString("pstats-host", "localhost");
const int pstats_port = config_pstats.GetInt("pstats-port", 5180);
const double pstats_target_frame_rate = config_pstats.GetDouble("pstats-target-frame-rate", 30.0);
const float pstats_target_frame_rate = config_pstats.GetFloat("pstats-target-frame-rate", 30.0);
// The rest are different in that they directly control the server,
// not the client.
const bool pstats_scroll_mode = config_pstats.GetBool("pstats-scroll-mode", true);
const double pstats_history = config_pstats.GetDouble("pstats-history", 30.0);
const float pstats_history = config_pstats.GetFloat("pstats-history", 30.0);
////////////////////////////////////////////////////////////////////
// Function: init_libpstatclient

View File

@ -15,13 +15,13 @@
NotifyCategoryDecl(pstats, EXPCL_PANDA, EXPTP_PANDA);
extern EXPCL_PANDA string get_pstats_name();
extern EXPCL_PANDA double get_pstats_max_rate();
extern EXPCL_PANDA float get_pstats_max_rate();
extern EXPCL_PANDA const string pstats_host;
extern EXPCL_PANDA const int pstats_port;
extern EXPCL_PANDA const double pstats_target_frame_rate;
extern EXPCL_PANDA const float pstats_target_frame_rate;
extern EXPCL_PANDA const bool pstats_scroll_mode;
extern EXPCL_PANDA const double pstats_history;
extern EXPCL_PANDA const float pstats_history;
extern EXPCL_PANDA void init_libpstatclient();

View File

@ -42,7 +42,7 @@ get_client_name() const {
// thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatClient::
set_max_rate(double rate) {
set_max_rate(float rate) {
_max_rate = rate;
}
@ -53,7 +53,7 @@ set_max_rate(double rate) {
// sent to the server per second, per thread. See
// set_max_rate().
////////////////////////////////////////////////////////////////////
INLINE double PStatClient::
INLINE float PStatClient::
get_max_rate() const {
return _max_rate;
}

View File

@ -14,6 +14,7 @@
#include "pStatCollector.h"
#include "pStatThread.h"
#include "config_pstats.h"
#include "pStatProperties.h"
#include <algorithm>
@ -25,6 +26,18 @@
PStatClient *PStatClient::_global_pstats = NULL;
////////////////////////////////////////////////////////////////////
// Function: PStatClient::PerThreadData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PStatClient::PerThreadData::
PerThreadData() {
_has_level = false;
_level = 0.0;
_nested_count = 0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::Constructor
// Access: Public
@ -196,36 +209,55 @@ get_main_thread() const {
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector
// Function: PStatClient::make_collector_with_relname
// Access: Private
// Description: Returns a PStatCollector suitable for measuring
// categories with the indicated name. This is normally
// called by a PStatCollector constructor.
//
// The name may contain colons; if it does, it specifies
// a relative path to the client indicated by the parent
// index.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
make_collector(int parent_index, string fullname) {
if (fullname.empty()) {
fullname = "Unnamed";
make_collector_with_relname(int parent_index, string relname) {
if (relname.empty()) {
relname = "Unnamed";
}
// Skip any colons at the beginning of the name.
size_t start = 0;
while (start < fullname.size() && fullname[start] == ':') {
while (start < relname.size() && relname[start] == ':') {
start++;
}
// If the name contains a colon (after the initial colon), it means
// we are making a nested collector.
size_t colon = fullname.find(':', start);
if (colon != string::npos) {
string parent_name = fullname.substr(start, colon - start);
size_t colon = relname.find(':', start);
while (colon != string::npos) {
string parent_name = relname.substr(start, colon - start);
PStatCollector parent_collector =
make_collector(parent_index, parent_name);
return make_collector(parent_collector._index, fullname.substr(colon + 1));
make_collector_with_name(parent_index, parent_name);
parent_index = parent_collector._index;
relname = relname.substr(colon + 1);
colon = relname.find(':', start);
}
string name = fullname.substr(start);
string name = relname.substr(start);
return make_collector_with_name(parent_index, name);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector_with_name
// Access: Private
// Description: Returns a PStatCollector suitable for measuring
// categories with the indicated name. This is normally
// called by a PStatCollector constructor.
//
// The name should not contain colons.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
make_collector_with_name(int parent_index, const string &name) {
nassertr(parent_index >= 0 && parent_index < (int)_collectors.size(),
PStatCollector());
@ -251,53 +283,22 @@ make_collector(int parent_index, string fullname) {
int new_index = _collectors.size();
parent._children.insert(ThingsByName::value_type(name, new_index));
Collector collector;
// Extending the vector invalidates the parent reference, above.
_collectors.push_back(Collector());
Collector &collector = _collectors.back();
collector._def = new PStatCollectorDef(new_index, name);
collector._def->_parent_index = parent_index;
// We need one nested_count for each thread.
while (collector._nested_count.size() < _threads.size()) {
collector._nested_count.push_back(0);
collector._def->set_parent(*_collectors[parent_index]._def);
initialize_collector_def(this, collector._def);
// We need one PerThreadData for each thread.
while (collector._per_thread.size() < _threads.size()) {
collector._per_thread.push_back(PerThreadData());
}
_collectors.push_back(collector);
return PStatCollector(this, new_index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_collector
// Access: Private
// Description: This flavor of make_collector will make a new
// collector and automatically set up some of its
// properties.
////////////////////////////////////////////////////////////////////
PStatCollector PStatClient::
make_collector(int parent_index, const string &fullname,
const RGBColorf &suggested_color, int sort) {
PStatCollector c = make_collector(parent_index, fullname);
nassertr(c._client == this, PStatCollector());
nassertr(c._index >= 0 && c._index < (int)_collectors.size(), PStatCollector());
PStatCollectorDef *def = _collectors[c._index]._def;
nassertr(def != (PStatCollectorDef *)NULL, PStatCollector());
if (suggested_color != RGBColorf::zero() &&
def->_suggested_color != suggested_color) {
// We need to change the suggested color.
def->_suggested_color = suggested_color;
_collectors_reported = min(_collectors_reported, c._index);
}
if (sort != -1 && def->_sort != sort) {
// We need to change the sort.
def->_sort = sort;
_collectors_reported = min(_collectors_reported, c._index);
}
return c;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::make_thread
// Access: Private
@ -329,12 +330,12 @@ make_thread(const string &name) {
_threads.push_back(thread);
// We need an additional nested_count for this thread in all of the
// We need an additional PerThreadData for this thread in all of the
// collectors.
Collectors::iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
(*ci)._nested_count.push_back(0);
nassertr((*ci)._nested_count.size() == _threads.size(), PStatThread());
(*ci)._per_thread.push_back(PerThreadData());
nassertr((*ci)._per_thread.size() == _threads.size(), PStatThread());
}
return PStatThread(this, new_index);
@ -440,11 +441,11 @@ ns_disconnect() {
Collectors::iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
vector_int::iterator ii;
for (ii = (*ci)._nested_count.begin();
ii != (*ci)._nested_count.end();
PerThread::iterator ii;
for (ii = (*ci)._per_thread.begin();
ii != (*ci)._per_thread.end();
++ii) {
(*ii) = 0;
(*ii)._nested_count = 0;
}
}
}
@ -464,20 +465,20 @@ ns_is_connected() const {
// Access: Private
// Description: Marks the indicated collector index as started.
// Normally you would not use this interface directly;
// instead, call pStatCollector::start().
// instead, call PStatCollector::start().
////////////////////////////////////////////////////////////////////
void PStatClient::
start(int collector_index, int thread_index, double as_of) {
start(int collector_index, int thread_index, float as_of) {
nassertv(collector_index >= 0 && collector_index < (int)_collectors.size());
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
if (_threads[thread_index]._is_active) {
if (_collectors[collector_index]._nested_count[thread_index] == 0) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
// This collector wasn't already started in this thread; record
// a new data point.
_threads[thread_index]._frame_data.add_start(collector_index, as_of);
}
_collectors[collector_index]._nested_count[thread_index]++;
_collectors[collector_index]._per_thread[thread_index]._nested_count++;
}
}
@ -486,15 +487,15 @@ start(int collector_index, int thread_index, double as_of) {
// Access: Private
// Description: Marks the indicated collector index as stopped.
// Normally you would not use this interface directly;
// instead, call pStatCollector::stop().
// instead, call PStatCollector::stop().
////////////////////////////////////////////////////////////////////
void PStatClient::
stop(int collector_index, int thread_index, double as_of) {
stop(int collector_index, int thread_index, float as_of) {
nassertv(collector_index >= 0 && collector_index < (int)_collectors.size());
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
if (_threads[thread_index]._is_active) {
if (_collectors[collector_index]._nested_count[thread_index] == 0) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
pstats_cat.warning()
<< "Collector " << get_collector_fullname(collector_index)
<< " was already stopped in thread " << get_thread_name(thread_index)
@ -502,9 +503,9 @@ stop(int collector_index, int thread_index, double as_of) {
return;
}
_collectors[collector_index]._nested_count[thread_index]--;
_collectors[collector_index]._per_thread[thread_index]._nested_count--;
if (_collectors[collector_index]._nested_count[thread_index] == 0) {
if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
// This collector has now been completely stopped; record a new
// data point.
_threads[thread_index]._frame_data.add_stop(collector_index, as_of);
@ -512,6 +513,54 @@ stop(int collector_index, int thread_index, double as_of) {
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::clear_level
// Access: Private
// Description: Removes the level value from the indicated collector.
// The collector will no longer be reported as having
// any particular level value.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::clear_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
clear_level(int collector_index, int thread_index) {
_collectors[collector_index]._per_thread[thread_index]._has_level = false;
_collectors[collector_index]._per_thread[thread_index]._level = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::set_level
// Access: Private
// Description: Sets the level value for the indicated collector to
// the given amount.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::set_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
set_level(int collector_index, int thread_index, float level) {
_collectors[collector_index]._per_thread[thread_index]._has_level = true;
_collectors[collector_index]._per_thread[thread_index]._level = level;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::add_level
// Access: Private
// Description: Adds the given value (which may be negative) to the
// current value for the given collector. If the
// collector does not already have a level value, it is
// initialized to 0.
//
// Normally you would not use this interface directly;
// instead, call PStatCollector::add_level().
////////////////////////////////////////////////////////////////////
void PStatClient::
add_level(int collector_index, int thread_index, float increment) {
_collectors[collector_index]._per_thread[thread_index]._has_level = true;
_collectors[collector_index]._per_thread[thread_index]._level += increment;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClient::new_frame
// Access: Private
@ -542,28 +591,21 @@ new_frame(int thread_index) {
return;
}
double frame_start = _clock.get_real_time();
float frame_start = _clock.get_real_time();
if (!thread._frame_data.is_empty()) {
// Collector 0 is the whole frame.
stop(0, thread_index, frame_start);
/*
We don't need to do this, since the stats server will do it.
Why should we waste our time?
// Make sure all of our Collectors have turned themselves off now
// at the end of the frame.
Collectors::iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
if ((*ci)._nested_count > 0) {
pstats_cat.warning()
<< "Collector " << get_collector_fullname((*ci)._def->_index)
<< " wasn't stopped!\n";
(*ci)._nested_count = 0;
// Fill up the level data for all the collectors who have level
// data for this thread.
int num_collectors = _collectors.size();
for (int i = 0; i < num_collectors; i++) {
const PerThreadData &ptd = _collectors[i]._per_thread[thread_index];
if (ptd._has_level) {
thread._frame_data.add_level(i, ptd._level);
}
} */
}
transmit_frame_data(thread_index);
}
@ -586,8 +628,8 @@ transmit_frame_data(int thread_index) {
// We don't want to send too many packets in a hurry and flood the
// server. In fact, we don't want to send more than
// _max_rate packets per second, per thread.
double min_packet_delay = 1.0 / _max_rate;
double now = _clock.get_real_time();
float min_packet_delay = 1.0 / _max_rate;
float now = _clock.get_real_time();
if (now - _threads[thread_index]._last_packet > min_packet_delay) {
nassertv(_got_udp_port);

View File

@ -11,10 +11,10 @@
#include "pStatFrameData.h"
#include <clockObject.h>
#include <vector_int.h>
#include <luse.h>
#include <map>
#ifdef DO_PSTATS
#ifdef HAVE_NET
#include <connectionManager.h>
#include <queuedConnectionReader.h>
#include <connectionWriter.h>
@ -43,8 +43,8 @@ public:
INLINE void set_client_name(const string &name);
INLINE string get_client_name() const;
INLINE void set_max_rate(double rate);
INLINE double get_max_rate() const;
INLINE void set_max_rate(float rate);
INLINE float get_max_rate() const;
int get_num_collectors() const;
PStatCollector get_collector(int index) const;
@ -72,13 +72,16 @@ private:
void ns_disconnect();
bool ns_is_connected() const;
PStatCollector make_collector(int parent_index, string fullname);
PStatCollector make_collector(int parent_index, const string &fullname,
const RGBColorf &suggested_color, int sort);
PStatCollector make_collector_with_relname(int parent_index, string relname);
PStatCollector make_collector_with_name(int parent_index, const string &name);
PStatThread make_thread(const string &name);
void start(int collector_index, int thread_index, double as_of);
void stop(int collector_index, int thread_index, double as_of);
void start(int collector_index, int thread_index, float as_of);
void stop(int collector_index, int thread_index, float as_of);
void clear_level(int collector_index, int thread_index);
void set_level(int collector_index, int thread_index, float level);
void add_level(int collector_index, int thread_index, float increment);
void new_frame(int thread_index);
void transmit_frame_data(int thread_index);
@ -91,27 +94,45 @@ private:
typedef map<string, int> ThingsByName;
ThingsByName _threads_by_name;
// This is for the data that is per-collector, per-thread. A vector
// of these is stored in each Collector object, below, indexed by
// thread index.
class PerThreadData {
public:
PerThreadData();
bool _has_level;
float _level;
int _nested_count;
};
typedef vector<PerThreadData> PerThread;
// This is where the meat of the Collector data is stored. (All the
// stuff in PStatCollector and PStatCollectorDef is just fluff.)
class Collector {
public:
PStatCollectorDef *_def;
vector_int _nested_count;
ThingsByName _children;
PerThread _per_thread;
};
typedef vector<Collector> Collectors;
Collectors _collectors;
// This defines a single thread, i.e. a separate chain of execution,
// independent of all other threads. Timing and level data are
// maintained separately for each thread.
class Thread {
public:
string _name;
PStatFrameData _frame_data;
bool _is_active;
int _frame_number;
double _last_packet;
float _last_packet;
};
typedef vector<Thread> Threads;
Threads _threads;
private:
// Networking stuff
string get_hostname();
@ -137,7 +158,7 @@ private:
string _hostname;
string _client_name;
double _max_rate;
float _max_rate;
static PStatClient *_global_pstats;
friend class PStatCollector;

View File

@ -52,13 +52,11 @@ PStatCollector(PStatClient *client, int index) :
// otherwise, the global client is used.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const string &name, const RGBColorf &suggested_color,
int sort, PStatClient *client) {
PStatCollector(const string &name, PStatClient *client) {
if (client == (PStatClient *)NULL) {
client = PStatClient::get_global_pstats();
}
PStatCollector collector(client->make_collector(0, name, suggested_color, sort));
(*this) = collector;
(*this) = client->make_collector_with_relname(0, name);
}
////////////////////////////////////////////////////////////////////
@ -85,12 +83,10 @@ PStatCollector(const string &name, const RGBColorf &suggested_color,
// as its parent.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const PStatCollector &parent, const string &name,
const RGBColorf &suggested_color, int sort) {
PStatCollector(const PStatCollector &parent, const string &name) {
nassertv(parent._client != (PStatClient *)NULL);
PStatCollector collector(parent._client->make_collector
(parent._index, name, suggested_color, sort));
(*this) = collector;
(*this) =
parent._client->make_collector_with_relname(parent._index, name);
}
////////////////////////////////////////////////////////////////////
@ -147,7 +143,7 @@ start(const PStatThread &thread) {
// a monotonically increasing series of time values.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
start(const PStatThread &thread, double as_of) {
start(const PStatThread &thread, float as_of) {
_client->start(_index, thread._index, as_of);
}
@ -182,8 +178,86 @@ stop(const PStatThread &thread) {
// a monotonically increasing series of time values.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
stop(const PStatThread &thread, double as_of) {
stop(const PStatThread &thread, float as_of) {
_client->stop(_index, thread._index, as_of);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::clear_level
// Access: Public
// Description: Removes the level setting associated with this
// collector for the main thread. The collector
// will no longer show up on any level graphs in the
// main thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
clear_level() {
_client->clear_level(_index, 0);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::clear_level
// Access: Public
// Description: Removes the level setting associated with this
// collector for the indicated thread. The collector
// will no longer show up on any level graphs in this
// thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
clear_level(const PStatThread &thread) {
_client->clear_level(_index, thread._index);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::set_level
// Access: Public
// Description: Sets the level setting associated with this
// collector for the main thread to the indicated
// value.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
set_level(float level) {
_client->set_level(_index, 0, level);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::set_level
// Access: Public
// Description: Sets the level setting associated with this
// collector for the indicated thread to the indicated
// value.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
set_level(const PStatThread &thread, float level) {
_client->set_level(_index, thread._index, level);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::add_level
// Access: Public
// Description: Adds the indicated increment (which may be negative)
// to the level setting associated with this collector
// for the main thread. If the collector did not
// already have a level setting for the main thread, it
// is initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_level(float increment) {
_client->add_level(_index, 0, increment);
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollector::add_level
// Access: Public
// Description: Adds the indicated increment (which may be negative)
// to the level setting associated with this collector
// for the indicated thread. If the collector did not
// already have a level setting for this thread, it is
// initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_level(const PStatThread &thread, float increment) {
_client->add_level(_index, thread._index, increment);
}
#endif // DO_PSTATS

View File

@ -16,8 +16,26 @@
////////////////////////////////////////////////////////////////////
// Class : PStatCollector
// Description : A lightweight class that represents a single element
// that may be timed via stats. Bracket the code to be
// timed with calls to start() and stop().
// that may be timed and/or counted via stats.
//
// Collectors can be used to measure two different kinds
// of values: elapsed time, and "other".
//
// To measure elapsed time, call start() and stop() as
// appropriate to bracket the section of code you want
// to time (or use a PStatTimer to do this
// automatically).
//
// To measure anything else, call set_level() and/or
// add_level() to set the "level" value associated with
// this collector. The meaning of the value set for the
// "level" is entirely up to the user; it may represent
// the number of triangles rendered or the kilobytes of
// texture memory consumed, for instance. The level set
// will remain fixed across multiple frames until it is
// reset via another set_level() or adjusted via a call
// to add_level(). It may also be completely removed
// via clear_level().
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PStatCollector {
#ifdef DO_PSTATS
@ -28,24 +46,27 @@ private:
public:
INLINE PStatCollector(const string &name,
const RGBColorf &suggested_color = RGBColorf::zero(),
int sort = -1,
PStatClient *client = NULL);
INLINE PStatCollector(const PStatCollector &parent,
const string &name,
const RGBColorf &suggested_color = RGBColorf::zero(),
int sort = -1);
const string &name);
INLINE PStatCollector(const PStatCollector &copy);
INLINE void operator = (const PStatCollector &copy);
INLINE void start();
INLINE void start(const PStatThread &thread);
INLINE void start(const PStatThread &thread, double as_of);
INLINE void start(const PStatThread &thread, float as_of);
INLINE void stop();
INLINE void stop(const PStatThread &thread);
INLINE void stop(const PStatThread &thread, double as_of);
INLINE void stop(const PStatThread &thread, float as_of);
INLINE void clear_level();
INLINE void clear_level(const PStatThread &thread);
INLINE void set_level(float level);
INLINE void set_level(const PStatThread &thread, float level);
INLINE void add_level(float increment);
INLINE void add_level(const PStatThread &thread, float increment);
private:
PStatClient *_client;
@ -66,11 +87,18 @@ public:
INLINE void start() { }
INLINE void start(const PStatThread &) { }
INLINE void start(const PStatThread &, double) { }
INLINE void start(const PStatThread &, float) { }
INLINE void stop() { }
INLINE void stop(const PStatThread &) { }
INLINE void stop(const PStatThread &, double) { }
INLINE void stop(const PStatThread &, float) { }
INLINE void clear_level() { }
INLINE void clear_level(const PStatThread &) { }
INLINE void set_level(float) { }
INLINE void set_level(const PStatThread &, float) { }
INLINE void add_level(float) { }
INLINE void add_level(const PStatThread &, float) { }
#endif // DO_PSTATS
};

View File

@ -20,6 +20,7 @@ PStatCollectorDef() {
_parent_index = 0;
_suggested_color.set(0.0, 0.0, 0.0);
_sort = -1;
_suggested_scale = 0.0;
}
////////////////////////////////////////////////////////////////////
@ -35,8 +36,23 @@ PStatCollectorDef(int index, const string &name) :
_parent_index = 0;
_suggested_color.set(0.0, 0.0, 0.0);
_sort = -1;
_suggested_scale = 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollectorDef::set_parent
// Access: Public
// Description: This is normally called only by the PStatClient when
// the new PStatCollectorDef is created; it sets the
// parent of the CollectorDef and inherits whatever
// properties are appropriate.
////////////////////////////////////////////////////////////////////
void PStatCollectorDef::
set_parent(const PStatCollectorDef &parent) {
_parent_index = parent._index;
_level_units = parent._level_units;
_suggested_scale = parent._suggested_scale;
}
////////////////////////////////////////////////////////////////////
// Function: PStatCollectorDef::write_datagram
@ -51,6 +67,8 @@ write_datagram(Datagram &destination) const {
destination.add_int16(_parent_index);
_suggested_color.write_datagram(destination);
destination.add_int16(_sort);
destination.add_string(_level_units);
destination.add_float32(_suggested_scale);
}
////////////////////////////////////////////////////////////////////
@ -64,8 +82,7 @@ read_datagram(DatagramIterator &source) {
_name = source.get_string();
_parent_index = source.get_int16();
_suggested_color.read_datagram(source);
if (source.get_remaining_size() > 0) {
_sort = source.get_int16();
}
_sort = source.get_int16();
_level_units = source.get_string();
_suggested_scale = source.get_float32();
}

View File

@ -23,6 +23,7 @@ class EXPCL_PANDA PStatCollectorDef {
public:
PStatCollectorDef();
PStatCollectorDef(int index, const string &name);
void set_parent(const PStatCollectorDef &parent);
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
@ -32,6 +33,8 @@ public:
int _parent_index;
RGBColorf _suggested_color;
int _sort;
string _level_units;
float _suggested_scale;
};
#endif

View File

@ -5,14 +5,36 @@
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::is_empty
// Function: PStatFrameData::is_time_empty
// Access: Public
// Description: Returns true if there are no data points in the frame
// Description: Returns true if there are no time events in the frame
// data, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PStatFrameData::
is_time_empty() const {
return _time_data.empty();
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::is_level_empty
// Access: Public
// Description: Returns true if there are no levels indicated in the
// frame data, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PStatFrameData::
is_level_empty() const {
return _level_data.empty();
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::is_empty
// Access: Public
// Description: Returns true if the FrameData has no time or level
// data.
////////////////////////////////////////////////////////////////////
INLINE bool PStatFrameData::
is_empty() const {
return _data.empty();
return is_time_empty() && is_level_empty();
}
////////////////////////////////////////////////////////////////////
@ -23,7 +45,8 @@ is_empty() const {
////////////////////////////////////////////////////////////////////
INLINE void PStatFrameData::
clear() {
_data.clear();
_time_data.clear();
_level_data.clear();
}
////////////////////////////////////////////////////////////////////
@ -33,11 +56,12 @@ clear() {
// data.
////////////////////////////////////////////////////////////////////
INLINE void PStatFrameData::
add_start(int index, double time) {
add_start(int index, float time) {
nassertv((index & 0x7fff) == index);
DataPoint dp;
dp._index = index;
dp._time = time;
_data.push_back(dp);
dp._value = time;
_time_data.push_back(dp);
}
////////////////////////////////////////////////////////////////////
@ -47,11 +71,27 @@ add_start(int index, double time) {
// data.
////////////////////////////////////////////////////////////////////
INLINE void PStatFrameData::
add_stop(int index, double time) {
add_stop(int index, float time) {
nassertv((index & 0x7fff) == index);
DataPoint dp;
dp._index = index | 0x8000;
dp._time = time;
_data.push_back(dp);
dp._value = time;
_time_data.push_back(dp);
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::add_level
// Access: Public
// Description: Adds a particular level value associated with a given
// collector to the frame data.
////////////////////////////////////////////////////////////////////
INLINE void PStatFrameData::
add_level(int index, float level) {
nassertv((index & 0xffff) == index);
DataPoint dp;
dp._index = index;
dp._value = level;
_level_data.push_back(dp);
}
////////////////////////////////////////////////////////////////////
@ -61,11 +101,11 @@ add_stop(int index, double time) {
// data. This will generally be the time of the start
// of the frame.
////////////////////////////////////////////////////////////////////
INLINE double PStatFrameData::
INLINE float PStatFrameData::
get_start() const {
nassertr(!is_empty(), 0.0);
return _data.front()._time;
return _time_data.front()._value;
}
////////////////////////////////////////////////////////////////////
@ -75,11 +115,11 @@ get_start() const {
// data. This will generally be the time of the end
// of the frame.
////////////////////////////////////////////////////////////////////
INLINE double PStatFrameData::
INLINE float PStatFrameData::
get_end() const {
nassertr(!is_empty(), 0.0);
return _data.back()._time;
return _time_data.back()._value;
}
////////////////////////////////////////////////////////////////////
@ -87,11 +127,11 @@ get_end() const {
// Access: Public
// Description: Returns the total time elapsed for the frame.
////////////////////////////////////////////////////////////////////
INLINE double PStatFrameData::
INLINE float PStatFrameData::
get_net_time() const {
nassertr(!is_empty(), 0.0);
return _data.back()._time - _data.front()._time;
return _time_data.back()._value - _time_data.front()._value;
}
////////////////////////////////////////////////////////////////////
@ -102,23 +142,31 @@ get_net_time() const {
////////////////////////////////////////////////////////////////////
INLINE int PStatFrameData::
get_num_events() const {
return _data.size();
return _time_data.size();
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::get_collector
// Function: PStatFrameData::get_time_collector
// Access: Public
// Description: Returns the index of the collector associated with
// the nth event. If this is the first time the
// collector appears in the event list, it indicates the
// collector has started; if it is the second time, it
// indicates the collector has stopped. Similarly for
// repeated appearances of the same collector.
// the nth event.
////////////////////////////////////////////////////////////////////
INLINE int PStatFrameData::
get_collector(int n) const {
nassertr(n >= 0 && n < (int)_data.size(), 0);
return _data[n]._index;
get_time_collector(int n) const {
nassertr(n >= 0 && n < (int)_time_data.size(), 0);
return _time_data[n]._index & 0x7fff;
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::is_start
// Access: Public
// Description: Returns true if the nth event represents a start
// event, or false if it represents a stop event.
////////////////////////////////////////////////////////////////////
INLINE bool PStatFrameData::
is_start(int n) const {
nassertr(n >= 0 && n < (int)_time_data.size(), 0);
return (_time_data[n]._index & 0x8000) == 0;
}
////////////////////////////////////////////////////////////////////
@ -129,8 +177,42 @@ get_collector(int n) const {
// guaranteed to be shared among all events returned
// from a given client).
////////////////////////////////////////////////////////////////////
INLINE double PStatFrameData::
INLINE float PStatFrameData::
get_time(int n) const {
nassertr(n >= 0 && n < (int)_data.size(), 0);
return _data[n]._time;
nassertr(n >= 0 && n < (int)_time_data.size(), 0);
return _time_data[n]._value;
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::get_num_levels
// Access: Public
// Description: Returns the number of individual level values stored
// in the FrameData.
////////////////////////////////////////////////////////////////////
INLINE int PStatFrameData::
get_num_levels() const {
return _level_data.size();
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::get_level_collector
// Access: Public
// Description: Returns the index of the collector associated with
// the nth level value.
////////////////////////////////////////////////////////////////////
INLINE int PStatFrameData::
get_level_collector(int n) const {
nassertr(n >= 0 && n < (int)_level_data.size(), 0);
return _level_data[n]._index;
}
////////////////////////////////////////////////////////////////////
// Function: PStatFrameData::get_level
// Access: Public
// Description: Returns the height of the nth level value.
////////////////////////////////////////////////////////////////////
INLINE float PStatFrameData::
get_level(int n) const {
nassertr(n >= 0 && n < (int)_level_data.size(), 0);
return _level_data[n]._value;
}

View File

@ -17,9 +17,15 @@
void PStatFrameData::
write_datagram(Datagram &destination) const {
Data::const_iterator di;
for (di = _data.begin(); di != _data.end(); ++di) {
destination.add_uint16(_time_data.size());
for (di = _time_data.begin(); di != _time_data.end(); ++di) {
destination.add_uint16((*di)._index);
destination.add_float64((*di)._time);
destination.add_float32((*di)._value);
}
destination.add_uint16(_level_data.size());
for (di = _level_data.begin(); di != _level_data.end(); ++di) {
destination.add_uint16((*di)._index);
destination.add_float32((*di)._value);
}
}
@ -31,11 +37,23 @@ write_datagram(Datagram &destination) const {
void PStatFrameData::
read_datagram(DatagramIterator &source) {
clear();
while (source.get_remaining_size() > 0) {
int i;
int time_size = source.get_uint16();
for (i = 0; i < time_size; i++) {
nassertv(source.get_remaining_size() > 0);
DataPoint dp;
dp._index = source.get_uint16();
dp._time = source.get_float64();
_data.push_back(dp);
dp._value = source.get_float32();
_time_data.push_back(dp);
}
int level_size = source.get_uint16();
for (i = 0; i < level_size; i++) {
nassertv(source.get_remaining_size() > 0);
DataPoint dp;
dp._index = source.get_uint16();
dp._value = source.get_float32();
_level_data.push_back(dp);
}
}

View File

@ -17,22 +17,34 @@ class DatagramIterator;
////////////////////////////////////////////////////////////////////
// Class : PStatFrameData
// Description : Defines the raw timing data for a single frame.
// Description : Contains the raw timing and level data for a single
// frame. This is a sequence of start/stop events, as
// well as a table of level values, associated with a
// number of collectors within a single frame.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA PStatFrameData {
public:
INLINE bool is_time_empty() const;
INLINE bool is_level_empty() const;
INLINE bool is_empty() const;
INLINE void clear();
INLINE void add_start(int index, double time);
INLINE void add_stop(int index, double time);
INLINE double get_start() const;
INLINE double get_end() const;
INLINE double get_net_time() const;
INLINE void add_start(int index, float time);
INLINE void add_stop(int index, float time);
INLINE void add_level(int index, float level);
INLINE float get_start() const;
INLINE float get_end() const;
INLINE float get_net_time() const;
INLINE int get_num_events() const;
INLINE int get_collector(int n) const;
INLINE double get_time(int n) const;
INLINE int get_time_collector(int n) const;
INLINE bool is_start(int n) const;
INLINE float get_time(int n) const;
INLINE int get_num_levels() const;
INLINE int get_level_collector(int n) const;
INLINE float get_level(int n) const;
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
@ -41,11 +53,11 @@ private:
class DataPoint {
public:
int _index;
double _time;
float _value;
};
typedef vector<DataPoint> Data;
Data _data;
Data _time_data, _level_data;
};
#include "pStatFrameData.I"

View File

@ -0,0 +1,127 @@
// Filename: pStatProperties.cxx
// Created by: drose (17May01)
//
////////////////////////////////////////////////////////////////////
#include "pStatProperties.h"
#include "pStatCollectorDef.h"
#include "pStatClient.h"
#ifdef DO_PSTATS
////////////////////////////////////////////////////////////////////
//
// This file defines the predefined properties (color, sort, etc.) for
// the various PStatCollectors that may be defined within Panda or
// even elsewhere.
//
// It is a little strange to defined these properties here instead of
// where the collectors are actually declared, but it's handy to have
// them all in one place, so we can easily see which colors are
// available, etc. It also makes the declarations a lot simpler,
// since there are quite a few esoteric parameters to specify.
//
// We could define these in some external data file that is read in at
// runtime, so that you could extend this list without having to
// relink panda, but then there are the usual problems with ensuring
// that the file is available to you at runtime. The heck with it.
//
// At least, no other file depends on this file, so it may be modified
// without forcing anything else to be recompiled.
//
////////////////////////////////////////////////////////////////////
struct ColorDef {
float r, g, b;
};
struct TimeCollectorProperties {
const char *name;
ColorDef color;
float suggested_scale;
};
struct LevelCollectorProperties {
const char *name;
ColorDef color;
const char *units;
float suggested_scale;
};
static TimeCollectorProperties time_properties[] = {
{ "App", { 0.0, 1.0, 1.0 }, 1.0 / 30.0 },
{ "App:Animation", { 1.0, 0.0, 1.0 } },
{ "App:Collisions", { 1.0, 0.5, 0.0 } },
{ "App:Data graph", { 0.5, 0.8, 0.4 } },
{ "App:Show code", { 0.8, 0.2, 1.0 } },
{ "Cull", { 0.0, 1.0, 0.0 }, 1.0 / 30.0 },
{ "Draw", { 1.0, 0.0, 0.0 }, 1.0 / 30.0 },
{ "Draw:Swap buffers", { 0.5, 1.0, 0.8 } },
{ "Draw:Clear", { 0.5, 0.7, 0.7 } },
{ "Draw:Show fps", { 0.5, 0.8, 1.0 } },
{ "Draw:Make current", { 1.0, 0.6, 0.3 } },
{ NULL }
};
static LevelCollectorProperties level_properties[] = {
{ "Texture usage", { 1.0, 0.0, 0.0 }, "MB", 8.0 },
{ "Texture usage:Active", { 1.0, 1.0, 0.0 } },
{ "Texture memory", { 0.0, 0.0, 1.0 }, "MB", 8.0 },
{ "Texture memory:In use", { 0.0, 1.0, 1.0 } },
{ NULL }
};
////////////////////////////////////////////////////////////////////
// Function: initialize_collector_def
// Description: This is the only accessor function into this table.
// The PStatCollectorDef constructor calls it when a new
// PStatCollectorDef is created. It should look up in
// the table and find a matching definition for this def
// by name; if one is found, the properties are applied.
////////////////////////////////////////////////////////////////////
void
initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
int i;
string fullname;
if (def->_index == 0) {
fullname = def->_name;
} else {
fullname = client->get_collector_fullname(def->_index);
}
for (i = 0;
time_properties[i].name != (const char *)NULL;
i++) {
const TimeCollectorProperties &tp = time_properties[i];
if (fullname == tp.name) {
def->_sort = i;
def->_suggested_color.set(tp.color.r, tp.color.g, tp.color.b);
if (tp.suggested_scale != 0.0) {
def->_suggested_scale = tp.suggested_scale;
}
return;
}
}
for (i = 0;
level_properties[i].name != (const char *)NULL;
i++) {
const LevelCollectorProperties &lp = level_properties[i];
if (fullname == lp.name) {
def->_sort = i;
def->_suggested_color.set(lp.color.r, lp.color.g, lp.color.b);
if (lp.suggested_scale != 0.0) {
def->_suggested_scale = lp.suggested_scale;
}
if (lp.units != (const char *)NULL) {
def->_level_units = lp.units;
}
return;
}
}
}
#endif // DO_PSTATS

View File

@ -0,0 +1,21 @@
// Filename: pStatProperties.h
// Created by: drose (17May01)
//
////////////////////////////////////////////////////////////////////
#ifndef PSTATPROPERTIES_H
#define PSTATPROPERTIES_H
#include <pandabase.h>
#ifdef DO_PSTATS
class PStatClient;
class PStatCollectorDef;
void initialize_collector_def(PStatClient *client, PStatCollectorDef *def);
#endif // DO_PSTATS
#endif

View File

@ -27,33 +27,34 @@ void signal_handler(int) {
struct SampleData {
const char *category;
RGBColorf color;
int min_ms;
int max_ms;
float min_ms;
float max_ms;
bool is_level;
};
SampleData dataset_zero[] = {
{ "Draw", RGBColorf(0,0,1), 10, 10 },
{ "Cull", RGBColorf(0,1,1), 5, 6 },
{ "App", RGBColorf(1,0,0), 0, 5 },
{ "Draw", 10, 10, false },
{ "Cull", 5, 6, false },
{ "App", 0, 5, false },
{ "Texture memory", 1, 0.01, true },
{ NULL },
};
SampleData dataset_one[] = {
{ "Draw", RGBColorf(0,0,1), 10, 12 },
{ "Squeak", RGBColorf(1,1,0), 25, 30 },
{ "Draw", 10, 12, false },
{ "Squeak", 25, 30, false },
{ NULL },
};
SampleData dataset_two[] = {
{ "Squeak", RGBColorf(1,1,0), 40, 45 },
{ "Cull", RGBColorf(0,1,1), 20, 22 },
{ "Draw", RGBColorf(0,0,1), 10, 20 },
{ "Animation", RGBColorf(0.5,1,0.5), 0, 0 },
{ "Animation:mickey", RGBColorf(0,0,0), 5, 6 },
{ "Animation:donald", RGBColorf(0,0,0), 5, 6 },
{ "Animation:goofy", RGBColorf(0,0,0), 5, 6 },
{ "Animation:pluto", RGBColorf(0,0,0), 5, 6 },
{ "Squeak", 40, 45, false },
{ "Cull", 20, 22, false },
{ "Draw", 10, 20, false },
{ "Animation", 0, 0, false },
{ "Animation:mickey", 5, 6, false },
{ "Animation:donald", 5, 6, false },
{ "Animation:goofy", 5, 6, false },
{ "Animation:pluto", 5, 6, false },
{ NULL },
};
@ -65,7 +66,7 @@ SampleData *datasets[NUM_DATASETS] = {
class WaitRequest {
public:
double _time;
float _time;
int _index;
bool _start;
@ -111,10 +112,10 @@ main(int argc, char *argv[]) {
ds_index = atoi(argv[3]);
} else {
// Pick a random Dataset.
ds_index = (int)((double)NUM_DATASETS * rand() / (RAND_MAX + 1.0));
ds_index = (int)((float)NUM_DATASETS * rand() / (RAND_MAX + 1.0));
}
if (ds_index < 0 || ds_index >= NUM_DATASETS) {
nout << "Invalid datasets; choose a number in the range 0 to "
nout << "Invalid dataset; choose a number in the range 0 to "
<< NUM_DATASETS - 1 << "\n";
exit(1);
}
@ -124,39 +125,49 @@ main(int argc, char *argv[]) {
vector<PStatCollector> _collectors;
int i = 0;
while (ds[i].category != (const char *)NULL) {
_collectors.push_back(PStatCollector(ds[i].category, ds[i].color, i));
_collectors.push_back(PStatCollector(ds[i].category));
if (ds[i].is_level) {
_collectors[i].set_level(ds[i].min_ms);
}
i++;
}
while (!user_interrupted && client->is_connected()) {
client->get_main_thread().new_frame();
double total_ms = 0.0;
double now = client->get_clock().get_real_time();
float total_ms = 0.0;
float now = client->get_clock().get_real_time();
typedef vector<WaitRequest> Wait;
Wait wait;
// Make up some random intervals to "wait".
for (i = 0; i < _collectors.size(); i++) {
// A bit of random jitter so the collectors might overlap some.
double jitter_ms = (5.0 * rand() / (RAND_MAX + 1.0));
for (i = 0; i < (int)_collectors.size(); i++) {
if (ds[i].is_level) {
// Make up an amount to add/delete to the level this frame.
float increment = ds[i].max_ms * (rand() / (RAND_MAX + 1.0) - 0.5);
_collectors[i].add_level(increment);
WaitRequest wr;
wr._time = now + jitter_ms / 1000.0;
wr._index = i;
wr._start = true;
wait.push_back(wr);
int ms_range = ds[i].max_ms - ds[i].min_ms;
double ms = (double)ds[i].min_ms +
((double)ms_range * rand() / (RAND_MAX + 1.0));
now += ms / 1000.0;
total_ms += ms;
wr._time = now + jitter_ms / 1000.0;
wr._start = false;
wait.push_back(wr);
} else {
// A bit of random jitter so the collectors might overlap some.
float jitter_ms = (5.0 * rand() / (RAND_MAX + 1.0));
WaitRequest wr;
wr._time = now + jitter_ms / 1000.0;
wr._index = i;
wr._start = true;
wait.push_back(wr);
float ms_range = ds[i].max_ms - ds[i].min_ms;
float ms = (float)ds[i].min_ms +
(ms_range * rand() / (RAND_MAX + 1.0));
now += ms / 1000.0;
total_ms += ms;
wr._time = now + jitter_ms / 1000.0;
wr._start = false;
wait.push_back(wr);
}
}
// Put the wait requests in order, to allow for the jitter, and

View File

@ -30,7 +30,7 @@
TypeHandle DirectRenderTraverser::_type_handle;
#ifndef CPPPARSER
PStatCollector DirectRenderTraverser::_draw_pcollector("Draw", RGBColorf(1,0,0), 20);
PStatCollector DirectRenderTraverser::_draw_pcollector("Draw");
#endif
////////////////////////////////////////////////////////////////////

View File

@ -24,7 +24,7 @@ GtkStatsGuide(PStatStripChart *chart) :
get_style()->gtkobj()->fg_gc[GTK_WIDGET_STATE (GTK_WIDGET(gtkobj()))];
Gdk_Font font = fg_gc.get_font();
int text_width = font.string_width("000");
int text_width = font.string_width("0000");
set_usize(text_width, 0);
}

View File

@ -28,7 +28,7 @@ GtkStatsLabel(PStatMonitor *monitor, int collector_index,
_bg_color.set_rgb_p(rgb[0], rgb[1], rgb[2]);
// Should our foreground be black or white?
double bright =
float bright =
rgb[0] * 0.299 +
rgb[1] * 0.587 +
rgb[2] * 0.114;

View File

@ -20,6 +20,7 @@
GtkStatsMonitor::
GtkStatsMonitor() {
_destructing = false;
_new_collector = false;
}
////////////////////////////////////////////////////////////////////
@ -81,7 +82,7 @@ get_monitor_name() {
void GtkStatsMonitor::
initialized() {
// Create a default window: a strip chart for the main thread.
new GtkStatsStripWindow(this, 0, 0, 400, 100);
new GtkStatsStripWindow(this, 0, 0, false, 400, 100);
}
////////////////////////////////////////////////////////////////////
@ -99,6 +100,22 @@ got_hello() {
}
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsMonitor::new_collector
// Access: Public, Virtual
// Description: Called whenever a new Collector definition is
// received from the client. Generally, the client will
// send all of its collectors over shortly after
// connecting, but there's no guarantee that they will
// all be received before the first frames are received.
// The monitor should be prepared to accept new Collector
// definitions midstream.
////////////////////////////////////////////////////////////////////
void GtkStatsMonitor::
new_collector(int) {
_new_collector = true;
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsMonitor::new_data
// Access: Public, Virtual
@ -142,6 +159,7 @@ idle() {
for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
(*wi)->idle();
}
_new_collector = false;
}
////////////////////////////////////////////////////////////////////

View File

@ -32,6 +32,7 @@ public:
virtual void initialized();
virtual void got_hello();
virtual void new_collector(int collector_index);
virtual void new_data(int thread_index, int frame_number);
virtual void lost_connection();
virtual void idle();
@ -46,6 +47,7 @@ public:
Windows _windows;
bool _destructing;
bool _new_collector;
};
#endif

View File

@ -115,7 +115,7 @@ begin_draw() {
int num_guide_bars = get_num_guide_bars();
for (int i = 0; i < num_guide_bars; i++) {
const GuideBar &bar = get_guide_bar(i);
int x = (int)((double)get_xsize() * bar._height / get_horizontal_scale());
int x = (int)((float)get_xsize() * bar._height / get_horizontal_scale());
if (x >= 5 && x <= get_xsize() - 5) {
// Only draw it if it's not too close to either edge.
@ -162,7 +162,7 @@ end_draw() {
int num_guide_bars = get_num_guide_bars();
for (int i = 0; i < num_guide_bars; i++) {
const GuideBar &bar = get_guide_bar(i);
int x = (int)((double)get_xsize() * bar._height / get_horizontal_scale());
int x = (int)((float)get_xsize() * bar._height / get_horizontal_scale());
if (x >= 5 && x <= get_xsize() - 5) {
// Only draw it if it's not too close to either edge.

View File

@ -46,6 +46,7 @@ mark_dead() {
////////////////////////////////////////////////////////////////////
void GtkStatsPianoWindow::
idle() {
GtkStatsWindow::idle();
_chart->update();
}
@ -62,28 +63,28 @@ setup_menu() {
scale_menu->items().push_back
(MenuElem("0.1 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 0.1)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 0.1f)));
scale_menu->items().push_back
(MenuElem("1 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 1.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 1.0f)));
scale_menu->items().push_back
(MenuElem("5 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 5.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 5.0f)));
scale_menu->items().push_back
(MenuElem("10 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 10.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 10.0f)));
scale_menu->items().push_back
(MenuElem("20 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 20.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 20.0f)));
scale_menu->items().push_back
(MenuElem("30 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 30.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 30.0f)));
scale_menu->items().push_back
(MenuElem("60 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 60.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 60.0f)));
scale_menu->items().push_back
(MenuElem("120 Hz",
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 120.0)));
bind(slot(this, &GtkStatsPianoWindow::menu_hscale), 120.0f)));
_menu->items().push_back(MenuElem("Scale", *manage(scale_menu)));
}
@ -108,7 +109,7 @@ menu_new_window() {
// The units is in Hz.
////////////////////////////////////////////////////////////////////
void GtkStatsPianoWindow::
menu_hscale(double hz) {
menu_hscale(float hz) {
_chart->set_horizontal_scale(1.0 / hz);
}

View File

@ -28,7 +28,7 @@ public:
protected:
virtual void setup_menu();
virtual void menu_new_window();
void menu_hscale(double hz);
void menu_hscale(float hz);
private:
void layout_window(int chart_xsize, int chart_ysize);

View File

@ -174,13 +174,22 @@ draw_slice(int x, int frame_number) {
// Start by clearing the band first.
_pixmap.draw_line(_white_gc, x, 0, x, get_ysize());
double overall_time = 0.0;
float overall_time = 0.0;
int y = get_ysize();
FrameData::const_iterator fi;
for (fi = frame.begin(); fi != frame.end(); ++fi) {
const ColorData &cd = (*fi);
overall_time += cd._net_time;
overall_time += cd._net_value;
if (overall_time > get_vertical_scale()) {
// Off the top. Go ahead and clamp it by hand, in case it's so
// far off the top we'd overflow the 16-bit pixel value.
_pixmap.draw_line(get_collector_gc(cd._collector_index), x, y, x, 0);
// And we can consider ourselves done now.
return;
}
int top_y = height_to_pixel(overall_time);
_pixmap.draw_line(get_collector_gc(cd._collector_index), x, y, x, top_y);
y = top_y;

View File

@ -37,7 +37,7 @@ public:
Gdk_GC get_collector_gc(int collector_index);
// This signal is thrown when the user double-clicks on a label or
// This signal is thrown when the user float-clicks on a label or
// on a band of color.
SigC::Signal1<void, int> collector_picked;

View File

@ -7,7 +7,9 @@
#include "gtkStatsStripChart.h"
#include "gtkStatsGuide.h"
#include <pStatCollectorDef.h>
#include <string_utils.h>
#include <stdio.h> // for sprintf
@ -21,15 +23,20 @@ using Gtk::Menu_Helpers::SeparatorElem;
////////////////////////////////////////////////////////////////////
GtkStatsStripWindow::
GtkStatsStripWindow(GtkStatsMonitor *monitor, int thread_index,
int collector_index, int chart_xsize, int chart_ysize) :
int collector_index, bool show_level,
int chart_xsize, int chart_ysize) :
GtkStatsWindow(monitor),
_thread_index(thread_index),
_collector_index(collector_index)
_collector_index(collector_index),
_show_level(show_level)
{
_title_unknown = false;
_setup_scale_menu = false;
setup_menu();
layout_window(chart_xsize, chart_ysize);
new_collector(); // To set up the menus in case we can.
show();
}
@ -47,6 +54,40 @@ mark_dead() {
_chart->mark_dead();
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsStripWindow::new_collector
// Access: Public, Virtual
// Description: Called when a new collector has become known.
//
// For the GtkStatsStripWindow, this forces a rebuild of
// the menu that selects the collectors available for
// picking levels.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
new_collector() {
const PStatClientData *client_data = _monitor->get_client_data();
_levels_menu->items().clear();
int num_collectors = client_data->get_num_collectors();
for (int i = 0; i < num_collectors; i++) {
if (client_data->has_collector(i) &&
client_data->get_collector_has_level(i)) {
const PStatCollectorDef &def =
client_data->get_collector_def(i);
// Normally, we only put top-level entries on the menu. The
// lower entries can take care of themselves.
if (def._parent_index == 0) {
_levels_menu->items().push_back
(MenuElem(client_data->get_collector_name(i),
bind(slot(this, &GtkStatsStripWindow::menu_show_levels), i)));
}
}
}
setup_scale_menu();
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsStripWindow::idle
// Access: Public, Virtual
@ -54,11 +95,12 @@ mark_dead() {
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
idle() {
GtkStatsWindow::idle();
_chart->update();
const PStatThreadData *thread_data = _chart->get_view().get_thread_data();
if (!thread_data->is_empty()) {
double frame_rate = thread_data->get_frame_rate();
float frame_rate = thread_data->get_frame_rate();
char buffer[128];
sprintf(buffer, "Frame rate: %0.1f Hz", frame_rate);
_frame_rate_label->set_text(buffer);
@ -82,51 +124,102 @@ setup_menu() {
speed_menu->items().push_back
(MenuElem("1", // 1 chart width scrolls by per minute.
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 1.0)));
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 1.0f)));
speed_menu->items().push_back
(MenuElem("2", // 2 chart widths scroll by per minute.
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 2.0)));
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 2.0f)));
speed_menu->items().push_back
(MenuElem("3",
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 3.0)));
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 3.0f)));
speed_menu->items().push_back
(MenuElem("6",
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 6.0)));
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 6.0f)));
speed_menu->items().push_back
(MenuElem("12",
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 12.0)));
bind(slot(this, &GtkStatsStripWindow::menu_hscale), 12.0f)));
_menu->items().push_back(MenuElem("Speed", *manage(speed_menu)));
Gtk::Menu *scale_menu = new Gtk::Menu;
_scale_menu = new Gtk::Menu;
_scale_menu->items().push_back
(MenuElem("Auto scale",
slot(this, &GtkStatsStripWindow::menu_auto_vscale)));
scale_menu->items().push_back
(MenuElem("10000 ms (0.1 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 0.1)));
scale_menu->items().push_back
(MenuElem("1000 ms (1 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 1.0)));
scale_menu->items().push_back
(MenuElem("200 ms (5 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 5.0)));
scale_menu->items().push_back
(MenuElem("100 ms (10 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 10.0)));
scale_menu->items().push_back
(MenuElem("50.0 ms (20 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 20.0)));
scale_menu->items().push_back
(MenuElem("33.3 ms (30 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 30.0)));
scale_menu->items().push_back
(MenuElem("16.7 ms (60 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 60.0)));
scale_menu->items().push_back
(MenuElem("8.3 ms (120 Hz)",
bind(slot(this, &GtkStatsStripWindow::menu_vscale), 120.0)));
_menu->items().push_back(MenuElem("Scale", *manage(_scale_menu)));
_menu->items().push_back(MenuElem("Scale", *manage(scale_menu)));
_levels_menu = new Gtk::Menu;
_menu->items().push_back(MenuElem("Levels", *manage(_levels_menu)));
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsStripWindow::setup_scale_menu
// Access: Protected
// Description: Sets up the options on the scale menu. We can't do
// this until we have initialized the _chart member and
// we have gotten our first collector_index.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
setup_scale_menu() {
if (_setup_scale_menu) {
// Already done it.
return;
}
const PStatClientData *client_data = _monitor->get_client_data();
if (!client_data->has_collector(_collector_index)) {
// Can't set up the scale menu yet.
return;
}
const PStatCollectorDef &def = client_data->get_collector_def(_collector_index);
float base_scale = 1.0;
string unit_name = def._level_units;
if (_show_level) {
_chart->set_guide_bar_unit_name(unit_name);
_chart->set_guide_bar_units(PStatGraph::GBU_named);
} else {
_chart->set_guide_bar_units(PStatGraph::GBU_ms);
}
if (def._suggested_scale != 0.0) {
base_scale = def._suggested_scale;
} else if (!_show_level) {
base_scale = 1.0 / _chart->get_target_frame_rate();
}
static const float scales[] = {
50.0,
10.0,
5.0,
2.0,
1.0,
0.5,
0.2,
0.1,
0.02,
};
static const int num_scales = sizeof(scales) / sizeof(float);
for (int i = 0; i < num_scales; i++) {
float scale = base_scale * scales[i];
string label;
if (_show_level) {
label = _chart->format_number(scale, PStatGraph::GBU_named | PStatGraph::GBU_show_units, unit_name);
} else {
label = _chart->format_number(scale, PStatGraph::GBU_ms | PStatGraph::GBU_hz | PStatGraph::GBU_show_units);
}
_scale_menu->items().push_back
(MenuElem(label,
bind(slot(this, &GtkStatsStripWindow::menu_vscale), scale)));
}
_setup_scale_menu = true;
}
////////////////////////////////////////////////////////////////////
@ -136,8 +229,9 @@ setup_menu() {
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
menu_new_window() {
new GtkStatsStripWindow(_monitor, _thread_index, _collector_index,
_chart->get_xsize(), _chart->get_ysize());
new GtkStatsStripWindow(_monitor, _thread_index, _collector_index,
_show_level,
_chart->get_xsize(), _chart->get_ysize());
}
////////////////////////////////////////////////////////////////////
@ -151,7 +245,7 @@ menu_new_window() {
// The units is in chart width per minute.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
menu_hscale(double wpm) {
menu_hscale(float wpm) {
_chart->set_horizontal_scale(60.0 / wpm);
}
@ -161,11 +255,36 @@ menu_hscale(double wpm) {
// Description: Selects a new vertical scale for the strip chart.
// This is done from the menu called "Scale".
//
// The units is in Hz.
// The units is in seconds, or whatever the units
// of choice are.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
menu_vscale(double hz) {
_chart->set_vertical_scale(1.0 / hz);
menu_vscale(float max_height) {
_chart->set_vertical_scale(max_height);
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsStripWindow::menu_auto_vscale
// Access: Protected
// Description: Selects a suitable vertical scale based on the data
// already visible in the chart.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
menu_auto_vscale() {
_chart->set_auto_vertical_scale();
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsStripWindow::menu_show_levels
// Access: Protected
// Description: Shows the level values known for the indicated
// collector.
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
menu_show_levels(int collector_index) {
new GtkStatsStripWindow(_monitor, _thread_index, collector_index,
true,
_chart->get_xsize(), _chart->get_ysize());
}
////////////////////////////////////////////////////////////////////
@ -180,7 +299,8 @@ menu_vscale(double hz) {
////////////////////////////////////////////////////////////////////
void GtkStatsStripWindow::
open_subchart(int collector_index) {
new GtkStatsStripWindow(_monitor, _thread_index, collector_index,
new GtkStatsStripWindow(_monitor, _thread_index, collector_index,
_show_level,
_chart->get_xsize(), _chart->get_ysize());
}
@ -224,10 +344,18 @@ layout_window(int chart_xsize, int chart_ysize) {
frame->show();
chart_table->attach(*manage(frame), 1, 2, 1, 2);
_chart = new GtkStatsStripChart(_monitor,
_monitor->get_view(_thread_index),
_collector_index,
chart_xsize, chart_ysize);
if (_show_level) {
_chart = new GtkStatsStripChart(_monitor,
_monitor->get_level_view(_collector_index, _thread_index),
_collector_index,
chart_xsize, chart_ysize);
} else {
_chart = new GtkStatsStripChart(_monitor,
_monitor->get_view(_thread_index),
_collector_index,
chart_xsize, chart_ysize);
}
_chart->collector_picked.
connect(slot(this, &GtkStatsStripWindow::open_subchart));
frame->add(*manage(_chart));
@ -254,7 +382,16 @@ get_title_text() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data->has_collector(_collector_index)) {
text = client_data->get_collector_name(_collector_index) + " time";
const PStatCollectorDef &def = client_data->get_collector_def(_collector_index);
if (_show_level) {
if (def._level_units.empty()) {
text = def._name;
} else {
text = def._name + " (" + def._level_units + ")";
}
} else {
text = def._name + " time";
}
} else {
_title_unknown = true;
}

View File

@ -21,16 +21,21 @@ class GtkStatsStripChart;
class GtkStatsStripWindow : public GtkStatsWindow {
public:
GtkStatsStripWindow(GtkStatsMonitor *monitor, int thread_index,
int collector_index, int chart_xsize, int chart_ysize);
int collector_index, bool show_level,
int chart_xsize, int chart_ysize);
virtual void mark_dead();
virtual void new_collector();
virtual void idle();
protected:
virtual void setup_menu();
void setup_scale_menu();
virtual void menu_new_window();
void menu_hscale(double wpm);
void menu_vscale(double hz);
void menu_hscale(float wpm);
void menu_vscale(float max_height);
void menu_auto_vscale();
void menu_show_levels(int collector_index);
void open_subchart(int collector_index);
private:
@ -40,11 +45,16 @@ private:
private:
int _thread_index;
int _collector_index;
bool _show_level;
bool _title_unknown;
bool _setup_scale_menu;
Gtk::Label *_title_label;
Gtk::Label *_frame_rate_label;
GtkStatsStripChart *_chart;
Gtk::Menu *_scale_menu;
Gtk::Menu *_levels_menu;
};

View File

@ -81,17 +81,7 @@ mark_dead() {
// the window cares.
////////////////////////////////////////////////////////////////////
void GtkStatsWindow::
new_collector(int) {
}
////////////////////////////////////////////////////////////////////
// Function: GtkStatsWindow::new_thread
// Access: Public, Virtual
// Description: Called when a new thread has become known, in case
// the window cares.
////////////////////////////////////////////////////////////////////
void GtkStatsWindow::
new_thread(int) {
new_collector() {
}
////////////////////////////////////////////////////////////////////
@ -101,6 +91,9 @@ new_thread(int) {
////////////////////////////////////////////////////////////////////
void GtkStatsWindow::
idle() {
if (_monitor->_new_collector) {
new_collector();
}
}
////////////////////////////////////////////////////////////////////
@ -150,7 +143,7 @@ setup_menu() {
////////////////////////////////////////////////////////////////////
void GtkStatsWindow::
menu_open_strip_chart() {
new GtkStatsStripWindow(_monitor, 0, 0, 400, 100);
new GtkStatsStripWindow(_monitor, 0, 0, false, 400, 100);
}
////////////////////////////////////////////////////////////////////

View File

@ -30,8 +30,7 @@ public:
virtual void update_title();
virtual void mark_dead();
virtual void new_collector(int collector_index);
virtual void new_thread(int thread_index);
virtual void new_collector();
virtual void idle();
protected:

View File

@ -33,7 +33,7 @@ PStatClientData::
~PStatClientData() {
Collectors::const_iterator ci;
for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
delete (*ci);
delete (*ci)._def;
}
}
@ -85,7 +85,7 @@ get_num_collectors() const {
bool PStatClientData::
has_collector(int index) const {
return (index >= 0 && index < (int)_collectors.size() &&
_collectors[index] != (PStatCollectorDef *)NULL);
_collectors[index]._def != (PStatCollectorDef *)NULL);
}
////////////////////////////////////////////////////////////////////
@ -98,7 +98,7 @@ get_collector_def(int index) const {
if (!has_collector(index)) {
return _null_collector;
}
return *_collectors[index];
return *_collectors[index]._def;
}
////////////////////////////////////////////////////////////////////
@ -111,7 +111,7 @@ get_collector_name(int index) const {
if (!has_collector(index)) {
return "Unknown";
}
const PStatCollectorDef *def = _collectors[index];
const PStatCollectorDef *def = _collectors[index]._def;
return def->_name;
}
@ -129,7 +129,7 @@ get_collector_fullname(int index) const {
return "Unknown";
}
const PStatCollectorDef *def = _collectors[index];
const PStatCollectorDef *def = _collectors[index]._def;
if (def->_parent_index == 0) {
return def->_name;
} else {
@ -137,6 +137,34 @@ get_collector_fullname(int index) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientData::set_collector_has_level
// Access: Public
// Description: Indicates whether the given collector has level data
// (and consequently, whether it should appear on the
// Levels menu).
////////////////////////////////////////////////////////////////////
void PStatClientData::
set_collector_has_level(int index, bool flag) {
slot_collector(index);
nassertv(index >= 0 && index < (int)_collectors.size());
_collectors[index]._is_level = flag;
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientData::get_collector_has_level
// Access: Public
// Description: Returns whether the given collector has level data
// (and consequently, whether it should appear on the
// Levels menu).
////////////////////////////////////////////////////////////////////
bool PStatClientData::
get_collector_has_level(int index) const {
return (index >= 0 && index < (int)_collectors.size() &&
_collectors[index]._is_level);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientData::get_num_threads
// Access: Public
@ -228,20 +256,15 @@ get_child_distance(int parent, int child) const {
////////////////////////////////////////////////////////////////////
void PStatClientData::
add_collector(PStatCollectorDef *def) {
// A sanity check on the index number.
nassertv(def->_index < 1000);
slot_collector(def->_index);
nassertv(def->_index >= 0 && def->_index < (int)_collectors.size());
// Make sure we have enough slots allocated.
while ((int)_collectors.size() <= def->_index) {
_collectors.push_back(NULL);
if (_collectors[def->_index]._def != (PStatCollectorDef *)NULL) {
// Free the old definition, if any.
delete _collectors[def->_index]._def;
}
if (_collectors[def->_index] != (PStatCollectorDef *)NULL) {
// Free any old definition.
delete _collectors[def->_index];
}
_collectors[def->_index] = def;
_collectors[def->_index]._def = def;
}
////////////////////////////////////////////////////////////////////
@ -288,3 +311,22 @@ record_new_frame(int thread_index, int frame_number,
nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
_threads[thread_index]._data->record_new_frame(frame_number, frame_data);
}
////////////////////////////////////////////////////////////////////
// Function: PStatClientData::slot_collector
// Access: Private
// Description: Makes sure there is an entry in the array for a
// collector with the given index number.
////////////////////////////////////////////////////////////////////
void PStatClientData::
slot_collector(int collector_index) {
// A sanity check on the index number.
nassertv(collector_index < 1000);
while ((int)_collectors.size() <= collector_index) {
Collector collector;
collector._def = (PStatCollectorDef *)NULL;
collector._is_level = false;
_collectors.push_back(collector);
}
}

View File

@ -36,6 +36,8 @@ public:
const PStatCollectorDef &get_collector_def(int index) const;
string get_collector_name(int index) const;
string get_collector_fullname(int index) const;
void set_collector_has_level(int index, bool flag);
bool get_collector_has_level(int index) const;
int get_num_threads() const;
bool has_thread(int index) const;
@ -50,12 +52,21 @@ public:
void record_new_frame(int thread_index, int frame_number,
PStatFrameData *frame_data);
private:
void slot_collector(int collector_index);
private:
bool _is_alive;
PStatReader *_reader;
typedef vector<PStatCollectorDef *> Collectors;
class Collector {
public:
PStatCollectorDef *_def;
bool _is_level;
};
typedef vector<Collector> Collectors;
Collectors _collectors;
class Thread {

View File

@ -67,7 +67,7 @@ get_label_color(int n) const {
// placement of guide bars.
////////////////////////////////////////////////////////////////////
INLINE void PStatGraph::
set_target_frame_rate(double frame_rate) {
set_target_frame_rate(float frame_rate) {
if (_target_frame_rate != frame_rate) {
_target_frame_rate = frame_rate;
normal_guide_bars();
@ -80,7 +80,7 @@ set_target_frame_rate(double frame_rate) {
// Description: Returns the indicated target frame rate in Hz. See
// set_target_frame_rate().
////////////////////////////////////////////////////////////////////
INLINE double PStatGraph::
INLINE float PStatGraph::
get_target_frame_rate() const {
return _target_frame_rate;
}
@ -131,3 +131,27 @@ INLINE int PStatGraph::
get_guide_bar_units() const {
return _guide_bar_units;
}
////////////////////////////////////////////////////////////////////
// Function: PStatGraph::set_guide_bar_unit_name
// Access: Public
// Description: Sets the name of the units to be used for the guide
// bars if the units type is set to GBU_named |
// GBU_show_units.
////////////////////////////////////////////////////////////////////
INLINE void PStatGraph::
set_guide_bar_unit_name(const string &unit_name) {
_unit_name = unit_name;
}
////////////////////////////////////////////////////////////////////
// Function: PStatGraph::get_guide_bar_unit_name
// Access: Public
// Description: Returns the name of the units to be used for the guide
// bars if the units type is set to GBU_named |
// GBU_show_units.
////////////////////////////////////////////////////////////////////
INLINE const string &PStatGraph::
get_guide_bar_unit_name() const {
return _unit_name;
}

View File

@ -18,7 +18,7 @@
// Description:
////////////////////////////////////////////////////////////////////
PStatGraph::GuideBar::
GuideBar(double height, const string &label, bool is_target) :
GuideBar(float height, const string &label, bool is_target) :
_height(height),
_label(label),
_is_target(is_target)
@ -98,6 +98,80 @@ get_guide_bar(int n) const {
}
////////////////////////////////////////////////////////////////////
// Function: PStatGraph::format_number
// Access: Public, Static
// Description: Returns a string representing the value nicely
// formatted for its range.
////////////////////////////////////////////////////////////////////
string PStatGraph::
format_number(float value) {
char buffer[128];
if (value < 0.01) {
sprintf(buffer, "%0.4f", value);
} else if (value < 0.1) {
sprintf(buffer, "%0.3f", value);
} else if (value < 1.0) {
sprintf(buffer, "%0.2f", value);
} else if (value < 10.0) {
sprintf(buffer, "%0.1f", value);
} else {
sprintf(buffer, "%0.0f", value);
}
return buffer;
}
////////////////////////////////////////////////////////////////////
// Function: PStatGraph::format_number
// Access: Public, Static
// Description: Returns a string representing the value nicely
// formatted for its range, including the units
// as indicated.
////////////////////////////////////////////////////////////////////
string PStatGraph::
format_number(float value, int guide_bar_units, const string &unit_name) {
string label;
if ((guide_bar_units & GBU_named) != 0) {
// Units are whatever is specified by unit_name, not a time unit
// at all.
label = format_number(value);
if ((guide_bar_units & GBU_show_units) != 0 && !unit_name.empty()) {
label += " ";
label += unit_name;
}
} else {
// Units are either milliseconds or hz, or both.
if ((guide_bar_units & GBU_ms) != 0) {
float ms = value * 1000.0;
label += format_number(ms);
if ((guide_bar_units & GBU_show_units) != 0) {
label += " ms";
}
}
if ((guide_bar_units & GBU_hz) != 0) {
float hz = 1.0 / value;
if ((guide_bar_units & GBU_ms) != 0) {
label += " (";
}
label += format_number(hz);
if ((guide_bar_units & GBU_show_units) != 0) {
label += " Hz";
}
if ((guide_bar_units & GBU_ms) != 0) {
label += ")";
}
}
}
return label;
}
// STL function object for sorting labels in order by the collector's
// sort index, used in update_labels(), below.
class SortCollectorLabels {
@ -122,7 +196,7 @@ public:
// Description: Resets the list of guide bars.
////////////////////////////////////////////////////////////////////
void PStatGraph::
update_guide_bars(int num_bars, double scale) {
update_guide_bars(int num_bars, float scale) {
_guide_bars.clear();
// We'd like to draw about num_bars bars on the chart. But we also
@ -133,9 +207,9 @@ update_guide_bars(int num_bars, double scale) {
// Choose a suitable harmonic of the target frame rate near the
// bottom part of the chart.
double bottom = (double)num_bars / scale;
float bottom = (float)num_bars / scale;
double harmonic;
float harmonic;
if (_target_frame_rate < bottom) {
// n * tfr
harmonic = floor(bottom / _target_frame_rate + 0.5) * _target_frame_rate;
@ -153,50 +227,24 @@ update_guide_bars(int num_bars, double scale) {
_guide_bars_changed = true;
}
////////////////////////////////////////////////////////////////////
// Function: PStatGraph::make_guide_bar
// Access: Protected
// Description: Makes a guide bar for the indicated frame rate.
// Description: Makes a guide bar for the indicated elapsed time or
// level units.
////////////////////////////////////////////////////////////////////
PStatGraph::GuideBar PStatGraph::
make_guide_bar(double time) const {
string label;
make_guide_bar(float value) const {
string label = format_number(value, _guide_bar_units, _unit_name);
char buffer[128];
bool is_target = false;
if ((_guide_bar_units & GBU_ms) != 0) {
double ms = time * 1000.0;
if (ms < 10.0) {
sprintf(buffer, "%0.1f", ms);
} else {
sprintf(buffer, "%0.0f", ms);
}
label += buffer;
if ((_guide_bar_units & GBU_show_units) != 0) {
label += " ms";
}
if ((_guide_bar_units & GBU_named) == 0) {
// If it's a time unit, check to see if it matches our target
// frame rate.
float hz = 1.0 / value;
is_target = IS_NEARLY_EQUAL(hz, _target_frame_rate);
}
if ((_guide_bar_units & GBU_hz) != 0) {
double frame_rate = 1.0 / time;
if (frame_rate < 10.0) {
sprintf(buffer, "%0.1f", frame_rate);
} else {
sprintf(buffer, "%0.0f", frame_rate);
}
if ((_guide_bar_units & GBU_ms) != 0) {
label += " (";
}
label += buffer;
if ((_guide_bar_units & GBU_show_units) != 0) {
label += " Hz";
}
if ((_guide_bar_units & GBU_ms) != 0) {
label += ")";
}
}
return GuideBar(time, label,
IS_NEARLY_EQUAL(1.0 / time, _target_frame_rate));
return GuideBar(value, label, is_target);
}

View File

@ -36,18 +36,18 @@ public:
INLINE string get_label_name(int n) const;
INLINE RGBColorf get_label_color(int n) const;
INLINE void set_target_frame_rate(double frame_rate);
INLINE double get_target_frame_rate() const;
INLINE void set_target_frame_rate(float frame_rate);
INLINE float get_target_frame_rate() const;
INLINE int get_xsize() const;
INLINE int get_ysize() const;
class GuideBar {
public:
GuideBar(double height, const string &label, bool is_target);
GuideBar(float height, const string &label, bool is_target);
GuideBar(const GuideBar &copy);
double _height;
float _height;
string _label;
bool _is_target;
};
@ -55,7 +55,8 @@ public:
enum GuideBarUnits {
GBU_hz = 0x0001,
GBU_ms = 0x0002,
GBU_show_units = 0x0004,
GBU_named = 0x0004,
GBU_show_units = 0x0008,
};
int get_num_guide_bars() const;
@ -63,18 +64,24 @@ public:
INLINE void set_guide_bar_units(int unit_mask);
INLINE int get_guide_bar_units() const;
INLINE void set_guide_bar_unit_name(const string &unit_name);
INLINE const string &get_guide_bar_unit_name() const;
static string format_number(float value);
static string format_number(float value, int guide_bar_units,
const string &unit_name = string());
protected:
virtual void normal_guide_bars()=0;
void update_guide_bars(int num_bars, double scale);
GuideBar make_guide_bar(double time) const;
void update_guide_bars(int num_bars, float scale);
GuideBar make_guide_bar(float value) const;
bool _labels_changed;
bool _guide_bars_changed;
PT(PStatMonitor) _monitor;
double _target_frame_rate;
float _target_frame_rate;
int _xsize;
int _ysize;
@ -87,6 +94,7 @@ protected:
typedef vector<GuideBar> GuideBars;
GuideBars _guide_bars;
int _guide_bar_units;
string _unit_name;
};
#include "pStatGraph.I"

View File

@ -112,9 +112,9 @@ get_collector_color(int collector_index) {
// We didn't have a color for the collector; make one up.
RGBColorf random_color;
random_color[0] = (double)rand() / (double)RAND_MAX;
random_color[1] = (double)rand() / (double)RAND_MAX;
random_color[2] = (double)rand() / (double)RAND_MAX;
random_color[0] = (float)rand() / (float)RAND_MAX;
random_color[1] = (float)rand() / (float)RAND_MAX;
random_color[2] = (float)rand() / (float)RAND_MAX;
ci = _colors.insert(Colors::value_type(collector_index, random_color)).first;
return (*ci).second;
@ -139,6 +139,33 @@ get_view(int thread_index) {
return (*vi).second;
}
////////////////////////////////////////////////////////////////////
// Function: PStatMonitor::get_level_view
// Access: Public
// Description: Returns a view on the level value (as opposed to
// elapsed time) for the given collector over the given
// thread. If there is no such view already for the
// indicated thread, this will create one.
////////////////////////////////////////////////////////////////////
PStatView &PStatMonitor::
get_level_view(int collector_index, int thread_index) {
LevelViews::iterator lvi;
lvi = _level_views.find(collector_index);
if (lvi == _level_views.end()) {
lvi = _level_views.insert(LevelViews::value_type(collector_index, Views())).first;
}
Views &views = (*lvi).second;
Views::iterator vi;
vi = views.find(thread_index);
if (vi == views.end()) {
vi = views.insert(Views::value_type(thread_index, PStatView())).first;
(*vi).second.set_thread_data(_client_data->get_thread_data(thread_index));
(*vi).second.constrain(collector_index, true);
}
return (*vi).second;
}
////////////////////////////////////////////////////////////////////
// Function: PStatMonitor::initialized
// Access: Public, Virtual

View File

@ -55,6 +55,7 @@ public:
INLINE string get_client_progname() const;
PStatView &get_view(int thread_index);
PStatView &get_level_view(int collector_index, int thread_index);
// The following virtual methods may be overridden by a derived
@ -84,6 +85,8 @@ private:
typedef map<int, PStatView> Views;
Views _views;
typedef map<int, Views> LevelViews;
LevelViews _level_views;
typedef map<int, RGBColorf> Colors;
Colors _colors;

View File

@ -10,7 +10,7 @@
// horizontal axis represents. This may force a redraw.
////////////////////////////////////////////////////////////////////
INLINE void PStatPianoRoll::
set_horizontal_scale(double time_width) {
set_horizontal_scale(float time_width) {
if (_time_width != time_width) {
_time_width = time_width;
normal_guide_bars();
@ -24,7 +24,7 @@ set_horizontal_scale(double time_width) {
// Description: Returns the amount of total time the width of the
// horizontal axis represents.
////////////////////////////////////////////////////////////////////
INLINE double PStatPianoRoll::
INLINE float PStatPianoRoll::
get_horizontal_scale() const {
return _time_width;
}
@ -35,8 +35,8 @@ get_horizontal_scale() const {
// Description: Converts a timestamp to a horizontal pixel offset.
////////////////////////////////////////////////////////////////////
INLINE int PStatPianoRoll::
timestamp_to_pixel(double time) const {
return (int)((double)_xsize * (time - _start_time) / _time_width);
timestamp_to_pixel(float time) const {
return (int)((float)_xsize * (time - _start_time) / _time_width);
}
////////////////////////////////////////////////////////////////////
@ -44,7 +44,7 @@ timestamp_to_pixel(double time) const {
// Access: Public
// Description: Converts a horizontal pixel offset to a timestamp.
////////////////////////////////////////////////////////////////////
INLINE double PStatPianoRoll::
INLINE float PStatPianoRoll::
pixel_to_timestamp(int x) const {
return _time_width * (double)x / (double)_xsize + _start_time;
return _time_width * (float)x / (float)_xsize + _start_time;
}

View File

@ -42,7 +42,7 @@ clear() {
// second data point turns it off (ends the bar).
////////////////////////////////////////////////////////////////////
void PStatPianoRoll::BarBuilder::
add_data_point(double time) {
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;
@ -63,7 +63,7 @@ add_data_point(double time) {
// by a corresponding end-bar data point.
////////////////////////////////////////////////////////////////////
void PStatPianoRoll::BarBuilder::
finish(double time) {
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;
@ -262,7 +262,7 @@ public:
// -1 appear to be a very large positive integer, thus placing
// collectors with a -1 sort value at the very end.
return
(unsigned int)_client_data->get_collector_def(a)._sort <
(unsigned int)_client_data->get_collector_def(a)._sort >
(unsigned int)_client_data->get_collector_def(b)._sort;
}
const PStatClientData *_client_data;
@ -287,8 +287,8 @@ compute_page(const PStatFrameData &frame_data) {
int num_events = frame_data.get_num_events();
for (int i = 0; i < num_events; i++) {
int collector_index = (frame_data.get_collector(i) & 0x7fff);
double time = frame_data.get_time(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);
}
@ -312,7 +312,7 @@ compute_page(const PStatFrameData &frame_data) {
}
// Finally, make sure all of the bars are closed.
double time = frame_data.get_end();
float time = frame_data.get_end();
for (pi = _page_data.begin(); pi != _page_data.end(); ++pi) {
(*pi).second.finish(time);
}

View File

@ -39,11 +39,11 @@ public:
void update();
INLINE void set_horizontal_scale(double time_width);
INLINE double get_horizontal_scale() const;
INLINE void set_horizontal_scale(float time_width);
INLINE float get_horizontal_scale() const;
INLINE int timestamp_to_pixel(double time) const;
INLINE double pixel_to_timestamp(int x) const;
INLINE int timestamp_to_pixel(float time) const;
INLINE float pixel_to_timestamp(int x) const;
protected:
void changed_size(int xsize, int ysize);
@ -62,13 +62,13 @@ private:
int _thread_index;
double _time_width;
double _start_time;
float _time_width;
float _start_time;
class ColorBar {
public:
double _start;
double _end;
float _start;
float _end;
};
typedef vector<ColorBar> ColorBars;
@ -76,8 +76,8 @@ private:
public:
BarBuilder();
void clear();
void add_data_point(double time);
void finish(double time);
void add_data_point(float time);
void finish(float time);
bool _is_new;
ColorBars _color_bars;

View File

@ -225,6 +225,18 @@ handle_client_udp_data(const Datagram &datagram) {
PStatFrameData *frame_data = new PStatFrameData;
frame_data->read_datagram(source);
// Check to see if any new collectors have level data.
int num_levels = frame_data->get_num_levels();
for (int i = 0; i < num_levels; i++) {
int collector_index = frame_data->get_level_collector(i);
if (!_client_data->get_collector_has_level(collector_index)) {
// This collector is now reporting level data, and it wasn't
// before.
_client_data->set_collector_has_level(collector_index, true);
_monitor->new_collector(collector_index);
}
}
_client_data->record_new_frame(thread_index, frame_number, frame_data);
_monitor->new_data(thread_index, frame_number);
}

View File

@ -32,7 +32,7 @@ get_collector_index() const {
// horizontal axis represents. This may force a redraw.
////////////////////////////////////////////////////////////////////
INLINE void PStatStripChart::
set_horizontal_scale(double time_width) {
set_horizontal_scale(float time_width) {
if (_time_width != time_width) {
if (_scroll_mode) {
_start_time += _time_width - time_width;
@ -49,7 +49,7 @@ set_horizontal_scale(double time_width) {
// Description: Returns the amount of total time the width of the
// horizontal axis represents.
////////////////////////////////////////////////////////////////////
INLINE double PStatStripChart::
INLINE float PStatStripChart::
get_horizontal_scale() const {
return _time_width;
}
@ -57,38 +57,27 @@ get_horizontal_scale() const {
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::set_vertical_scale
// Access: Public
// Description: Changes the amount of time the height of the
// vertical axis represents. This may force a redraw.
// Description: Changes the value the height of the vertical axis
// represents. This may force a redraw.
////////////////////////////////////////////////////////////////////
INLINE void PStatStripChart::
set_vertical_scale(double time_height) {
if (_time_height != time_height) {
_time_height = time_height;
set_vertical_scale(float value_height) {
if (_value_height != value_height) {
_value_height = value_height;
normal_guide_bars();
force_redraw();
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::set_default_vertical_scale
// Access: Public
// Description: Sets the vertical scale to center the target frame
// rate bar.
////////////////////////////////////////////////////////////////////
INLINE void PStatStripChart::
set_default_vertical_scale() {
set_vertical_scale(2.0 / get_target_frame_rate());
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::get_vertical_scale
// Access: Public
// Description: Returns the amount of total time the height of the
// vertical axis represents.
// Description: Returns total value the height of the vertical axis
// represents.
////////////////////////////////////////////////////////////////////
INLINE double PStatStripChart::
INLINE float PStatStripChart::
get_vertical_scale() const {
return _time_height;
return _value_height;
}
////////////////////////////////////////////////////////////////////
@ -127,8 +116,8 @@ get_scroll_mode() const {
// Description: Converts a timestamp to a horizontal pixel offset.
////////////////////////////////////////////////////////////////////
INLINE int PStatStripChart::
timestamp_to_pixel(double time) const {
return (int)((double)get_xsize() * (time - _start_time) / _time_width);
timestamp_to_pixel(float time) const {
return (int)((float)get_xsize() * (time - _start_time) / _time_width);
}
////////////////////////////////////////////////////////////////////
@ -136,29 +125,29 @@ timestamp_to_pixel(double time) const {
// Access: Public
// Description: Converts a horizontal pixel offset to a timestamp.
////////////////////////////////////////////////////////////////////
INLINE double PStatStripChart::
INLINE float PStatStripChart::
pixel_to_timestamp(int x) const {
return _time_width * (double)x / (double)get_xsize() + _start_time;
return _time_width * (float)x / (float)get_xsize() + _start_time;
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::height_to_pixel
// Access: Public
// Description: Converts an elapsed time (e.g. a "height" in the
// strip chart) to a vertical pixel offset.
// Description: Converts a value (i.e. a "height" in the strip chart)
// to a vertical pixel offset.
////////////////////////////////////////////////////////////////////
INLINE int PStatStripChart::
height_to_pixel(double elapsed_time) const {
return get_ysize() - (int)((double)get_ysize() * elapsed_time / _time_height);
height_to_pixel(float value) const {
return get_ysize() - (int)((float)get_ysize() * value / _value_height);
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::pixel_to_height
// Access: Public
// Description: Converts a vertical pixel offset to an elapsed time
// (a "height" in the strip chart).
// Description: Converts a vertical pixel offset to a value (a
// "height" in the strip chart).
////////////////////////////////////////////////////////////////////
INLINE double PStatStripChart::
INLINE float PStatStripChart::
pixel_to_height(int x) const {
return _time_height * (double)(get_ysize() - x) / (double)get_ysize();
return _value_height * (float)(get_ysize() - x) / (float)get_ysize();
}

View File

@ -4,13 +4,14 @@
////////////////////////////////////////////////////////////////////
#include "pStatStripChart.h"
#include "pStatClientData.h"
#include "pStatMonitor.h"
#include <pStatFrameData.h>
#include <pStatCollectorDef.h>
#include <string_utils.h>
#include <config_pstats.h>
#include <stdio.h> // for sprintf
#include <algorithm>
////////////////////////////////////////////////////////////////////
@ -32,11 +33,20 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view,
_cursor_pixel = 0;
_time_width = 20.0;
_time_height = 1.0/10.0;
_value_height = 1.0/10.0;
_start_time = 0.0;
_level_index = 0;
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data->has_collector(_collector_index)) {
const PStatCollectorDef &def = client_data->get_collector_def(_collector_index);
_unit_name = def._level_units;
if (!_unit_name.empty()) {
_guide_bar_units = GBU_named;
}
}
set_default_vertical_scale();
}
@ -85,7 +95,7 @@ update() {
_next_frame = latest;
// Clean out the old data.
double oldest_time =
float oldest_time =
thread_data->get_frame(latest).get_start() - _time_width;
Data::iterator di;
@ -116,6 +126,66 @@ first_data() const {
return _first_data;
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::set_default_vertical_scale
// Access: Public
// Description: Sets the vertical scale according to the suggested
// scale of the base collector, if any, or to center the
// target frame rate bar otherwise.
////////////////////////////////////////////////////////////////////
void PStatStripChart::
set_default_vertical_scale() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data->has_collector(_collector_index)) {
const PStatCollectorDef &def =
client_data->get_collector_def(_collector_index);
if (def._suggested_scale != 0.0) {
set_vertical_scale(def._suggested_scale);
return;
}
}
set_vertical_scale(2.0 / get_target_frame_rate());
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::set_auto_vertical_scale
// Access: Public
// Description: Sets the vertical scale to make all the data visible.
////////////////////////////////////////////////////////////////////
void PStatStripChart::
set_auto_vertical_scale() {
const PStatThreadData *thread_data = _view.get_thread_data();
float max_value = 0.0;
for (int x = 0; x <= _xsize; x++) {
float time = pixel_to_timestamp(x);
int frame_number =
thread_data->get_frame_number_at_time(time, frame_number);
if (thread_data->has_frame(frame_number)) {
const FrameData &frame = get_frame_data(frame_number);
float overall_value = 0.0;
FrameData::const_iterator fi;
for (fi = frame.begin(); fi != frame.end(); ++fi) {
const ColorData &cd = (*fi);
overall_value += cd._net_value;
}
max_value = max(max_value, overall_value);
}
}
// Ok, now we know what the max value visible in the chart is.
// Choose a scale that will show all of this sensibly.
if (max_value == 0.0) {
set_vertical_scale(1.0);
} else {
set_vertical_scale(max_value * 1.1);
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatStripChart::get_collector_under_pixel
// Access: Public
@ -127,23 +197,23 @@ int PStatStripChart::
get_collector_under_pixel(int xpoint, int ypoint) {
// First, we need to know what frame it was; to know that, we need
// to determine the time corresponding to the x pixel.
double time = pixel_to_timestamp(xpoint);
float time = pixel_to_timestamp(xpoint);
// Now use that time to determine the frame.
const PStatThreadData *thread_data = _view.get_thread_data();
int frame_number = thread_data->get_frame_number_at_time(time);
// And now we can determine which collector within the frame,
// based on the time height.
// based on the value height.
const FrameData &frame = get_frame_data(frame_number);
double overall_time = 0.0;
float overall_value = 0.0;
int y = get_ysize();
FrameData::const_iterator fi;
for (fi = frame.begin(); fi != frame.end(); ++fi) {
const ColorData &cd = (*fi);
overall_time += cd._net_time;
y = height_to_pixel(overall_time);
overall_value += cd._net_value;
y = height_to_pixel(overall_value);
if (y <= ypoint) {
return cd._collector_index;
}
@ -179,18 +249,18 @@ get_frame_data(int frame_number) {
const PStatViewLevel *child = level->get_child(i);
ColorData cd;
cd._collector_index = child->get_collector();
cd._net_time = child->get_net_time();
if (cd._net_time != 0.0) {
cd._net_value = child->get_net_value();
if (cd._net_value != 0.0) {
data.push_back(cd);
}
}
// Also, there might be some time in the overall Collector that
// Also, there might be some value in the overall Collector that
// wasn't included in all of the children.
ColorData cd;
cd._collector_index = level->get_collector();
cd._net_time = level->get_time_alone();
if (cd._net_time != 0.0) {
cd._net_value = level->get_value_alone();
if (cd._net_value != 0.0) {
data.push_back(cd);
}
@ -218,7 +288,7 @@ changed_size(int xsize, int ysize) {
} else {
// Redraw the stats that were there before.
double old_start_time = _start_time;
float old_start_time = _start_time;
// Back up a bit to draw the stuff to the right of the cursor.
_start_time -= _time_width;
@ -366,7 +436,7 @@ public:
// -1 appear to be a very large positive integer, thus placing
// collectors with a -1 sort value at the very end.
return
(unsigned int)_client_data->get_collector_def(a)._sort <
(unsigned int)_client_data->get_collector_def(a)._sort >
(unsigned int)_client_data->get_collector_def(b)._sort;
}
const PStatClientData *_client_data;
@ -407,7 +477,7 @@ update_labels() {
////////////////////////////////////////////////////////////////////
void PStatStripChart::
normal_guide_bars() {
update_guide_bars(4, _time_height);
update_guide_bars(4, _value_height);
}
@ -468,7 +538,7 @@ draw_frames(int first_frame, int last_frame) {
copy_region(slide_pixels, first_pixel, 0);
first_pixel -= slide_pixels;
last_pixel -= slide_pixels;
_start_time += (double)slide_pixels / (double)_xsize * _time_width;
_start_time += (float)slide_pixels / (float)_xsize * _time_width;
draw_pixels(first_pixel, last_pixel);
} else {
@ -499,7 +569,7 @@ draw_pixels(int first_pixel, int last_pixel) {
draw_cursor(x);
} else {
double time = pixel_to_timestamp(x);
float time = pixel_to_timestamp(x);
frame_number = thread_data->get_frame_number_at_time(time, frame_number);
if (thread_data->has_frame(frame_number)) {

View File

@ -23,9 +23,8 @@ class PStatView;
// Class : PStatStripChart
// Description : This is an abstract class that presents the interface
// for drawing a basic strip-chart, showing the relative
// time elapsed over an interval of time for several
// different collectors, differentiated by bands of
// color.
// value over an interval of time for several different
// collectors, differentiated by bands of color.
//
// This class just manages all the strip-chart logic;
// the actual nuts and bolts of drawing pixels is left
@ -44,26 +43,27 @@ public:
INLINE PStatView &get_view() const;
INLINE int get_collector_index() const;
INLINE void set_horizontal_scale(double time_width);
INLINE double get_horizontal_scale() const;
INLINE void set_vertical_scale(double time_height);
INLINE void set_default_vertical_scale();
INLINE double get_vertical_scale() const;
INLINE void set_horizontal_scale(float time_width);
INLINE float get_horizontal_scale() const;
INLINE void set_vertical_scale(float value_height);
void set_default_vertical_scale();
void set_auto_vertical_scale();
INLINE float get_vertical_scale() const;
INLINE void set_scroll_mode(bool scroll_mode);
INLINE bool get_scroll_mode() const;
int get_collector_under_pixel(int xpoint, int ypoint);
INLINE int timestamp_to_pixel(double time) const;
INLINE double pixel_to_timestamp(int x) const;
INLINE int height_to_pixel(double elapsed_time) const;
INLINE double pixel_to_height(int y) const;
INLINE int timestamp_to_pixel(float time) const;
INLINE float pixel_to_timestamp(int x) const;
INLINE int height_to_pixel(float value) const;
INLINE float pixel_to_height(int y) const;
protected:
class ColorData {
public:
int _collector_index;
double _net_time;
float _net_value;
};
typedef vector<ColorData> FrameData;
typedef map<int, FrameData> Data;
@ -101,9 +101,9 @@ private:
int _level_index;
double _time_width;
double _start_time;
double _time_height;
float _time_width;
float _start_time;
float _value_height;
};
#include "pStatStripChart.I"

View File

@ -122,7 +122,7 @@ get_frame(int frame_number) const {
// Description: Returns the timestamp (in seconds elapsed since
// connection) of the latest available frame.
////////////////////////////////////////////////////////////////////
double PStatThreadData::
float PStatThreadData::
get_latest_time() const {
nassertr(!_frames.empty(), 0.0);
return _frames.back()->get_start();
@ -134,7 +134,7 @@ get_latest_time() const {
// Description: Returns the timestamp (in seconds elapsed since
// connection) of the oldest available frame.
////////////////////////////////////////////////////////////////////
double PStatThreadData::
float PStatThreadData::
get_oldest_time() const {
nassertr(!_frames.empty(), 0.0);
return _frames.front()->get_start();
@ -147,7 +147,7 @@ get_oldest_time() const {
// latest frame not later than the indicated time.
////////////////////////////////////////////////////////////////////
const PStatFrameData &PStatThreadData::
get_frame_at_time(double time) const {
get_frame_at_time(float time) const {
return get_frame(get_frame_number_at_time(time));
}
@ -162,7 +162,7 @@ get_frame_at_time(double time) const {
// which may speed the search for the frame.
////////////////////////////////////////////////////////////////////
int PStatThreadData::
get_frame_number_at_time(double time, int hint) const {
get_frame_number_at_time(float time, int hint) const {
hint -= _first_frame_number;
if (hint >= 0 && hint < (int)_frames.size()) {
if (_frames[hint] != (PStatFrameData *)NULL &&
@ -211,8 +211,8 @@ get_latest_frame() const {
// of seconds, by counting up the number of frames
// elapsed in that time interval.
////////////////////////////////////////////////////////////////////
double PStatThreadData::
get_frame_rate(double time) const {
float PStatThreadData::
get_frame_rate(float time) const {
if (_frames.empty()) {
// No frames in the data at all; nothing to base the frame rate
// on.
@ -229,8 +229,8 @@ get_frame_rate(double time) const {
}
nassertr(_frames[now_i] != (PStatFrameData *)NULL, 0.0);
double now = _frames[now_i]->get_end();
double then = now - time;
float now = _frames[now_i]->get_end();
float then = now - time;
int then_i = now_i;
int last_good_i = now_i;
@ -246,7 +246,7 @@ get_frame_rate(double time) const {
nassertr(_frames[last_good_i] != (PStatFrameData *)NULL, 0.0);
int num_frames = now_i - last_good_i + 1;
return (double)num_frames / (now - _frames[last_good_i]->get_start());
return (float)num_frames / (now - _frames[last_good_i]->get_start());
}
@ -259,7 +259,7 @@ get_frame_rate(double time) const {
// frame that may be queried is.
////////////////////////////////////////////////////////////////////
void PStatThreadData::
set_history(double time) {
set_history(float time) {
_history = time;
}
@ -271,7 +271,7 @@ set_history(double time) {
// new frame is added. This affects how old the oldest
// frame that may be queried is.
////////////////////////////////////////////////////////////////////
double PStatThreadData::
float PStatThreadData::
get_history() const {
return _history;
}
@ -292,11 +292,11 @@ void PStatThreadData::
record_new_frame(int frame_number, PStatFrameData *frame_data) {
nassertv(frame_data != (PStatFrameData *)NULL);
nassertv(!frame_data->is_empty());
double time = frame_data->get_start();
float time = frame_data->get_start();
// First, remove all the old frames that fall outside of our
// history window.
double oldest_allowable_time = time - _history;
float oldest_allowable_time = time - _history;
while (!_frames.empty() &&
(_frames.front() == (PStatFrameData *)NULL ||
_frames.front()->is_empty() ||

View File

@ -40,18 +40,18 @@ public:
bool has_frame(int frame_number) const;
const PStatFrameData &get_frame(int frame_number) const;
double get_latest_time() const;
double get_oldest_time() const;
const PStatFrameData &get_frame_at_time(double time) const;
int get_frame_number_at_time(double time, int hint = -1) const;
float get_latest_time() const;
float get_oldest_time() const;
const PStatFrameData &get_frame_at_time(float time) const;
int get_frame_number_at_time(float time, int hint = -1) const;
const PStatFrameData &get_latest_frame() const;
double get_frame_rate(double time = 3.0) const;
float get_frame_rate(float time = 3.0) const;
void set_history(double time);
double get_history() const;
void set_history(float time);
float get_history() const;
void record_new_frame(int frame_number, PStatFrameData *frame_data);
@ -61,7 +61,7 @@ private:
typedef deque<PStatFrameData *> Frames;
Frames _frames;
int _first_frame_number;
double _history;
float _history;
static PStatFrameData _null_frame;
};

View File

@ -52,7 +52,7 @@ set_to_frame(int frame_number) {
// set_to_frame.
////////////////////////////////////////////////////////////////////
INLINE void PStatView::
set_to_time(double time) {
set_to_time(float time) {
set_to_frame(_thread_data->get_frame_at_time(time));
}

View File

@ -18,8 +18,8 @@
// Class : FrameSample
// Description : This class is used within this module only--in fact,
// within PStatView::set_to_frame() only--to help
// collect data out of the PStatFrameData object and
// boil it down to a list of elapsed times.
// collect event data out of the PStatFrameData object
// and boil it down to a list of elapsed times.
////////////////////////////////////////////////////////////////////
class FrameSample {
public:
@ -31,10 +31,25 @@ public:
_pushed = false;
_net_time = 0.0;
}
void data_point(double time, Started &started) {
void data_point(float time, bool is_start, Started &started) {
_touched = true;
_is_started = !_is_started;
// We only consider events that change the start/stop state.
// With two consecutive 'start' events, for instance, we ignore
// the second one.
// *** That's not quite the right thing to do. We should keep
// track of the nesting level and bracket things correctly, so
// that we ignore the second start and the *first* stop, but
// respect the outer start/stop. For the short term, this
// works, because the client is already doing this logic and
// won't send us nested start/stop pairs, but we'd like to
// generalize this in the future so we can deal with these
// nested pairs properly.
nassertv(is_start != _is_started);
_is_started = is_start;
if (_pushed) {
nassertv(!_is_started);
Started::iterator si = find(started.begin(), started.end(), this);
@ -43,19 +58,19 @@ public:
} else {
if (_is_started) {
_net_time -= time;
push_all(time, started);
started.push_back(this);
_net_time -= time;
push_all(time, started);
started.push_back(this);
} else {
_net_time += time;
Started::iterator si = find(started.begin(), started.end(), this);
nassertv(si != started.end());
started.erase(si);
pop_one(time, started);
_net_time += time;
Started::iterator si = find(started.begin(), started.end(), this);
nassertv(si != started.end());
started.erase(si);
pop_one(time, started);
}
}
}
void push(double time) {
void push(float time) {
if (!_pushed) {
_pushed = true;
if (_is_started) {
@ -63,7 +78,7 @@ public:
}
}
}
void pop(double time) {
void pop(float time) {
if (_pushed) {
_pushed = false;
if (_is_started) {
@ -72,14 +87,14 @@ public:
}
}
void push_all(double time, Started &started) {
void push_all(float time, Started &started) {
Started::iterator si;
for (si = started.begin(); si != started.end(); ++si) {
(*si)->push(time);
}
}
void pop_one(double time, Started &started) {
void pop_one(float time, Started &started) {
Started::reverse_iterator si;
for (si = started.rbegin(); si != started.rend(); ++si) {
if ((*si)->_pushed) {
@ -92,7 +107,7 @@ public:
bool _touched;
bool _is_started;
bool _pushed;
double _net_time;
float _net_time;
};
@ -105,6 +120,7 @@ public:
PStatView::
PStatView() {
_constraint = 0;
_show_level = false;
_all_collectors_known = false;
_level_index = 0;
}
@ -130,13 +146,20 @@ PStatView::
// reporting only the collector and its immediate
// parents.
//
// When you constrain the view, you may also specify
// whether the view should show time data or level data
// for the indicated collector. If level data, it
// reports the levels for the collector, and all of its
// children; otherwise, it collects the elapsed time.
//
// Changing the constraint causes the current frame's
// data to become invalidated; you must then call
// set_to_frame() again to get any useful data out.
////////////////////////////////////////////////////////////////////
void PStatView::
constrain(int collector) {
constrain(int collector, bool show_level) {
_constraint = collector;
_show_level = show_level;
clear_levels();
}
@ -148,7 +171,7 @@ constrain(int collector) {
////////////////////////////////////////////////////////////////////
void PStatView::
unconstrain() {
constrain(0);
constrain(0, false);
}
////////////////////////////////////////////////////////////////////
@ -184,95 +207,10 @@ set_to_frame(const PStatFrameData &frame_data) {
nassertv(!_thread_data.is_null());
nassertv(!_client_data.is_null());
int num_events = frame_data.get_num_events();
typedef vector<FrameSample> Samples;
Samples samples(_client_data->get_num_collectors());
FrameSample::Started started;
_all_collectors_known = true;
// This tracks the set of samples we actually care about.
typedef set<int> GotSamples;
GotSamples got_samples;
int i;
for (i = 0; i < num_events; i++) {
int collector_index = (frame_data.get_collector(i) & 0x7fff);
if (!_client_data->has_collector(collector_index)) {
_all_collectors_known = false;
} else {
nassertv(collector_index >= 0 && collector_index < (int)samples.size());
if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
// Here's a data point we care about: anything at constraint
// level or below.
samples[collector_index].data_point(frame_data.get_time(i), started);
got_samples.insert(collector_index);
}
}
}
// Make sure everything is stopped.
Samples::iterator si;
for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
if ((*si)._is_started) {
nout << _client_data->get_collector_fullname(i)
<< " was not stopped at frame end!\n";
(*si).data_point(frame_data.get_end(), started);
}
}
nassertv(started.empty());
bool any_new_levels = false;
// Now match these samples we got up with those we already had in
// the levels.
Levels::iterator li, lnext;
li = _levels.begin();
while (li != _levels.end()) {
// Be careful while traversing a container and calling functions
// that could modify that container.
lnext = li;
++lnext;
PStatViewLevel *level = (*li).second;
if (reset_level(level)) {
any_new_levels = true;
}
int collector_index = level->_collector;
GotSamples::iterator gi;
gi = got_samples.find(collector_index);
if (gi != got_samples.end()) {
level->_time_alone = samples[collector_index]._net_time;
got_samples.erase(gi);
}
li = lnext;
}
// Finally, any samples left over in the got_samples set are new
// collectors that we need to add to the Levels list.
if (!got_samples.empty()) {
any_new_levels = true;
GotSamples::const_iterator gi;
for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
int collector_index = (*gi);
PStatViewLevel *level = get_level(collector_index);
level->_time_alone = samples[*gi]._net_time;
}
}
if (any_new_levels) {
_level_index++;
if (_show_level) {
update_level_data(frame_data);
} else {
update_time_data(frame_data);
}
}
@ -291,19 +229,19 @@ all_collectors_known() const {
}
////////////////////////////////////////////////////////////////////
// Function: PStatView::get_net_time
// Function: PStatView::get_net_value
// Access: Public
// Description: Returns the total time accounted for by the frame (or
// Description: Returns the total value accounted for by the frame (or
// by whatever Collector we are constrained to). This
// is the sum of all of the individual levels'
// get_net_time() value.
// get_net_value() value.
////////////////////////////////////////////////////////////////////
double PStatView::
get_net_time() const {
double net = 0.0;
float PStatView::
get_net_value() const {
float net = 0.0;
Levels::const_iterator li;
for (li = _levels.begin(); li != _levels.end(); ++li) {
net += (*li).second->_time_alone;
net += (*li).second->_value_alone;
}
return net;
@ -362,6 +300,211 @@ get_level(int collector) {
return level;
}
////////////////////////////////////////////////////////////////////
// Function: PStatView::update_time_data
// Access: Private
// Description: The implementation of set_to_frame() for views that
// show elapsed time.
////////////////////////////////////////////////////////////////////
void PStatView::
update_time_data(const PStatFrameData &frame_data) {
int num_events = frame_data.get_num_events();
typedef vector<FrameSample> Samples;
Samples samples(_client_data->get_num_collectors());
FrameSample::Started started;
_all_collectors_known = true;
// This tracks the set of samples we actually care about.
typedef set<int> GotSamples;
GotSamples got_samples;
int i;
for (i = 0; i < num_events; i++) {
int collector_index = frame_data.get_time_collector(i);
bool is_start = frame_data.is_start(i);
if (!_client_data->has_collector(collector_index)) {
_all_collectors_known = false;
} else {
nassertv(collector_index >= 0 && collector_index < (int)samples.size());
if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
// Here's a data point we care about: anything at constraint
// level or below.
samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
got_samples.insert(collector_index);
}
}
}
// Make sure everything is stopped.
Samples::iterator si;
for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
if ((*si)._is_started) {
nout << _client_data->get_collector_fullname(i)
<< " was not stopped at frame end!\n";
(*si).data_point(frame_data.get_end(), false, started);
}
}
nassertv(started.empty());
bool any_new_levels = false;
// Now match these samples we got up with those we already had in
// the levels.
Levels::iterator li, lnext;
li = _levels.begin();
while (li != _levels.end()) {
// Be careful while traversing a container and calling functions
// that could modify that container.
lnext = li;
++lnext;
PStatViewLevel *level = (*li).second;
if (reset_level(level)) {
any_new_levels = true;
}
int collector_index = level->_collector;
GotSamples::iterator gi;
gi = got_samples.find(collector_index);
if (gi != got_samples.end()) {
level->_value_alone = samples[collector_index]._net_time;
got_samples.erase(gi);
}
li = lnext;
}
// Finally, any samples left over in the got_samples set are new
// collectors that we need to add to the Levels list.
if (!got_samples.empty()) {
any_new_levels = true;
GotSamples::const_iterator gi;
for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
int collector_index = (*gi);
PStatViewLevel *level = get_level(collector_index);
level->_value_alone = samples[*gi]._net_time;
}
}
if (any_new_levels) {
_level_index++;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatView::update_level_data
// Access: Private
// Description: The implementation of set_to_frame() for views that
// show level values.
////////////////////////////////////////////////////////////////////
void PStatView::
update_level_data(const PStatFrameData &frame_data) {
_all_collectors_known = true;
// This tracks the set of level values we got.
typedef map<int, float> GotValues;
GotValues net_values;
int i;
int num_levels = frame_data.get_num_levels();
for (i = 0; i < num_levels; i++) {
int collector_index = frame_data.get_level_collector(i);
float value = frame_data.get_level(i);
if (!_client_data->has_collector(collector_index)) {
_all_collectors_known = false;
} else {
if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
net_values[collector_index] = value;
}
}
}
// Now that we've counted up the net level for each collector,
// compute the level for each collector alone by subtracting out
// each child from its parents. If a parent has no data, nothing is
// subtracted.
GotValues alone_values = net_values;
GotValues::iterator gi;
for (gi = net_values.begin(); gi != net_values.end(); ++gi) {
int collector_index = (*gi).first;
float value = (*gi).second;
// Walk up to the top.
while (collector_index != 0 && collector_index != _constraint) {
const PStatCollectorDef &def =
_client_data->get_collector_def(collector_index);
int parent_index = def._parent_index;
GotValues::iterator pi = alone_values.find(parent_index);
if (pi != alone_values.end()) {
// The parent has data; subtract it.
(*pi).second -= value;
}
collector_index = parent_index;
}
}
bool any_new_levels = false;
// Now match these samples we got up with those we already had in
// the levels.
Levels::iterator li, lnext;
li = _levels.begin();
while (li != _levels.end()) {
// Be careful while traversing a container and calling functions
// that could modify that container.
lnext = li;
++lnext;
PStatViewLevel *level = (*li).second;
if (reset_level(level)) {
any_new_levels = true;
}
int collector_index = level->_collector;
GotValues::iterator gi;
gi = alone_values.find(collector_index);
if (gi != alone_values.end()) {
level->_value_alone = (*gi).second;
alone_values.erase(gi);
}
li = lnext;
}
// Finally, any values left over in the alone_values set are new
// collectors that we need to add to the Levels list.
if (!alone_values.empty()) {
any_new_levels = true;
GotValues::const_iterator gi;
for (gi = alone_values.begin(); gi != alone_values.end(); ++gi) {
int collector_index = (*gi).first;
PStatViewLevel *level = get_level(collector_index);
level->_value_alone = (*gi).second;
}
}
if (any_new_levels) {
_level_index++;
}
}
////////////////////////////////////////////////////////////////////
// Function: PStatView::clear_levels
// Access: Private
@ -379,7 +522,7 @@ clear_levels() {
////////////////////////////////////////////////////////////////////
// Function: PStatView::reset_level
// Access: Private
// Description: Resets the total time of the Level to zero, and also
// Description: Resets the total value of the Level to zero, and also
// makes sure it is parented to the right Level
// corresponding to its Collector's parent. Since the
// client might change its mind from time to time about
@ -392,7 +535,7 @@ clear_levels() {
bool PStatView::
reset_level(PStatViewLevel *level) {
bool any_changed = false;
level->_time_alone = 0.0;
level->_value_alone = 0.0;
if (level->_collector == _constraint) {
return false;

View File

@ -27,7 +27,7 @@ public:
PStatView();
~PStatView();
void constrain(int collector);
void constrain(int collector, bool show_level);
void unconstrain();
void set_thread_data(const PStatThreadData *thread_data);
@ -36,10 +36,10 @@ public:
void set_to_frame(const PStatFrameData &frame_data);
INLINE void set_to_frame(int frame_number);
INLINE void set_to_time(double time);
INLINE void set_to_time(float time);
bool all_collectors_known() const;
double get_net_time() const;
float get_net_value() const;
const PStatViewLevel *get_top_level();
@ -49,10 +49,14 @@ public:
INLINE int get_level_index() const;
private:
void update_time_data(const PStatFrameData &frame_data);
void update_level_data(const PStatFrameData &frame_data);
void clear_levels();
bool reset_level(PStatViewLevel *level);
int _constraint;
bool _show_level;
bool _all_collectors_known;
typedef map<int, PStatViewLevel *> Levels;

View File

@ -16,13 +16,13 @@ get_collector() const {
}
////////////////////////////////////////////////////////////////////
// Function: PStatViewLevel::get_time_alone
// Function: PStatViewLevel::get_value_alone
// Access: Public
// Description: Returns the total elapsed time spent by this
// Collector, not including any time spent in its child
// Collectors.
// Description: Returns the total level value (or elapsed time value)
// for this Collector, not including any values
// accounted for by its child Collectors.
////////////////////////////////////////////////////////////////////
INLINE double PStatViewLevel::
get_time_alone() const {
return _time_alone;
INLINE float PStatViewLevel::
get_value_alone() const {
return _value_alone;
}

View File

@ -12,19 +12,19 @@
#include <algorithm>
////////////////////////////////////////////////////////////////////
// Function: PStatViewLevel::get_net_time
// Function: PStatViewLevel::get_net_value
// Access: Public
// Description: Returns the total elapsed time spent by this
// Collector, including all time spent in its child
// Collectors.
// Description: Returns the total level value (or elapsed time)
// represented by this Collector, including all values
// in its child Collectors.
////////////////////////////////////////////////////////////////////
double PStatViewLevel::
get_net_time() const {
double net = _time_alone;
float PStatViewLevel::
get_net_value() const {
float net = _value_alone;
Children::const_iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) {
net += (*ci)->get_net_time();
net += (*ci)->get_net_value();
}
return net;
@ -66,9 +66,9 @@ sort_children(const PStatClientData *client_data) {
// Function: PStatViewLevel::get_num_children
// Access: Public
// Description: Returns the number of children of this
// Level/Collector. These are the Collectors whose time
// is considered to be part of the total time of this
// level's Collector.
// Level/Collector. These are the Collectors whose
// value is considered to be part of the total value of
// this level's Collector.
////////////////////////////////////////////////////////////////////
int PStatViewLevel::
get_num_children() const {

View File

@ -15,15 +15,17 @@ class PStatClientData;
////////////////////////////////////////////////////////////////////
// Class : PStatViewLevel
// Description : This is a single level value, or band of color,
// within a View. It generally indicates the elapsed
// time for a particular Collector within a given frame
// for a particular thread.
// within a View.
//
// It generally indicates either the elapsed time, or
// the "level" value, for a particular Collector within
// a given frame for a particular thread.
////////////////////////////////////////////////////////////////////
class PStatViewLevel {
public:
INLINE int get_collector() const;
INLINE double get_time_alone() const;
double get_net_time() const;
INLINE float get_value_alone() const;
float get_net_value() const;
void sort_children(const PStatClientData *client_data);
@ -32,7 +34,7 @@ public:
private:
int _collector;
double _time_alone;
float _value_alone;
PStatViewLevel *_parent;
typedef vector<PStatViewLevel *> Children;

View File

@ -61,7 +61,7 @@ new_data(int thread_index, int frame_number) {
nout << "\rThread "
<< get_client_data()->get_thread_name(thread_index)
<< " frame " << frame_number << ", "
<< view.get_net_time() * 1000.0 << " ms ("
<< view.get_net_value() * 1000.0 << " ms ("
<< thread_data->get_frame_rate() << " Hz):\n";
const PStatViewLevel *level = view.get_top_level();
int num_children = level->get_num_children();
@ -113,7 +113,7 @@ show_level(const PStatViewLevel *level, int indent_level) {
indent(nout, indent_level)
<< get_client_data()->get_collector_name(collector_index)
<< " = " << level->get_net_time() * 1000.0 << " ms\n";
<< " = " << level->get_net_value() * 1000.0 << " ms\n";
int num_children = level->get_num_children();
for (int i = 0; i < num_children; i++) {