Panda3D

sceneGraphAnalyzer.cxx

00001 // Filename: sceneGraphAnalyzer.cxx
00002 // Created by:  drose (02Jul00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "sceneGraphAnalyzer.h"
00016 #include "config_pgraph.h"
00017 
00018 #include "indent.h"
00019 #include "lodNode.h"
00020 #include "geomNode.h"
00021 #include "geomVertexData.h"
00022 #include "geom.h"
00023 #include "geomPrimitive.h"
00024 #include "geomPoints.h"
00025 #include "geomLines.h"
00026 #include "geomLinestrips.h"
00027 #include "geomTriangles.h"
00028 #include "geomTristrips.h"
00029 #include "geomTrifans.h"
00030 #include "transformState.h"
00031 #include "textureAttrib.h"
00032 #include "pta_ushort.h"
00033 #include "geomVertexReader.h"
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: SceneGraphAnalyzer::Constructor
00037 //       Access: Published
00038 //  Description:
00039 ////////////////////////////////////////////////////////////////////
00040 SceneGraphAnalyzer::
00041 SceneGraphAnalyzer() {
00042   _lod_mode = LM_all;
00043   clear();
00044 }
00045 
00046 ////////////////////////////////////////////////////////////////////
00047 //     Function: SceneGraphAnalyzer::Destructor
00048 //       Access: Published
00049 //  Description:
00050 ////////////////////////////////////////////////////////////////////
00051 SceneGraphAnalyzer::
00052 ~SceneGraphAnalyzer() {
00053 }
00054 
00055 ////////////////////////////////////////////////////////////////////
00056 //     Function: SceneGraphAnalyzer::clear
00057 //       Access: Published
00058 //  Description: Resets all of the data in the analyzer in preparation
00059 //               for a new run.
00060 ////////////////////////////////////////////////////////////////////
00061 void SceneGraphAnalyzer::
00062 clear() {
00063   _nodes.clear();
00064   _vdatas.clear();
00065   _vformats.clear();
00066   _vadatas.clear();
00067   _unique_vdatas.clear();
00068   _unique_vadatas.clear();
00069   _textures.clear();
00070 
00071   _num_nodes = 0;
00072   _num_instances = 0;
00073   _num_transforms = 0;
00074   _num_nodes_with_attribs = 0;
00075   _num_lod_nodes = 0;
00076   _num_geom_nodes = 0;
00077   _num_geoms = 0;
00078   _num_geom_vertex_datas = 0;
00079   _num_geom_vertex_formats = 0;
00080   _vertex_data_size = 0;
00081   _prim_data_size = 0;
00082 
00083   _num_vertices = 0;
00084   _num_normals = 0;
00085   _num_colors = 0;
00086   _num_texcoords = 0;
00087   _num_tris = 0;
00088   _num_lines = 0;
00089   _num_points = 0;
00090 
00091   _num_individual_tris = 0;
00092   _num_tristrips = 0;
00093   _num_triangles_in_strips = 0;
00094   _num_trifans = 0;
00095   _num_triangles_in_fans = 0;
00096 
00097   _texture_bytes = 0;
00098 
00099   _num_long_normals = 0;
00100   _num_short_normals = 0;
00101   _total_normal_length = 0.0f;
00102 }
00103 
00104 ////////////////////////////////////////////////////////////////////
00105 //     Function: SceneGraphAnalyzer::add_node
00106 //       Access: Published
00107 //  Description: Adds a new node to the set of data for analysis.
00108 //               Normally, this would only be called once, and passed
00109 //               the top of the scene graph, but it's possible to
00110 //               repeatedly pass in subgraphs to get an analysis of
00111 //               all the graphs together.
00112 ////////////////////////////////////////////////////////////////////
00113 void SceneGraphAnalyzer::
00114 add_node(PandaNode *node) {
00115   collect_statistics(node, false);
00116 }
00117 
00118 ////////////////////////////////////////////////////////////////////
00119 //     Function: SceneGraphAnalyzer::write
00120 //       Access: Published
00121 //  Description: Describes all the data collected.
00122 ////////////////////////////////////////////////////////////////////
00123 void SceneGraphAnalyzer::
00124 write(ostream &out, int indent_level) const {
00125   indent(out, indent_level)
00126     << _num_nodes << " total nodes (including "
00127     << _num_instances << " instances); " << _num_lod_nodes << " LODNodes.\n";
00128 
00129   indent(out, indent_level)
00130     << _num_transforms << " transforms";
00131 
00132   if (_num_nodes != 0) {
00133     out << "; " << 100 * _num_nodes_with_attribs / _num_nodes
00134         << "% of nodes have some render attribute.";
00135   }
00136   out << "\n";
00137 
00138   indent(out, indent_level)
00139     << _num_geoms << " Geoms, with " << _num_geom_vertex_datas 
00140     << " GeomVertexDatas and " << _num_geom_vertex_formats 
00141     << " GeomVertexFormats, appear on " << _num_geom_nodes
00142     << " GeomNodes.\n";
00143 
00144   indent(out, indent_level)
00145     << _num_vertices << " vertices, " << _num_normals << " normals, "
00146     << _num_colors << " colors, "
00147     << _num_texcoords << " texture coordinates.\n";
00148 
00149   if (_num_long_normals != 0 || _num_short_normals != 0) {
00150     indent(out, indent_level)
00151       << _num_long_normals << " normals are too long, "
00152       << _num_short_normals << " are too short.  Average normal length is "
00153       << _total_normal_length / (float)_num_normals << "\n";
00154   }
00155 
00156   indent(out, indent_level)
00157     << "GeomVertexData arrays occupy " << (_vertex_data_size + 1023) / 1024 
00158     << "K memory.\n";
00159 
00160   indent(out, indent_level)
00161     << "GeomPrimitive arrays occupy " << (_prim_data_size + 1023) / 1024 
00162     << "K memory.\n";
00163 
00164   int unreferenced_vertices = 0;
00165   VDatas::const_iterator vdi;
00166   for (vdi = _vdatas.begin(); vdi != _vdatas.end(); ++vdi) {
00167     CPT(GeomVertexData) vdata = (*vdi).first;
00168     const VDataTracker &tracker = (*vdi).second;
00169     int num_unreferenced = vdata->get_num_rows() - tracker._referenced_vertices.get_num_on_bits();
00170     nassertv(num_unreferenced >= 0);
00171     unreferenced_vertices += num_unreferenced;
00172   }
00173   if (unreferenced_vertices != 0) {
00174     indent(out, indent_level)
00175       << unreferenced_vertices << " vertices are unreferenced by any GeomPrimitives.\n";
00176   }
00177   if (_unique_vdatas.size() != _vdatas.size()) {
00178     indent(out, indent_level)
00179       << _vdatas.size() - _unique_vdatas.size()
00180       << " GeomVertexDatas are redundantly duplicated\n";
00181   }
00182   if (_unique_vadatas.size() != _vadatas.size()) {
00183     int wasted_bytes = 0;
00184 
00185     UniqueVADatas::const_iterator uvai;
00186     for (uvai = _unique_vadatas.begin();
00187          uvai != _unique_vadatas.end();
00188          ++uvai) {
00189       const GeomVertexArrayData *gvad = (*uvai).first;
00190       int dup_count = (*uvai).second;
00191       if (dup_count > 1) {
00192         wasted_bytes += (dup_count - 1) * gvad->get_data_size_bytes();
00193       }
00194     }
00195     indent(out, indent_level)
00196       << _vadatas.size() - _unique_vadatas.size()
00197       << " GeomVertexArrayDatas are redundant, wasting " 
00198       << (wasted_bytes + 1023) / 1024 << "K.\n";
00199   }
00200   if (_unique_prim_vadatas.size() != _prim_vadatas.size()) {
00201     int wasted_bytes = 0;
00202 
00203     UniqueVADatas::const_iterator uvai;
00204     for (uvai = _unique_prim_vadatas.begin();
00205          uvai != _unique_prim_vadatas.end();
00206          ++uvai) {
00207       const GeomVertexArrayData *gvad = (*uvai).first;
00208       int dup_count = (*uvai).second;
00209       if (dup_count > 1) {
00210         wasted_bytes += (dup_count - 1) * gvad->get_data_size_bytes();
00211       }
00212     }
00213     indent(out, indent_level)
00214       << _prim_vadatas.size() - _unique_prim_vadatas.size()
00215       << " GeomPrimitive arrays are redundant, wasting " 
00216       << (wasted_bytes + 1023) / 1024 << "K.\n";
00217   }
00218 
00219   indent(out, indent_level)
00220     << _num_tris << " triangles:\n";
00221   indent(out, indent_level + 2)
00222     << _num_triangles_in_strips
00223     << " of these are on " << _num_tristrips << " tristrips";
00224   if (_num_tristrips != 0) {
00225     out << " ("
00226         << (double)_num_triangles_in_strips / (double)_num_tristrips
00227         << " average tris per strip)";
00228   }
00229   out << ".\n";
00230 
00231   if (_num_trifans != 0) {
00232     indent(out, indent_level + 2)
00233       << _num_triangles_in_fans
00234       << " of these are on " << _num_trifans << " trifans";
00235     if (_num_trifans != 0) {
00236       out << " ("
00237           << (double)_num_triangles_in_fans / (double)_num_trifans
00238           << " average tris per fan)";
00239     }
00240     out << ".\n";
00241   }
00242 
00243   indent(out, indent_level + 2)
00244     << _num_individual_tris
00245     << " of these are independent triangles.\n";
00246 
00247   if (_num_lines != 0 || _num_points != 0) {
00248     indent(out, indent_level)
00249       << _num_lines << " lines, " << _num_points << " points.\n";
00250   }
00251 
00252   indent(out, indent_level)
00253     << _textures.size() << " textures, estimated minimum "
00254     << (_texture_bytes + 1023) / 1024 << "K texture memory required.\n";
00255 }
00256 
00257 ////////////////////////////////////////////////////////////////////
00258 //     Function: SceneGraphAnalyzer::collect_statistics
00259 //       Access: Private
00260 //  Description: Recursively visits each node, counting up the
00261 //               statistics.
00262 ////////////////////////////////////////////////////////////////////
00263 void SceneGraphAnalyzer::
00264 collect_statistics(PandaNode *node, bool under_instance) {
00265   _num_nodes++;
00266 
00267   if (!under_instance) {
00268     Nodes::iterator ni = _nodes.find(node);
00269     if (ni == _nodes.end()) {
00270       // This is the first time this node has been encountered.
00271       _nodes.insert(Nodes::value_type(node, 1));
00272     } else {
00273       // This node has been encountered before; that makes it an
00274       // instance.
00275       (*ni).second++;
00276       _num_instances++;
00277       under_instance = true;
00278     }
00279   }
00280 
00281   if (!node->get_state()->is_empty()) {
00282     _num_nodes_with_attribs++;
00283     const RenderAttrib *attrib = 
00284       node->get_attrib(TextureAttrib::get_class_slot());
00285     if (attrib != (RenderAttrib *)NULL) {
00286       const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
00287       for (int i = 0; i < ta->get_num_on_stages(); i++) {
00288         collect_statistics(ta->get_on_texture(ta->get_on_stage(i)));
00289       }
00290     }      
00291   }
00292   if (!node->get_transform()->is_identity()) {
00293     _num_transforms++;
00294   }
00295 
00296   if (node->is_geom_node()) {
00297     collect_statistics(DCAST(GeomNode, node));
00298   }
00299 
00300   if (node->is_lod_node()) {
00301     LODNode *lod_node = DCAST(LODNode, node);
00302     ++_num_lod_nodes;
00303 
00304     switch (_lod_mode) {
00305     case LM_lowest:
00306     case LM_highest:
00307       {
00308         int sw = (_lod_mode == LM_lowest) ? lod_node->get_lowest_switch() : lod_node->get_highest_switch();
00309         if (sw >= 0 && sw < node->get_num_children()) {
00310           PandaNode *child = node->get_child(sw);
00311           collect_statistics(child, under_instance);
00312         }
00313         return;
00314       }
00315 
00316     case LM_none:
00317       return;
00318 
00319     case LM_all:
00320       // fall through to the loop below.
00321       break;
00322     }
00323   }
00324 
00325   int num_children = node->get_num_children();
00326   for (int i = 0; i < num_children; i++) {
00327     PandaNode *child = node->get_child(i);
00328     collect_statistics(child, under_instance);
00329   }
00330 }
00331 
00332 ////////////////////////////////////////////////////////////////////
00333 //     Function: SceneGraphAnalyzer::collect_statistics
00334 //       Access: Private
00335 //  Description: Recursively visits each node, counting up the
00336 //               statistics.
00337 ////////////////////////////////////////////////////////////////////
00338 void SceneGraphAnalyzer::
00339 collect_statistics(GeomNode *geom_node) {
00340   nassertv(geom_node != (GeomNode *)NULL);
00341 
00342   ++_num_geom_nodes;
00343 
00344   int num_geoms = geom_node->get_num_geoms();
00345   _num_geoms += num_geoms;
00346 
00347   for (int i = 0; i < num_geoms; i++) {
00348     const Geom *geom = geom_node->get_geom(i);
00349     collect_statistics(geom);
00350 
00351     const RenderState *geom_state = geom_node->get_geom_state(i);
00352 
00353     const RenderAttrib *attrib = 
00354       geom_state->get_attrib(TextureAttrib::get_class_slot());
00355     if (attrib != (RenderAttrib *)NULL) {
00356       const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
00357       for (int i = 0; i < ta->get_num_on_stages(); i++) {
00358         collect_statistics(ta->get_on_texture(ta->get_on_stage(i)));
00359       }
00360     }      
00361   }
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: SceneGraphAnalyzer::collect_statistics
00366 //       Access: Private
00367 //  Description: Recursively visits each node, counting up the
00368 //               statistics.
00369 ////////////////////////////////////////////////////////////////////
00370 void SceneGraphAnalyzer::
00371 collect_statistics(const Geom *geom) {
00372   CPT(GeomVertexData) vdata = geom->get_vertex_data();
00373   pair<VDatas::iterator, bool> result = _vdatas.insert(VDatas::value_type(vdata, VDataTracker()));
00374   if (result.second) {
00375     // This is the first time we've encountered this vertex data.
00376     ++_num_geom_vertex_datas;
00377     
00378     CPT(GeomVertexFormat) vformat = vdata->get_format();
00379     bool format_inserted = _vformats.insert(vformat).second;
00380     if (format_inserted) {
00381       // This is the first time we've encountered this vertex format.
00382       ++_num_geom_vertex_formats;
00383     }
00384 
00385     int &dup_count = (*(_unique_vdatas.insert(UniqueVDatas::value_type(vdata, 0)).first)).second;
00386     ++dup_count;
00387 
00388     int num_rows = vdata->get_num_rows();
00389     if (vdata->has_column(InternalName::get_vertex())) {
00390       _num_vertices += num_rows;
00391     }
00392     if (vdata->has_column(InternalName::get_normal())) {
00393       _num_normals += num_rows;
00394       GeomVertexReader rnormal(vdata, InternalName::get_normal());
00395       while (!rnormal.is_at_end()) {
00396         LVector3f normal = rnormal.get_data3f();
00397         float length = normal.length();
00398         if (IS_NEARLY_EQUAL(length, 1.0f)) {
00399           // Correct length normal.
00400         } else if (length > 1.0f) {
00401           ++_num_long_normals;
00402         } else {  // length < 1.0f
00403           ++_num_short_normals;
00404         }
00405         _total_normal_length += length;
00406       }
00407     }
00408     if (vdata->has_column(InternalName::get_color())) {
00409       _num_colors += num_rows;
00410     }
00411     const GeomVertexFormat *format = vdata->get_format();
00412     int num_texcoords = format->get_num_texcoords();
00413     _num_texcoords += num_rows * num_texcoords;
00414 
00415     int num_arrays = vdata->get_num_arrays();
00416     for (int i = 0; i < num_arrays; ++i) {
00417       collect_statistics(vdata->get_array(i));
00418     }
00419   }
00420   VDataTracker &tracker = (*(result.first)).second;
00421 
00422   // Now consider the primitives in the Geom.
00423   int num_primitives = geom->get_num_primitives();
00424   for (int i = 0; i < num_primitives; ++i) {
00425     CPT(GeomPrimitive) prim = geom->get_primitive(i);
00426 
00427     int num_vertices = prim->get_num_vertices();
00428     for (int vi = 0; vi < num_vertices; ++vi) {
00429       tracker._referenced_vertices.set_bit(prim->get_vertex(vi));
00430     }
00431 
00432     if (prim->is_indexed()) {
00433       collect_prim_statistics(prim->get_vertices());
00434       if (prim->is_composite()) {
00435         collect_statistics(prim->get_mins());
00436         collect_statistics(prim->get_maxs());
00437       }
00438     }
00439       
00440     if (prim->is_of_type(GeomPoints::get_class_type())) {
00441       _num_points += prim->get_num_primitives();
00442       
00443     } else if (prim->is_of_type(GeomLines::get_class_type())) {
00444       _num_lines += prim->get_num_primitives();
00445       
00446     } else if (prim->is_of_type(GeomLinestrips::get_class_type())) {
00447       _num_lines += prim->get_num_faces();
00448       
00449     } else if (prim->is_of_type(GeomTriangles::get_class_type())) {
00450       _num_tris += prim->get_num_primitives();
00451       _num_individual_tris += prim->get_num_primitives();
00452 
00453     } else if (prim->is_of_type(GeomTristrips::get_class_type())) {
00454       _num_tris += prim->get_num_faces();
00455       _num_tristrips += prim->get_num_primitives();
00456       _num_triangles_in_strips += prim->get_num_faces();
00457 
00458     } else if (prim->is_of_type(GeomTrifans::get_class_type())) {
00459       _num_tris += prim->get_num_faces();
00460       _num_trifans += prim->get_num_primitives();
00461       _num_triangles_in_fans += prim->get_num_faces();
00462 
00463     } else {
00464       pgraph_cat.warning()
00465         << "Unknown GeomPrimitive type in SceneGraphAnalyzer: "
00466         << prim->get_type() << "\n";
00467     }
00468   }
00469 }
00470 
00471 ////////////////////////////////////////////////////////////////////
00472 //     Function: SceneGraphAnalyzer::collect_statistics
00473 //       Access: Private
00474 //  Description: Recursively visits each node, counting up the
00475 //               statistics.
00476 ////////////////////////////////////////////////////////////////////
00477 void SceneGraphAnalyzer::
00478 collect_statistics(Texture *texture) {
00479   nassertv(texture != (Texture *)NULL);
00480 
00481   Textures::iterator ti = _textures.find(texture);
00482   if (ti == _textures.end()) {
00483     // This is the first time this texture has been encountered.
00484     _textures.insert(Textures::value_type(texture, 1));
00485 
00486     // Attempt to guess how many bytes of texture memory this one
00487     // requires.
00488     int bytes =
00489       texture->get_x_size() * texture->get_y_size() * 
00490       texture->get_num_components() * texture->get_component_width();
00491     
00492     if (texture->uses_mipmaps()) {
00493       bytes *= 4/3;
00494     }
00495     
00496     _texture_bytes += bytes;
00497 
00498   } else {
00499     // This texture has been encountered before; don't count it again.
00500     (*ti).second++;
00501   }
00502 }
00503 
00504 ////////////////////////////////////////////////////////////////////
00505 //     Function: SceneGraphAnalyzer::collect_statistics
00506 //       Access: Private
00507 //  Description: Recursively visits each node, counting up the
00508 //               statistics.
00509 ////////////////////////////////////////////////////////////////////
00510 void SceneGraphAnalyzer::
00511 collect_statistics(const GeomVertexArrayData *vadata) {
00512   nassertv(vadata != NULL);
00513   bool inserted = _vadatas.insert(vadata).second;
00514   if (inserted) {
00515     // This is the first time we've encountered this vertex array.
00516     _vertex_data_size += vadata->get_data_size_bytes();
00517     int &dup_count = (*(_unique_vadatas.insert(UniqueVADatas::value_type(vadata, 0)).first)).second;
00518     ++dup_count;
00519   }
00520 }
00521 
00522 ////////////////////////////////////////////////////////////////////
00523 //     Function: SceneGraphAnalyzer::collect_prim_statistics
00524 //       Access: Private
00525 //  Description: Recursively visits each node, counting up the
00526 //               statistics.  This one records the vertex index array
00527 //               associated with a GeomPrimitive, as opposed to the
00528 //               vertex data array, component of a GeomVertexData.
00529 ////////////////////////////////////////////////////////////////////
00530 void SceneGraphAnalyzer::
00531 collect_prim_statistics(const GeomVertexArrayData *vadata) {
00532   nassertv(vadata != NULL);
00533   bool inserted = _prim_vadatas.insert(vadata).second;
00534   if (inserted) {
00535     // This is the first time we've encountered this vertex array.
00536     _prim_data_size += vadata->get_data_size_bytes();
00537     int &dup_count = (*(_unique_prim_vadatas.insert(UniqueVADatas::value_type(vadata, 0)).first)).second;
00538     ++dup_count;
00539   }
00540 }
 All Classes Functions Variables Enumerations