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