diff --git a/panda/src/char/character.cxx b/panda/src/char/character.cxx index da5afea27d..6154aed23f 100644 --- a/panda/src/char/character.cxx +++ b/panda/src/char/character.cxx @@ -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 //////////////////////////////////////////////////////////////////// diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index c1ac6d9906..65c10de9d9 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -19,7 +19,7 @@ #include #ifndef CPPPARSER -PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions", RGBColorf(1,0.5,0), 40); +PStatCollector CollisionTraverser::_collisions_pcollector("App:Collisions"); #endif //////////////////////////////////////////////////////////////////// diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index e481bb68a7..5128851120 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -17,7 +17,6 @@ #include #include #include - #include #include diff --git a/panda/src/cull/cullTraverser.cxx b/panda/src/cull/cullTraverser.cxx index 78e5f36db7..487a2fdd85 100644 --- a/panda/src/cull/cullTraverser.cxx +++ b/panda/src/cull/cullTraverser.cxx @@ -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 //////////////////////////////////////////////////////////////////// diff --git a/panda/src/dgraph/dataGraphTraversal.cxx b/panda/src/dgraph/dataGraphTraversal.cxx index 0a86d2d76f..f8be485820 100644 --- a/panda/src/dgraph/dataGraphTraversal.cxx +++ b/panda/src/dgraph/dataGraphTraversal.cxx @@ -9,7 +9,7 @@ #include #ifndef CPPPARSER -PStatCollector _dgraph_pcollector("App:Data Graph", RGBColorf(0.5,0.8,0.4), 30); +PStatCollector _dgraph_pcollector("App:Data graph"); #endif //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 95cf2cc4e6..68f94b6d46 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -6,12 +6,21 @@ #include "graphicsStateGuardian.h" #include "renderBuffer.h" #include "config_display.h" +#include "textureContext.h" #include #include #include + +#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; diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 7955af2b5b..25cb3b775b 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -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 _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 Textures; Textures _prepared_textures; // NOTE: on win32 another DLL (e.g. libpandadx.dll) cannot access set directly due to exported template issue diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 5c8ccde4fa..c2d5b6572c 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -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 //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/textureContext.cxx b/panda/src/display/textureContext.cxx index 59cc2f9151..592950241e 100644 --- a/panda/src/display/textureContext.cxx +++ b/panda/src/display/textureContext.cxx @@ -5,4 +5,81 @@ #include "textureContext.h" +#include +#include + 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; +} diff --git a/panda/src/display/textureContext.h b/panda/src/display/textureContext.h index 9d8850c8a3..a924186c19 100644 --- a/panda/src/display/textureContext.h +++ b/panda/src/display/textureContext.h @@ -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. diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index 14f8f6017c..a94f60aefe 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -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(); } diff --git a/panda/src/pstatclient/Sources.pp b/panda/src/pstatclient/Sources.pp index a8494f3121..5aa13848e2 100644 --- a/panda/src/pstatclient/Sources.pp +++ b/panda/src/pstatclient/Sources.pp @@ -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 \ diff --git a/panda/src/pstatclient/config_pstats.cxx b/panda/src/pstatclient/config_pstats.cxx index 28fc501e70..e05b61be7a 100644 --- a/panda/src/pstatclient/config_pstats.cxx +++ b/panda/src/pstatclient/config_pstats.cxx @@ -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 diff --git a/panda/src/pstatclient/config_pstats.h b/panda/src/pstatclient/config_pstats.h index e6e01919a1..2bfb43ffb7 100644 --- a/panda/src/pstatclient/config_pstats.h +++ b/panda/src/pstatclient/config_pstats.h @@ -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(); diff --git a/panda/src/pstatclient/pStatClient.I b/panda/src/pstatclient/pStatClient.I index 5f3898165b..807110f02b 100644 --- a/panda/src/pstatclient/pStatClient.I +++ b/panda/src/pstatclient/pStatClient.I @@ -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; } diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 6d59679c65..b5b3aae6dc 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -14,6 +14,7 @@ #include "pStatCollector.h" #include "pStatThread.h" #include "config_pstats.h" +#include "pStatProperties.h" #include @@ -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); diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index 7f0cf3433e..1c8461d60a 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -11,10 +11,10 @@ #include "pStatFrameData.h" #include -#include #include +#include -#ifdef DO_PSTATS +#ifdef HAVE_NET #include #include #include @@ -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 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 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 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 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; diff --git a/panda/src/pstatclient/pStatCollector.I b/panda/src/pstatclient/pStatCollector.I index d918b0e89b..1cdaca9e3b 100644 --- a/panda/src/pstatclient/pStatCollector.I +++ b/panda/src/pstatclient/pStatCollector.I @@ -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 diff --git a/panda/src/pstatclient/pStatCollector.h b/panda/src/pstatclient/pStatCollector.h index 5a9236b49c..b74742343b 100644 --- a/panda/src/pstatclient/pStatCollector.h +++ b/panda/src/pstatclient/pStatCollector.h @@ -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 ©); INLINE void operator = (const PStatCollector ©); 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 }; diff --git a/panda/src/pstatclient/pStatCollectorDef.cxx b/panda/src/pstatclient/pStatCollectorDef.cxx index cd6af8caaf..f6ae6aeeb1 100644 --- a/panda/src/pstatclient/pStatCollectorDef.cxx +++ b/panda/src/pstatclient/pStatCollectorDef.cxx @@ -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(); } diff --git a/panda/src/pstatclient/pStatCollectorDef.h b/panda/src/pstatclient/pStatCollectorDef.h index b933b272c8..20d9b33d17 100644 --- a/panda/src/pstatclient/pStatCollectorDef.h +++ b/panda/src/pstatclient/pStatCollectorDef.h @@ -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 diff --git a/panda/src/pstatclient/pStatFrameData.I b/panda/src/pstatclient/pStatFrameData.I index d726e0e528..dbf8ce46ef 100644 --- a/panda/src/pstatclient/pStatFrameData.I +++ b/panda/src/pstatclient/pStatFrameData.I @@ -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; } diff --git a/panda/src/pstatclient/pStatFrameData.cxx b/panda/src/pstatclient/pStatFrameData.cxx index fc60e602af..0ff8bfc380 100644 --- a/panda/src/pstatclient/pStatFrameData.cxx +++ b/panda/src/pstatclient/pStatFrameData.cxx @@ -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); } } diff --git a/panda/src/pstatclient/pStatFrameData.h b/panda/src/pstatclient/pStatFrameData.h index b33bd5e62f..5bb5022b69 100644 --- a/panda/src/pstatclient/pStatFrameData.h +++ b/panda/src/pstatclient/pStatFrameData.h @@ -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 Data; - Data _data; + Data _time_data, _level_data; }; #include "pStatFrameData.I" diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx new file mode 100644 index 0000000000..c49d530289 --- /dev/null +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -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 diff --git a/panda/src/pstatclient/pStatProperties.h b/panda/src/pstatclient/pStatProperties.h new file mode 100644 index 0000000000..617a7c091e --- /dev/null +++ b/panda/src/pstatclient/pStatProperties.h @@ -0,0 +1,21 @@ +// Filename: pStatProperties.h +// Created by: drose (17May01) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSTATPROPERTIES_H +#define PSTATPROPERTIES_H + +#include + +#ifdef DO_PSTATS + +class PStatClient; +class PStatCollectorDef; + +void initialize_collector_def(PStatClient *client, PStatCollectorDef *def); + +#endif // DO_PSTATS + +#endif + diff --git a/panda/src/pstatclient/test_client.cxx b/panda/src/pstatclient/test_client.cxx index c259c606ff..66c340f9e1 100644 --- a/panda/src/pstatclient/test_client.cxx +++ b/panda/src/pstatclient/test_client.cxx @@ -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 _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 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 diff --git a/panda/src/sgraphutil/directRenderTraverser.cxx b/panda/src/sgraphutil/directRenderTraverser.cxx index 8d9c069d74..95d80e2199 100644 --- a/panda/src/sgraphutil/directRenderTraverser.cxx +++ b/panda/src/sgraphutil/directRenderTraverser.cxx @@ -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 //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/gtk-stats/gtkStatsGuide.cxx b/pandatool/src/gtk-stats/gtkStatsGuide.cxx index 152e74162e..d0a2ad8541 100644 --- a/pandatool/src/gtk-stats/gtkStatsGuide.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGuide.cxx @@ -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); } diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index 66e263749f..290867b40f 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -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; diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index 74d7424407..e5e46c6144 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -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; } //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.h b/pandatool/src/gtk-stats/gtkStatsMonitor.h index 399d27a463..fc937a6a43 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.h +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.h @@ -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 diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 2774d00796..8224167c19 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -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. diff --git a/pandatool/src/gtk-stats/gtkStatsPianoWindow.cxx b/pandatool/src/gtk-stats/gtkStatsPianoWindow.cxx index 685bc3fed2..e0ad296392 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoWindow.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoWindow.cxx @@ -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); } diff --git a/pandatool/src/gtk-stats/gtkStatsPianoWindow.h b/pandatool/src/gtk-stats/gtkStatsPianoWindow.h index 1ed54b610f..e5d5c136bd 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoWindow.h +++ b/pandatool/src/gtk-stats/gtkStatsPianoWindow.h @@ -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); diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index 00f4b26dab..6486a33309 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -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; diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h index d0791951a2..0cb13584f3 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.h +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h @@ -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 collector_picked; diff --git a/pandatool/src/gtk-stats/gtkStatsStripWindow.cxx b/pandatool/src/gtk-stats/gtkStatsStripWindow.cxx index ec26aa2da5..cf57cb58ec 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripWindow.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripWindow.cxx @@ -7,7 +7,9 @@ #include "gtkStatsStripChart.h" #include "gtkStatsGuide.h" +#include #include + #include // 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; } diff --git a/pandatool/src/gtk-stats/gtkStatsStripWindow.h b/pandatool/src/gtk-stats/gtkStatsStripWindow.h index 5cbf50ef10..71455913b4 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripWindow.h +++ b/pandatool/src/gtk-stats/gtkStatsStripWindow.h @@ -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; }; diff --git a/pandatool/src/gtk-stats/gtkStatsWindow.cxx b/pandatool/src/gtk-stats/gtkStatsWindow.cxx index 338cd2a502..5ec9eef36d 100644 --- a/pandatool/src/gtk-stats/gtkStatsWindow.cxx +++ b/pandatool/src/gtk-stats/gtkStatsWindow.cxx @@ -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); } //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/gtk-stats/gtkStatsWindow.h b/pandatool/src/gtk-stats/gtkStatsWindow.h index e56756f52e..7ffd830b1b 100644 --- a/pandatool/src/gtk-stats/gtkStatsWindow.h +++ b/pandatool/src/gtk-stats/gtkStatsWindow.h @@ -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: diff --git a/pandatool/src/pstatserver/pStatClientData.cxx b/pandatool/src/pstatserver/pStatClientData.cxx index d8790c1407..6bed0fcbca 100644 --- a/pandatool/src/pstatserver/pStatClientData.cxx +++ b/pandatool/src/pstatserver/pStatClientData.cxx @@ -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); + } +} diff --git a/pandatool/src/pstatserver/pStatClientData.h b/pandatool/src/pstatserver/pStatClientData.h index 64a95703d0..aedf9ddf29 100644 --- a/pandatool/src/pstatserver/pStatClientData.h +++ b/pandatool/src/pstatserver/pStatClientData.h @@ -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 Collectors; + class Collector { + public: + PStatCollectorDef *_def; + bool _is_level; + }; + + typedef vector Collectors; Collectors _collectors; class Thread { diff --git a/pandatool/src/pstatserver/pStatGraph.I b/pandatool/src/pstatserver/pStatGraph.I index 70290aed1b..72a97e146c 100644 --- a/pandatool/src/pstatserver/pStatGraph.I +++ b/pandatool/src/pstatserver/pStatGraph.I @@ -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; +} diff --git a/pandatool/src/pstatserver/pStatGraph.cxx b/pandatool/src/pstatserver/pStatGraph.cxx index aeb337fb6d..15ed252f8b 100644 --- a/pandatool/src/pstatserver/pStatGraph.cxx +++ b/pandatool/src/pstatserver/pStatGraph.cxx @@ -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); } diff --git a/pandatool/src/pstatserver/pStatGraph.h b/pandatool/src/pstatserver/pStatGraph.h index c3f90b8656..1fa07ca440 100644 --- a/pandatool/src/pstatserver/pStatGraph.h +++ b/pandatool/src/pstatserver/pStatGraph.h @@ -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 ©); - 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 GuideBars; GuideBars _guide_bars; int _guide_bar_units; + string _unit_name; }; #include "pStatGraph.I" diff --git a/pandatool/src/pstatserver/pStatMonitor.cxx b/pandatool/src/pstatserver/pStatMonitor.cxx index 4b1d11a5aa..b5e0115a2c 100644 --- a/pandatool/src/pstatserver/pStatMonitor.cxx +++ b/pandatool/src/pstatserver/pStatMonitor.cxx @@ -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 diff --git a/pandatool/src/pstatserver/pStatMonitor.h b/pandatool/src/pstatserver/pStatMonitor.h index cd5d2615be..4e649e3340 100644 --- a/pandatool/src/pstatserver/pStatMonitor.h +++ b/pandatool/src/pstatserver/pStatMonitor.h @@ -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 Views; Views _views; + typedef map LevelViews; + LevelViews _level_views; typedef map Colors; Colors _colors; diff --git a/pandatool/src/pstatserver/pStatPianoRoll.I b/pandatool/src/pstatserver/pStatPianoRoll.I index b4aa138f5f..e733533719 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.I +++ b/pandatool/src/pstatserver/pStatPianoRoll.I @@ -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; } diff --git a/pandatool/src/pstatserver/pStatPianoRoll.cxx b/pandatool/src/pstatserver/pStatPianoRoll.cxx index 32f52812c5..4efa7efe88 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.cxx +++ b/pandatool/src/pstatserver/pStatPianoRoll.cxx @@ -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); } diff --git a/pandatool/src/pstatserver/pStatPianoRoll.h b/pandatool/src/pstatserver/pStatPianoRoll.h index d95f2e15f2..1111c66e3d 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.h +++ b/pandatool/src/pstatserver/pStatPianoRoll.h @@ -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 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; diff --git a/pandatool/src/pstatserver/pStatReader.cxx b/pandatool/src/pstatserver/pStatReader.cxx index aff96e4c53..43d6b39346 100644 --- a/pandatool/src/pstatserver/pStatReader.cxx +++ b/pandatool/src/pstatserver/pStatReader.cxx @@ -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); } diff --git a/pandatool/src/pstatserver/pStatStripChart.I b/pandatool/src/pstatserver/pStatStripChart.I index 9c8e2ab836..2ae5ad2746 100644 --- a/pandatool/src/pstatserver/pStatStripChart.I +++ b/pandatool/src/pstatserver/pStatStripChart.I @@ -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(); } diff --git a/pandatool/src/pstatserver/pStatStripChart.cxx b/pandatool/src/pstatserver/pStatStripChart.cxx index 2568e52e44..ee91bcb4c0 100644 --- a/pandatool/src/pstatserver/pStatStripChart.cxx +++ b/pandatool/src/pstatserver/pStatStripChart.cxx @@ -4,13 +4,14 @@ //////////////////////////////////////////////////////////////////// #include "pStatStripChart.h" +#include "pStatClientData.h" +#include "pStatMonitor.h" #include #include #include #include -#include // for sprintf #include //////////////////////////////////////////////////////////////////// @@ -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)) { diff --git a/pandatool/src/pstatserver/pStatStripChart.h b/pandatool/src/pstatserver/pStatStripChart.h index ab611bd322..c6d1a263c1 100644 --- a/pandatool/src/pstatserver/pStatStripChart.h +++ b/pandatool/src/pstatserver/pStatStripChart.h @@ -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 FrameData; typedef map 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" diff --git a/pandatool/src/pstatserver/pStatThreadData.cxx b/pandatool/src/pstatserver/pStatThreadData.cxx index 91f5251f18..627d202bfd 100644 --- a/pandatool/src/pstatserver/pStatThreadData.cxx +++ b/pandatool/src/pstatserver/pStatThreadData.cxx @@ -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() || diff --git a/pandatool/src/pstatserver/pStatThreadData.h b/pandatool/src/pstatserver/pStatThreadData.h index aa232d3f37..4deaccb971 100644 --- a/pandatool/src/pstatserver/pStatThreadData.h +++ b/pandatool/src/pstatserver/pStatThreadData.h @@ -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 Frames; Frames _frames; int _first_frame_number; - double _history; + float _history; static PStatFrameData _null_frame; }; diff --git a/pandatool/src/pstatserver/pStatView.I b/pandatool/src/pstatserver/pStatView.I index bfc4652038..975251869a 100644 --- a/pandatool/src/pstatserver/pStatView.I +++ b/pandatool/src/pstatserver/pStatView.I @@ -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)); } diff --git a/pandatool/src/pstatserver/pStatView.cxx b/pandatool/src/pstatserver/pStatView.cxx index bcd9b86a4f..19b7400884 100644 --- a/pandatool/src/pstatserver/pStatView.cxx +++ b/pandatool/src/pstatserver/pStatView.cxx @@ -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 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 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 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 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 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; diff --git a/pandatool/src/pstatserver/pStatView.h b/pandatool/src/pstatserver/pStatView.h index c9413aceb2..4e79e33415 100644 --- a/pandatool/src/pstatserver/pStatView.h +++ b/pandatool/src/pstatserver/pStatView.h @@ -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 Levels; diff --git a/pandatool/src/pstatserver/pStatViewLevel.I b/pandatool/src/pstatserver/pStatViewLevel.I index e68eb57429..b58ba0bd84 100644 --- a/pandatool/src/pstatserver/pStatViewLevel.I +++ b/pandatool/src/pstatserver/pStatViewLevel.I @@ -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; } diff --git a/pandatool/src/pstatserver/pStatViewLevel.cxx b/pandatool/src/pstatserver/pStatViewLevel.cxx index fd66996cd5..6e13309a06 100644 --- a/pandatool/src/pstatserver/pStatViewLevel.cxx +++ b/pandatool/src/pstatserver/pStatViewLevel.cxx @@ -12,19 +12,19 @@ #include //////////////////////////////////////////////////////////////////// -// 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 { diff --git a/pandatool/src/pstatserver/pStatViewLevel.h b/pandatool/src/pstatserver/pStatViewLevel.h index aecf845062..014f60f17e 100644 --- a/pandatool/src/pstatserver/pStatViewLevel.h +++ b/pandatool/src/pstatserver/pStatViewLevel.h @@ -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 Children; diff --git a/pandatool/src/text-stats/textMonitor.cxx b/pandatool/src/text-stats/textMonitor.cxx index feeba583e8..2310a63458 100644 --- a/pandatool/src/text-stats/textMonitor.cxx +++ b/pandatool/src/text-stats/textMonitor.cxx @@ -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++) {