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