Panda3D
|
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 }