Panda3D
Loading...
Searching...
No Matches
sceneGraphAnalyzer.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file sceneGraphAnalyzer.cxx
10 * @author drose
11 * @date 2000-07-02
12 */
13
14#include "sceneGraphAnalyzer.h"
15#include "config_pgraph.h"
16
17#include "indent.h"
18#include "lodNode.h"
19#include "geomNode.h"
20#include "geomVertexData.h"
21#include "geom.h"
22#include "geomPrimitive.h"
23#include "geomPoints.h"
24#include "geomLines.h"
25#include "geomLinestrips.h"
26#include "geomTriangles.h"
27#include "geomTristrips.h"
28#include "geomTrifans.h"
29#include "geomPatches.h"
30#include "transformState.h"
31#include "textureAttrib.h"
32#include "pta_ushort.h"
33#include "geomVertexReader.h"
34
35/**
36 *
37 */
38SceneGraphAnalyzer::
39SceneGraphAnalyzer() {
40 _lod_mode = LM_all;
41 clear();
42}
43
44/**
45 *
46 */
47SceneGraphAnalyzer::
48~SceneGraphAnalyzer() {
49}
50
51/**
52 * Resets all of the data in the analyzer in preparation for a new run.
53 */
55clear() {
56 _nodes.clear();
57 _vdatas.clear();
58 _vformats.clear();
59 _vadatas.clear();
60 _unique_vdatas.clear();
61 _unique_vadatas.clear();
62 _textures.clear();
63
64 _num_nodes = 0;
65 _num_instances = 0;
66 _num_transforms = 0;
67 _num_nodes_with_attribs = 0;
68 _num_lod_nodes = 0;
69 _num_geom_nodes = 0;
70 _num_geoms = 0;
71 _num_geom_vertex_datas = 0;
72 _num_geom_vertex_formats = 0;
73 _vertex_data_size = 0;
74 _prim_data_size = 0;
75
76 _num_vertices = 0;
77 _num_vertices_64 = 0;
78 _num_normals = 0;
79 _num_colors = 0;
80 _num_texcoords = 0;
81 _num_tris = 0;
82 _num_lines = 0;
83 _num_points = 0;
84 _num_patches = 0;
85
86 _num_individual_tris = 0;
87 _num_tristrips = 0;
88 _num_triangles_in_strips = 0;
89 _num_trifans = 0;
90 _num_triangles_in_fans = 0;
91 _num_vertices_in_patches = 0;
92
93 _texture_bytes = 0;
94
95 _num_long_normals = 0;
96 _num_short_normals = 0;
97 _total_normal_length = 0.0f;
98}
99
100/**
101 * Adds a new node to the set of data for analysis. Normally, this would only
102 * be called once, and passed the top of the scene graph, but it's possible to
103 * repeatedly pass in subgraphs to get an analysis of all the graphs together.
104 */
106add_node(PandaNode *node) {
107 collect_statistics(node, false);
108}
109
110/**
111 * Describes all the data collected.
112 */
114write(std::ostream &out, int indent_level) const {
115 indent(out, indent_level)
116 << _num_nodes << " total nodes (including "
117 << _num_instances << " instances); " << _num_lod_nodes << " LODNodes.\n";
118
119 indent(out, indent_level)
120 << _num_transforms << " transforms";
121
122 if (_num_nodes != 0) {
123 out << "; " << 100 * _num_nodes_with_attribs / _num_nodes
124 << "% of nodes have some render attribute.";
125 }
126 out << "\n";
127
128 indent(out, indent_level)
129 << _num_geoms << " Geoms, with " << _num_geom_vertex_datas
130 << " GeomVertexDatas and " << _num_geom_vertex_formats
131 << " GeomVertexFormats, appear on " << _num_geom_nodes
132 << " GeomNodes.\n";
133
134 indent(out, indent_level);
135 if (_num_vertices_64 != 0) {
136 out << _num_vertices_64 << " 64-bit vertices, ";
137 if (_num_vertices != _num_vertices_64) {
138 out << _num_vertices - _num_vertices_64 << " 32-bit vertices, ";
139 }
140 } else {
141 out << _num_vertices << " vertices, ";
142 }
143
144 out << _num_normals << " normals, "
145 << _num_colors << " colors, "
146 << _num_texcoords << " texture coordinates.\n";
147
148 if (_num_long_normals != 0 || _num_short_normals != 0) {
149 indent(out, indent_level)
150 << _num_long_normals << " normals are too long, "
151 << _num_short_normals << " are too short. Average normal length is "
152 << _total_normal_length / (PN_stdfloat)_num_normals << "\n";
153 }
154
155 indent(out, indent_level)
156 << "GeomVertexData arrays occupy " << (_vertex_data_size + 1023) / 1024
157 << "K memory.\n";
158
159 indent(out, indent_level)
160 << "GeomPrimitive arrays occupy " << (_prim_data_size + 1023) / 1024
161 << "K memory.\n";
162
163 int unreferenced_vertices = 0;
164 VDatas::const_iterator vdi;
165 for (vdi = _vdatas.begin(); vdi != _vdatas.end(); ++vdi) {
166 CPT(GeomVertexData) vdata = (*vdi).first;
167 const VDataTracker &tracker = (*vdi).second;
168 int num_unreferenced = vdata->get_num_rows() - tracker._referenced_vertices.get_num_on_bits();
169 nassertv(num_unreferenced >= 0);
170 unreferenced_vertices += num_unreferenced;
171 }
172 if (unreferenced_vertices != 0) {
173 indent(out, indent_level)
174 << unreferenced_vertices << " vertices are unreferenced by any GeomPrimitives.\n";
175 }
176 if (_unique_vdatas.size() != _vdatas.size()) {
177 indent(out, indent_level)
178 << _vdatas.size() - _unique_vdatas.size()
179 << " GeomVertexDatas are redundantly duplicated\n";
180 }
181 if (_unique_vadatas.size() != _vadatas.size()) {
182 int wasted_bytes = 0;
183
184 UniqueVADatas::const_iterator uvai;
185 for (uvai = _unique_vadatas.begin();
186 uvai != _unique_vadatas.end();
187 ++uvai) {
188 const GeomVertexArrayData *gvad = (*uvai).first;
189 int dup_count = (*uvai).second;
190 if (dup_count > 1) {
191 wasted_bytes += (dup_count - 1) * gvad->get_data_size_bytes();
192 }
193 }
194 indent(out, indent_level)
195 << _vadatas.size() - _unique_vadatas.size()
196 << " GeomVertexArrayDatas are redundant, wasting "
197 << (wasted_bytes + 1023) / 1024 << "K.\n";
198 }
199 if (_unique_prim_vadatas.size() != _prim_vadatas.size()) {
200 int wasted_bytes = 0;
201
202 UniqueVADatas::const_iterator uvai;
203 for (uvai = _unique_prim_vadatas.begin();
204 uvai != _unique_prim_vadatas.end();
205 ++uvai) {
206 const GeomVertexArrayData *gvad = (*uvai).first;
207 int dup_count = (*uvai).second;
208 if (dup_count > 1) {
209 wasted_bytes += (dup_count - 1) * gvad->get_data_size_bytes();
210 }
211 }
212 indent(out, indent_level)
213 << _prim_vadatas.size() - _unique_prim_vadatas.size()
214 << " GeomPrimitive arrays are redundant, wasting "
215 << (wasted_bytes + 1023) / 1024 << "K.\n";
216 }
217
218 indent(out, indent_level)
219 << _num_tris << " triangles:\n";
220 indent(out, indent_level + 2)
221 << _num_triangles_in_strips
222 << " of these are on " << _num_tristrips << " tristrips";
223 if (_num_tristrips != 0) {
224 out << " ("
225 << (double)_num_triangles_in_strips / (double)_num_tristrips
226 << " average tris per strip)";
227 }
228 out << ".\n";
229
230 if (_num_trifans != 0) {
231 indent(out, indent_level + 2)
232 << _num_triangles_in_fans
233 << " of these are on " << _num_trifans << " trifans";
234 if (_num_trifans != 0) {
235 out << " ("
236 << (double)_num_triangles_in_fans / (double)_num_trifans
237 << " average tris per fan)";
238 }
239 out << ".\n";
240 }
241
242 indent(out, indent_level + 2)
243 << _num_individual_tris
244 << " of these are independent triangles.\n";
245
246 if (_num_patches != 0) {
247 indent(out, indent_level)
248 << _num_patches << " patches ("
249 << (double)_num_vertices_in_patches / (double)_num_patches
250 << " average verts per patch).\n";
251 }
252
253 if (_num_lines != 0 || _num_points != 0) {
254 indent(out, indent_level)
255 << _num_lines << " lines, " << _num_points << " points.\n";
256 }
257
258 indent(out, indent_level)
259 << _textures.size() << " textures, estimated minimum "
260 << (_texture_bytes + 1023) / 1024 << "K texture memory required.\n";
261}
262
263/**
264 * Recursively visits each node, counting up the statistics.
265 */
266void SceneGraphAnalyzer::
267collect_statistics(PandaNode *node, bool under_instance) {
268 _num_nodes++;
269
270 if (!under_instance) {
271 Nodes::iterator ni = _nodes.find(node);
272 if (ni == _nodes.end()) {
273 // This is the first time this node has been encountered.
274 _nodes.insert(Nodes::value_type(node, 1));
275 } else {
276 // This node has been encountered before; that makes it an instance.
277 (*ni).second++;
278 _num_instances++;
279 under_instance = true;
280 }
281 }
282
283 if (!node->get_state()->is_empty()) {
284 _num_nodes_with_attribs++;
285 const RenderAttrib *attrib =
286 node->get_attrib(TextureAttrib::get_class_slot());
287 if (attrib != nullptr) {
288 const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
289 for (int i = 0; i < ta->get_num_on_stages(); i++) {
290 collect_statistics(ta->get_on_texture(ta->get_on_stage(i)));
291 }
292 }
293 }
294 if (!node->get_transform()->is_identity()) {
295 _num_transforms++;
296 }
297
298 if (node->is_geom_node()) {
299 collect_statistics(DCAST(GeomNode, node));
300 }
301
302 if (node->is_lod_node()) {
303 LODNode *lod_node = DCAST(LODNode, node);
304 ++_num_lod_nodes;
305
306 switch (_lod_mode) {
307 case LM_lowest:
308 case LM_highest:
309 {
310 int sw = (_lod_mode == LM_lowest) ? lod_node->get_lowest_switch() : lod_node->get_highest_switch();
311 if (sw >= 0 && sw < node->get_num_children()) {
312 PandaNode *child = node->get_child(sw);
313 collect_statistics(child, under_instance);
314 }
315 return;
316 }
317
318 case LM_none:
319 return;
320
321 case LM_all:
322 // fall through to the loop below.
323 break;
324 }
325 }
326
327 int num_children = node->get_num_children();
328 for (int i = 0; i < num_children; i++) {
329 PandaNode *child = node->get_child(i);
330 collect_statistics(child, under_instance);
331 }
332}
333
334/**
335 * Recursively visits each node, counting up the statistics.
336 */
337void SceneGraphAnalyzer::
338collect_statistics(GeomNode *geom_node) {
339 nassertv(geom_node != nullptr);
340
341 ++_num_geom_nodes;
342
343 int num_geoms = geom_node->get_num_geoms();
344 _num_geoms += num_geoms;
345
346 for (int i = 0; i < num_geoms; i++) {
347 const Geom *geom = geom_node->get_geom(i);
348 collect_statistics(geom);
349
350 const RenderState *geom_state = geom_node->get_geom_state(i);
351
352 const RenderAttrib *attrib =
353 geom_state->get_attrib(TextureAttrib::get_class_slot());
354 if (attrib != nullptr) {
355 const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
356 for (int i = 0; i < ta->get_num_on_stages(); i++) {
357 collect_statistics(ta->get_on_texture(ta->get_on_stage(i)));
358 }
359 }
360 }
361}
362
363/**
364 * Recursively visits each node, counting up the statistics.
365 */
366void SceneGraphAnalyzer::
367collect_statistics(const Geom *geom) {
368 CPT(GeomVertexData) vdata = geom->get_vertex_data();
369 std::pair<VDatas::iterator, bool> result = _vdatas.insert(VDatas::value_type(vdata, VDataTracker()));
370 if (result.second) {
371 // This is the first time we've encountered this vertex data.
372 ++_num_geom_vertex_datas;
373
374 CPT(GeomVertexFormat) vformat = vdata->get_format();
375 bool format_inserted = _vformats.insert(vformat).second;
376 if (format_inserted) {
377 // This is the first time we've encountered this vertex format.
378 ++_num_geom_vertex_formats;
379 }
380
381 int &dup_count = (*(_unique_vdatas.insert(UniqueVDatas::value_type(vdata, 0)).first)).second;
382 ++dup_count;
383
384 int num_rows = vdata->get_num_rows();
385 const GeomVertexFormat *format = vdata->get_format();
386 if (format->has_column(InternalName::get_vertex())) {
387 _num_vertices += num_rows;
388 const GeomVertexColumn *vcolumn = format->get_column(InternalName::get_vertex());
389 if (vcolumn->get_numeric_type() == GeomEnums::NT_float64) {
390 _num_vertices_64 += num_rows;
391 }
392 }
393 if (format->has_column(InternalName::get_normal())) {
394 _num_normals += num_rows;
395 GeomVertexReader rnormal(vdata, InternalName::get_normal());
396 while (!rnormal.is_at_end()) {
397 LVector3f normal = rnormal.get_data3f();
398 float length = normal.length();
399 if (IS_NEARLY_EQUAL(length, 1.0f)) {
400 // Correct length normal.
401 } else if (length > 1.0f) {
402 ++_num_long_normals;
403 } else { // length < 1.0f
404 ++_num_short_normals;
405 }
406 _total_normal_length += length;
407 }
408 }
409 if (format->has_column(InternalName::get_color())) {
410 _num_colors += num_rows;
411 }
412 int num_texcoords = format->get_num_texcoords();
413 _num_texcoords += num_rows * num_texcoords;
414
415 int num_arrays = vdata->get_num_arrays();
416 for (int i = 0; i < num_arrays; ++i) {
417 collect_statistics(vdata->get_array(i));
418 }
419 }
420 VDataTracker &tracker = (*(result.first)).second;
421
422 // Now consider the primitives in the Geom.
423 int num_primitives = geom->get_num_primitives();
424 for (int i = 0; i < num_primitives; ++i) {
425 CPT(GeomPrimitive) prim = geom->get_primitive(i);
426
427 int num_vertices = prim->get_num_vertices();
428 int strip_cut_index = prim->get_strip_cut_index();
429 for (int vi = 0; vi < num_vertices; ++vi) {
430 int index = prim->get_vertex(vi);
431 if (index != strip_cut_index) {
432 tracker._referenced_vertices.set_bit(index);
433 }
434 }
435
436 if (prim->is_indexed()) {
437 collect_prim_statistics(prim->get_vertices());
438 if (prim->is_composite()) {
439 collect_statistics(prim->get_mins());
440 collect_statistics(prim->get_maxs());
441 }
442 }
443
444 if (prim->is_of_type(GeomPoints::get_class_type())) {
445 _num_points += prim->get_num_primitives();
446
447 } else if (prim->is_of_type(GeomLines::get_class_type())) {
448 _num_lines += prim->get_num_primitives();
449
450 } else if (prim->is_of_type(GeomLinestrips::get_class_type())) {
451 _num_lines += prim->get_num_faces();
452
453 } else if (prim->is_of_type(GeomTriangles::get_class_type())) {
454 _num_tris += prim->get_num_primitives();
455 _num_individual_tris += prim->get_num_primitives();
456
457 } else if (prim->is_of_type(GeomTristrips::get_class_type())) {
458 _num_tris += prim->get_num_faces();
459 _num_tristrips += prim->get_num_primitives();
460 _num_triangles_in_strips += prim->get_num_faces();
461
462 } else if (prim->is_of_type(GeomTrifans::get_class_type())) {
463 _num_tris += prim->get_num_faces();
464 _num_trifans += prim->get_num_primitives();
465 _num_triangles_in_fans += prim->get_num_faces();
466
467 } else if (prim->is_of_type(GeomPatches::get_class_type())) {
468 _num_patches += prim->get_num_primitives();
469 _num_vertices_in_patches += prim->get_num_vertices();
470
471 } else {
472 pgraph_cat.warning()
473 << "Unknown GeomPrimitive type in SceneGraphAnalyzer: "
474 << prim->get_type() << "\n";
475 }
476 }
477}
478
479/**
480 * Recursively visits each node, counting up the statistics.
481 */
482void SceneGraphAnalyzer::
483collect_statistics(Texture *texture) {
484 nassertv(texture != nullptr);
485
486 Textures::iterator ti = _textures.find(texture);
487 if (ti == _textures.end()) {
488 // This is the first time this texture has been encountered.
489 _textures.insert(Textures::value_type(texture, 1));
490
491 // Attempt to guess how many bytes of texture memory this one requires.
492 size_t bytes =
493 texture->get_x_size() * texture->get_y_size() *
494 texture->get_num_components() * texture->get_component_width();
495
496 if (texture->uses_mipmaps()) {
497 bytes *= 4/3;
498 }
499
500 _texture_bytes += bytes;
501
502 } else {
503 // This texture has been encountered before; don't count it again.
504 (*ti).second++;
505 }
506}
507
508/**
509 * Recursively visits each node, counting up the statistics.
510 */
511void SceneGraphAnalyzer::
512collect_statistics(const GeomVertexArrayData *vadata) {
513 nassertv(vadata != nullptr);
514 bool inserted = _vadatas.insert(vadata).second;
515 if (inserted) {
516 // This is the first time we've encountered this vertex array.
517 _vertex_data_size += vadata->get_data_size_bytes();
518 int &dup_count = (*(_unique_vadatas.insert(UniqueVADatas::value_type(vadata, 0)).first)).second;
519 ++dup_count;
520 }
521}
522
523/**
524 * Recursively visits each node, counting up the statistics. This one records
525 * the vertex index array associated with a GeomPrimitive, as opposed to the
526 * vertex data array, component of a GeomVertexData.
527 */
528void SceneGraphAnalyzer::
529collect_prim_statistics(const GeomVertexArrayData *vadata) {
530 nassertv(vadata != nullptr);
531 bool inserted = _prim_vadatas.insert(vadata).second;
532 if (inserted) {
533 // This is the first time we've encountered this vertex array.
534 _prim_data_size += vadata->get_data_size_bytes();
535 int &dup_count = (*(_unique_prim_vadatas.insert(UniqueVADatas::value_type(vadata, 0)).first)).second;
536 ++dup_count;
537 }
538}
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the array.
Definition bitArray.cxx:296
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
get_num_geoms
Returns the number of geoms in the node.
Definition geomNode.h:71
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition geomNode.h:75
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
This is the data for one array of a GeomVertexData structure.
get_data_size_bytes
Returns the number of bytes stored in the array.
This defines how a single column is interleaved within a vertex array stored within a Geom.
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
get_num_texcoords
Returns the number of columns within the format that represent texture coordinates.
has_column
Returns true if the format has the named column, false otherwise.
get_column
Returns the ith column of the specification, across all arrays.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
A Level-of-Detail node.
Definition lodNode.h:28
get_lowest_switch
Returns the index number of the child with the lowest level of detail; that is, the one that is desig...
Definition lodNode.h:80
get_highest_switch
Returns the index number of the child with the highest level of detail; that is, the one that is desi...
Definition lodNode.h:81
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
get_child
Returns the nth child node of this node.
Definition pandaNode.h:124
virtual bool is_lod_node() const
A simple downcast check.
get_num_children
Returns the number of child nodes this node has.
Definition pandaNode.h:124
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
void add_node(PandaNode *node)
Adds a new node to the set of data for analysis.
void write(std::ostream &out, int indent_level=0) const
Describes all the data collected.
void clear()
Resets all of the data in the analyzer in preparation for a new run.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
get_component_width
Returns the number of bytes stored for each color component of a texel.
Definition texture.h:365
get_y_size
Returns the height of the texture image in texels.
Definition texture.h:347
get_num_components
Returns the number of color components for each texel of the texture image.
Definition texture.h:364
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping,...
Definition texture.I:1128
get_x_size
Returns the width of the texture image in texels.
Definition texture.h:343
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.