1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*! \file SbProfilingData.h */
34 #include <Inventor/annex/Profiler/SbProfilingData.h>
35 #include "coindefs.h"
36
37 #include <algorithm> // std::reverse
38 #include <cstring>
39 #include <map>
40 #include <vector>
41
42 #include <Inventor/SbName.h>
43 #include <Inventor/SoFullPath.h>
44 #include <Inventor/nodes/SoNode.h>
45
46 // *************************************************************************
47
48 /*!
49 \typedef void * SbProfilingNodeKey
50
51 Type definition for the key node. This is a void * to ensure
52 that it is not inadvertantly dereferenced.
53 */
54
55 /*!
56 \typedef int16_t SbProfilingNodeTypeKey
57
58 Type definition for the key node type.
59 */
60
61 /*!
62 \typedef const char * SbProfilingNodeNameKey
63
64 Type definition for the key node name.
65 */
66
67 // *************************************************************************
68
69 // SbNodeProfilingData - internal structure containing profiling data
70 // for one node.
71 struct SbNodeProfilingData {
72 SbProfilingNodeKey node;
73 SbProfilingNodeNameKey nodename;
74 SbProfilingNodeTypeKey nodetype;
75 int parentidx;
76 int childidx;
77
78 SbTime traversaltime;
79 size_t memorysize;
80 size_t texturesize;
81 int traversalcount;
82
83 struct {
84 int glcached : 1;
85 int culled : 1;
86 } flags;
87
88 inline SbNodeProfilingData(void);
89 inline int operator == (const SbNodeProfilingData & rhs) const;
90 inline int operator != (const SbNodeProfilingData & rhs) const;
91
92 }; // SbNodeProfilingData
93
94 struct SbTypeProfilingData {
95 SbTime totaltime;
96 SbTime maximumtime;
97 int count;
98
99 inline SbTypeProfilingData(void);
100
101 }; // SbTypeProfilingData
102
103 struct SbNameProfilingData {
104 SbTime totaltime;
105 SbTime maximumtime;
106 int count;
107
108 inline SbNameProfilingData(void);
109
110 }; // SbNameProfilingData
111
112 // inlined methods
113
SbNodeProfilingData(void)114 SbNodeProfilingData::SbNodeProfilingData(void)
115 : node(NULL), /* nodename(NULL), */ nodetype(0),
116 parentidx(-1), childidx(0),
117 traversaltime(0.0), memorysize(0), texturesize(0), traversalcount(0)
118 {
119 this->flags.glcached = 0;
120 this->flags.culled = 0;
121 }
122
123 int
operator ==(const SbNodeProfilingData & rhs) const124 SbNodeProfilingData::operator == (const SbNodeProfilingData & rhs) const {
125 return (memcmp(this, &rhs, sizeof(SbNodeProfilingData)) == 0);
126 }
127
128 int
operator !=(const SbNodeProfilingData & rhs) const129 SbNodeProfilingData::operator != (const SbNodeProfilingData & rhs) const {
130 return (memcmp(this, &rhs, sizeof(SbNodeProfilingData)) != 0);
131 }
132
SbTypeProfilingData(void)133 SbTypeProfilingData::SbTypeProfilingData(void)
134 : totaltime(0.0), maximumtime(0.0), count(0)
135 {
136 }
137
SbNameProfilingData(void)138 SbNameProfilingData::SbNameProfilingData(void)
139 : totaltime(0.0), maximumtime(0.0), count(0)
140 {
141 }
142
143 // *************************************************************************
144
145 class SbProfilingDataP {
146 public:
147
148 std::vector<SbNodeProfilingData> nodeData;
149 int lastPathIndex;
150
151 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData> nodeTypeData;
152 std::map<SbProfilingNodeNameKey, SbNameProfilingData> nodeNameData;
153
154 }; // SbProfilingDataP
155
156 #define PRIVATE(obj) ((obj)->pimpl)
157
158 /*!
159 \struct SbNodeProfilingData
160 \brief Data structure for gathering scene graph traversal profiling information for one node.
161
162 \ingroup profiler
163 */
164
165 /*!
166 \class SbProfilingData
167 \brief Data structure for gathering scene graph traversal profiling information.
168
169 \ingroup profiler
170 */
171
172 /*!
173 Constructor.
174 */
SbProfilingData(void)175 SbProfilingData::SbProfilingData(void)
176 {
177 this->constructorInit();
178 }
179
180 /*!
181 Copy constructor.
182 */
SbProfilingData(const SbProfilingData & rhs)183 SbProfilingData::SbProfilingData(const SbProfilingData & rhs)
184 {
185 this->constructorInit();
186 this->operator = (rhs);
187 }
188
189 /*!
190 Desctructor.
191 */
~SbProfilingData(void)192 SbProfilingData::~SbProfilingData(void)
193 {
194 this->reset();
195 }
196
197 /*!
198 Common initialization code for the constructors.
199 */
200 void
constructorInit(void)201 SbProfilingData::constructorInit(void)
202 {
203 // NB: if resource allocation is added, rewrite reset() to not call here
204 this->actionType = SoType::badType();
205 this->actionStartTime = SbTime::zero();
206 this->actionStopTime = SbTime::zero();
207 PRIVATE(this)->lastPathIndex = -1;
208 }
209
210 /*!
211 Remove all stored data.
212 */
213 void
reset(void)214 SbProfilingData::reset(void)
215 {
216 this->constructorInit();
217 PRIVATE(this)->nodeData.clear();
218 PRIVATE(this)->nodeTypeData.clear();
219 PRIVATE(this)->nodeNameData.clear();
220 assert(PRIVATE(this)->nodeData.size() == 0);
221 assert(PRIVATE(this)->nodeTypeData.size() == 0);
222 assert(PRIVATE(this)->nodeNameData.size() == 0);
223 }
224
225 /*!
226 Assignment operator.
227 */
228 SbProfilingData &
operator =(const SbProfilingData & rhs)229 SbProfilingData::operator = (const SbProfilingData & rhs)
230 {
231 this->reset();
232 this->actionType = rhs.actionType;
233 this->actionStartTime = rhs.actionStartTime;
234 this->actionStopTime = rhs.actionStopTime;
235 PRIVATE(this)->lastPathIndex = -1;
236 PRIVATE(this)->nodeData = PRIVATE(&rhs)->nodeData;
237 PRIVATE(this)->nodeTypeData = PRIVATE(&rhs)->nodeTypeData;
238 PRIVATE(this)->nodeNameData = PRIVATE(&rhs)->nodeNameData;
239 assert(PRIVATE(this)->nodeData.size() == PRIVATE(&rhs)->nodeData.size());
240 return *this;
241 }
242
243 // search dst for the given path in src, and if nonexistent create it
244 static void
findPath(const std::vector<SbNodeProfilingData> & src,std::vector<SbNodeProfilingData> & dst,int srcentryidx,int & matchidx,int & parentidx)245 findPath(const std::vector<SbNodeProfilingData> & src, std::vector<SbNodeProfilingData> & dst, int srcentryidx, int & matchidx, int & parentidx)
246 {
247 matchidx = -1;
248 parentidx = -1;
249
250 // By going from 0 and outwards, the source-vector parent node
251 // will always already exist in the target-vector, which should
252 // help a lot.
253 const int numdestentries = dst.size();
254
255 // FIXME: optimize this part
256
257 bool match = false;
258 for (int i = 0; i < numdestentries && !match; ++i) {
259 if (dst[i].node == src[srcentryidx].node &&
260 dst[i].childidx == src[srcentryidx].childidx) {
261 // we have a potential match
262 int dstidx = dst[i].parentidx, srcidx = src[srcentryidx].parentidx;
263 while ((srcidx != -1) &&
264 (dstidx != -1) &&
265 (src[srcidx].node == dst[dstidx].node) &&
266 (src[srcidx].childidx == dst[dstidx].childidx)) {
267 srcidx = src[srcidx].parentidx;
268 dstidx = dst[dstidx].parentidx;
269 }
270 if (srcidx == -1 && dstidx == -1) { // match!
271 match = true;
272 matchidx = i;
273 }
274 }
275 }
276
277 if (match) {
278 parentidx = dst[matchidx].parentidx;
279 } else {
280 matchidx = -1;
281
282 // search for parent instead
283 srcentryidx = src[srcentryidx].parentidx;
284 if (srcentryidx == -1) { // root doesn't have parent
285 parentidx = -1;
286 return;
287 }
288
289 for (int i = 0; i < numdestentries && !match; ++i) {
290 if (dst[i].node == src[srcentryidx].node &&
291 dst[i].childidx == src[srcentryidx].childidx) {
292 // we have a potential match
293 int dstidx = dst[i].parentidx, srcidx = src[srcentryidx].parentidx;
294 while ((srcidx != -1) &&
295 (dstidx != -1) &&
296 (src[srcidx].node == dst[dstidx].node) &&
297 (src[srcidx].childidx == dst[dstidx].childidx)) {
298 srcidx = src[srcidx].parentidx;
299 dstidx = dst[dstidx].parentidx;
300 }
301 if (srcidx == -1 && dstidx == -1) { // match!
302 match = true;
303 parentidx = i;
304 }
305 }
306 }
307 }
308 }
309
310 /*!
311 Add profiling data from another data set.
312 */
313 SbProfilingData &
operator +=(const SbProfilingData & rhs)314 SbProfilingData::operator += (const SbProfilingData & rhs)
315 {
316 assert(this != &rhs);
317
318 if (PRIVATE(this)->nodeData.size() == 0) {
319 return this->operator = (rhs);
320 }
321
322 if (rhs.actionType == SoType::badType()) {
323 // nada - assume same as this
324 } else if (this->actionType == SoType::badType()) {
325 this->actionType = rhs.actionType;
326 } else if (this->actionType != rhs.actionType) {
327 this->actionType = SoType::badType();
328 }
329
330 if (this->actionStartTime == SbTime::zero()) {
331 this->actionStartTime = rhs.actionStartTime;
332 this->actionStopTime = rhs.actionStopTime;
333 } else {
334 this->actionStopTime += rhs.getActionDuration();
335 }
336
337 const std::vector<SbNodeProfilingData> & src = PRIVATE(&rhs)->nodeData;
338 std::vector<SbNodeProfilingData> & dst = PRIVATE(this)->nodeData;
339
340 { // nodeData
341 const int numsrcentries = src.size();
342 for (int c = 0; c < numsrcentries; ++c) {
343 int matchidx = -1, parentidx = -1;
344 findPath(src, dst, c, matchidx, parentidx);
345 if (matchidx == -1) {
346 SbNodeProfilingData data;
347 data.node = src[c].node;
348 data.childidx = src[c].childidx;
349 data.parentidx = parentidx;
350 data.nodetype = src[c].nodetype;
351 data.nodename = src[c].nodename;
352 dst.push_back(data);
353 matchidx = dst.size() - 1;
354 }
355 // acumulate data (something about this really doesn't make sense)
356 dst[matchidx].traversaltime += src[c].traversaltime;
357 dst[matchidx].memorysize += src[c].memorysize;
358 dst[matchidx].texturesize += src[c].texturesize;
359 dst[matchidx].traversalcount += src[c].traversalcount;
360 }
361 }
362
363 { // nodeTypeData
364 typedef std::map<SbProfilingNodeTypeKey, SbTypeProfilingData> maptype;
365 maptype::const_iterator srctypeit = PRIVATE(&rhs)->nodeTypeData.begin();
366 while (srctypeit != PRIVATE(&rhs)->nodeTypeData.end()) {
367 maptype::iterator dsttypeit = PRIVATE(this)->nodeTypeData.find(srctypeit->first);
368 if (dsttypeit != PRIVATE(this)->nodeTypeData.end()) {
369 dsttypeit->second.totaltime += srctypeit->second.totaltime;
370 dsttypeit->second.count += srctypeit->second.count;
371 if (srctypeit->second.maximumtime > dsttypeit->second.maximumtime) {
372 dsttypeit->second.maximumtime = srctypeit->second.maximumtime;
373 }
374 } else {
375 // new type entry - copy data in
376 PRIVATE(this)->nodeTypeData.insert(*srctypeit);
377 }
378 ++srctypeit;
379 }
380 }
381
382 { // nodeNameData
383 typedef std::map<SbProfilingNodeNameKey, SbNameProfilingData> maptype;
384 maptype::const_iterator srctypeit = PRIVATE(&rhs)->nodeNameData.begin();
385 while (srctypeit != PRIVATE(&rhs)->nodeNameData.end()) {
386 maptype::iterator dsttypeit = PRIVATE(this)->nodeNameData.find(srctypeit->first);
387 if (dsttypeit != PRIVATE(this)->nodeNameData.end()) {
388 dsttypeit->second.totaltime += srctypeit->second.totaltime;
389 dsttypeit->second.count += srctypeit->second.count;
390 if (srctypeit->second.maximumtime > dsttypeit->second.maximumtime) {
391 dsttypeit->second.maximumtime = srctypeit->second.maximumtime;
392 }
393 } else {
394 // new type entry - copy data in
395 PRIVATE(this)->nodeNameData.insert(*srctypeit);
396 }
397 ++srctypeit;
398 }
399 }
400
401 assert(PRIVATE(this)->nodeData.size() >= PRIVATE(&rhs)->nodeData.size());
402 assert(PRIVATE(this)->nodeTypeData.size() >= PRIVATE(&rhs)->nodeTypeData.size());
403 assert(PRIVATE(this)->nodeNameData.size() >= PRIVATE(&rhs)->nodeNameData.size());
404
405 return *this;
406 }
407
408 /*!
409 Register which type of action we are recording statistics for.
410 */
411
412 void
setActionType(SoType actiontype)413 SbProfilingData::setActionType(SoType actiontype)
414 {
415 this->actionType = actiontype;
416 }
417
418 /*!
419 Return the action type set for this SbProfilingData.
420 */
421
422 SoType
getActionType(void) const423 SbProfilingData::getActionType(void) const
424 {
425 return this->actionType;
426 }
427
428 /*!
429 Set traversal start time.
430 */
431
432 void
setActionStartTime(SbTime starttime)433 SbProfilingData::setActionStartTime(SbTime starttime)
434 {
435 this->actionStartTime = starttime;
436 }
437
438 /*!
439 Return the action start time.
440 */
441
442 SbTime
getActionStartTime(void) const443 SbProfilingData::getActionStartTime(void) const
444 {
445 return this->actionStartTime;
446 }
447
448 /*!
449 Set traversal stop time.
450 */
451
452 void
setActionStopTime(SbTime stoptime)453 SbProfilingData::setActionStopTime(SbTime stoptime)
454 {
455 this->actionStopTime = stoptime;
456 }
457
458 /*!
459 Return the action stop time.
460 */
461
462 SbTime
getActionStopTime(void) const463 SbProfilingData::getActionStopTime(void) const
464 {
465 return this->actionStopTime;
466 }
467
468 /*!
469 Return the time the action has spent on the traversal that was profiled.
470 */
471
472 SbTime
getActionDuration(void) const473 SbProfilingData::getActionDuration(void) const
474 {
475 if ((this->actionStopTime == SbTime::zero()) &&
476 (this->actionStartTime != SbTime::zero())) {
477 // action still running... (?)
478 return SbTime::getTimeOfDay() - this->actionStartTime;
479 }
480 return (this->actionStopTime - this->actionStartTime);
481 }
482
483 // *************************************************************************
484
485 /*
486 * Check if the path matches the given index in the nodedata vector.
487 */
488
489 SbBool
isPathMatch(const SoFullPath * fullpath,int pathlen,int idx)490 SbProfilingData::isPathMatch(const SoFullPath * fullpath, int pathlen, int idx)
491 {
492 assert(pathlen > 0 && pathlen <= fullpath->getLength());
493 while (pathlen > 0 && idx != -1) {
494 SbProfilingNodeKey node =
495 static_cast<SbProfilingNodeKey>(fullpath->getNode(pathlen-1));
496 int childidx = fullpath->getIndex(pathlen-1);
497 if (PRIVATE(this)->nodeData[idx].node != node) return FALSE;
498 if (PRIVATE(this)->nodeData[idx].childidx != childidx) return FALSE;
499 idx = PRIVATE(this)->nodeData[idx].parentidx;
500 --pathlen;
501 }
502 if (pathlen == 0 && idx == -1) return TRUE;
503 return FALSE;
504 }
505
506 // *************************************************************************
507
508 /*!
509 Return the index of the tail node in the path.
510 If node is not registered, add it and return that index.
511 */
512
513 int
getIndex(const SoPath * path,SbBool create)514 SbProfilingData::getIndex(const SoPath * path, SbBool create)
515 {
516 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
517 if ((PRIVATE(this)->lastPathIndex != -1) &&
518 isPathMatch(fullpath, fullpath->getLength(),
519 PRIVATE(this)->lastPathIndex)) {
520 return PRIVATE(this)->lastPathIndex;
521 }
522 int idx = -1;
523 if (create) {
524 idx = this->getIndexCreate(fullpath, fullpath->getLength());
525 } else {
526 idx = this->getIndexNoCreate(fullpath, fullpath->getLength());
527 }
528 if (idx != -1) { PRIVATE(this)->lastPathIndex = idx; }
529 return idx;
530 }
531
532 /*!
533 Return the index of the parent of the node entry at index \a idx.
534 If entry is a root entry, -1 is returned.
535 */
536
537 int
getParentIndex(int idx) const538 SbProfilingData::getParentIndex(int idx) const
539 {
540 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
541 return PRIVATE(this)->nodeData[idx].parentidx;
542 }
543
544 /*
545 * Return the index of the tail node in the path ("tail" node at pathlen
546 * position). If node is not registered, add it and return that index.
547 */
548
549 int
getIndexCreate(const SoFullPath * fullpath,int COIN_UNUSED_ARG (pathlen))550 SbProfilingData::getIndexCreate(const SoFullPath * fullpath, int COIN_UNUSED_ARG(pathlen))
551 {
552
553 std::vector<int> lastentrypathindexes;
554 int idx = PRIVATE(this)->nodeData.size() - 1;
555 while (idx != -1) {
556 lastentrypathindexes.push_back(idx);
557 idx = PRIVATE(this)->nodeData[idx].parentidx;
558 }
559 std::reverse(lastentrypathindexes.begin(), lastentrypathindexes.end());
560
561 int samelength = 0;
562 if (lastentrypathindexes.size() > 0) {
563 const int pathlength =
564 SbMin(fullpath->getLength(), (int) lastentrypathindexes.size());
565 while (samelength < pathlength) {
566 if ((PRIVATE(this)->nodeData[lastentrypathindexes[samelength]].node !=
567 static_cast<SbProfilingNodeKey>(fullpath->getNode(samelength))) ||
568 (PRIVATE(this)->nodeData[lastentrypathindexes[samelength]].childidx !=
569 fullpath->getIndex(samelength))) {
570 break;
571 }
572 ++samelength;
573 }
574 }
575
576 if (samelength == 0) {
577 // FIXME: need to check for the possibility of multiple roots
578 // this is rooted in a new root - add it
579
580 SbNodeProfilingData data;
581 SoNode * rootnode = fullpath->getNode(0);
582 assert(rootnode != NULL);
583 data.node = static_cast<SbProfilingNodeKey>(rootnode);
584 data.nodetype = static_cast<SbProfilingNodeTypeKey>(rootnode->getTypeId().getKey());
585 data.nodename = static_cast<SbProfilingNodeNameKey>(rootnode->getName().getString());
586 PRIVATE(this)->nodeData.push_back(data);
587
588 ++samelength;
589 lastentrypathindexes.clear();
590 lastentrypathindexes.push_back(PRIVATE(this)->nodeData.size() - 1);
591 }
592
593 int pos = samelength;
594 idx = lastentrypathindexes[pos-1];
595 ++pos;
596 while (pos <= fullpath->getLength()) {
597 idx = this->getIndexForwardCreate(fullpath, pos, idx);
598 ++pos;
599 }
600
601 return idx;
602 }
603
604 /*
605 * This function looks for an existing path, and will not create the
606 * entries if they do not exist.
607 */
608
609 int
getIndexNoCreate(const SoPath * path,int COIN_UNUSED_ARG (pathlen)) const610 SbProfilingData::getIndexNoCreate(const SoPath * path, int COIN_UNUSED_ARG(pathlen)) const
611 {
612 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
613
614 std::vector<int> lastentrypathindexes;
615 int idx = PRIVATE(this)->nodeData.size() - 1;
616 while (idx != -1) {
617
618 lastentrypathindexes.push_back(idx);
619 idx = PRIVATE(this)->nodeData[idx].parentidx;
620
621 }
622
623 std::reverse(lastentrypathindexes.begin(), lastentrypathindexes.end());
624
625 int samelength = 0;
626 if (lastentrypathindexes.size() > 0) {
627 const int pathlength =
628 SbMin(fullpath->getLength(), (int) lastentrypathindexes.size());
629 while (samelength < pathlength) {
630 if ((PRIVATE(this)->nodeData[lastentrypathindexes[samelength]].node !=
631 static_cast<SbProfilingNodeKey>(fullpath->getNode(samelength))) ||
632 (PRIVATE(this)->nodeData[lastentrypathindexes[samelength]].childidx !=
633 fullpath->getIndex(samelength))) {
634 break;
635 }
636 ++samelength;
637 }
638 }
639
640 if (samelength == 0) {
641 // FIXME: get
642 return -1;
643 }
644
645 int pos = samelength;
646 idx = lastentrypathindexes[pos-1];
647 ++pos;
648 while (pos < fullpath->getLength() && idx != -1) {
649 idx = this->getIndexForwardNoCreate(fullpath, pos, idx);
650 ++pos;
651 }
652
653 return idx;
654 }
655
656 // *************************************************************************
657
658 /*
659 * This function is used when you have a partial path leading up to
660 * the parent of the tail ("tail" at pathlen). Then you can take some
661 * shortcuts and make some assumptions that saves some time. This
662 * function is intended used only as a subfunction of the getIndex()
663 * function for reverse-finding/building the missing data-structure
664 * from the given path.
665 */
666
667 int
getIndexForwardCreate(const SoFullPath * fullpath,int pathlen,int parentidx)668 SbProfilingData::getIndexForwardCreate(const SoFullPath * fullpath, int pathlen, int parentidx)
669 {
670 assert(parentidx != -1); // illegal usage
671 assert(parentidx < static_cast<int>(PRIVATE(this)->nodeData.size()));
672 assert(pathlen > 1);
673
674 SbProfilingNodeKey parent =
675 static_cast<SbProfilingNodeKey>(fullpath->getNode(pathlen - 2));
676 int pidx = fullpath->getIndex(pathlen - 2);
677 SoNode * tailnode = fullpath->getNode(pathlen - 1);
678 SbProfilingNodeKey tail = static_cast<SbProfilingNodeKey>(tailnode);
679 int tidx = fullpath->getIndex(pathlen - 1);
680
681 assert(parent == PRIVATE(this)->nodeData[parentidx].node);
682 assert(pidx == PRIVATE(this)->nodeData[parentidx].childidx);
683
684 const int nodedatacount = PRIVATE(this)->nodeData.size();
685 for (int idx = parentidx + 1; idx < nodedatacount; ++idx) {
686 if ((PRIVATE(this)->nodeData[idx].node == tail) &&
687 (PRIVATE(this)->nodeData[idx].childidx == tidx)) { // found it!
688 return idx;
689 }
690 }
691
692 // entry not found - add entry and return new index
693 SbNodeProfilingData data;
694 data.node = tail;
695 data.nodetype = static_cast<SbProfilingNodeTypeKey>(tailnode->getTypeId().getKey());
696 data.nodename = static_cast<SbProfilingNodeNameKey>(tailnode->getName().getString());
697 data.parentidx = parentidx;
698 data.childidx = tidx;
699 PRIVATE(this)->nodeData.push_back(data);
700
701 return PRIVATE(this)->nodeData.size() - 1;
702 }
703
704 /*
705 *
706 */
707
708 int
getIndexForwardNoCreate(const SoFullPath * fullpath,int pathlen,int parentidx) const709 SbProfilingData::getIndexForwardNoCreate(const SoFullPath * fullpath, int pathlen, int parentidx) const
710 {
711 assert(parentidx != -1); // illegal usage
712 assert(pathlen > 1);
713
714 SbProfilingNodeKey parent =
715 static_cast<SbProfilingNodeKey>(fullpath->getNode(pathlen - 2));
716 int pidx = fullpath->getIndex(pathlen - 2);
717 SbProfilingNodeKey tail =
718 static_cast<SbProfilingNodeKey>(fullpath->getNode(pathlen - 1));
719 int tidx = fullpath->getIndex(pathlen - 1);
720
721 assert(parent == PRIVATE(this)->nodeData[parentidx].node);
722 assert(pidx == PRIVATE(this)->nodeData[parentidx].childidx);
723
724 const int nodedatacount = PRIVATE(this)->nodeData.size();
725 for (int idx = parentidx + 1; idx < nodedatacount; ++idx) {
726 if ((PRIVATE(this)->nodeData[idx].node == tail) &&
727 (PRIVATE(this)->nodeData[idx].childidx == tidx)) { // found it!
728 return idx;
729 }
730 }
731 return -1;
732 }
733
734 // *************************************************************************
735
736 /*!
737 This function calls the index-version of setNodeTiming after having
738 fetched the index.
739 */
740 void
setNodeTiming(const SoPath * path,SbTime timing)741 SbProfilingData::setNodeTiming(const SoPath * path, SbTime timing)
742 {
743 assert(path);
744 assert(path->getLength() > 0);
745 assert(timing.getValue() >= 0.0);
746
747 const int idx = this->getIndex(path);
748 this->setNodeTiming(idx, timing);
749 }
750
751 /*!
752 This method sets the timing for a node, as if it was new data to
753 be registered. This means that counters of various types are
754 implicitly incremented and similar things. To avoid those
755 sideeffects, use offsetNodeTiming, which leaves all the counters
756 alone.
757
758 \sa offsetNodeTiming
759 */
760 void
setNodeTiming(int idx,SbTime timing)761 SbProfilingData::setNodeTiming(int idx, SbTime timing)
762 {
763 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
764 assert(timing.getValue() >= 0.0);
765
766 // 1) set for path (node)
767 PRIVATE(this)->nodeData[idx].traversaltime = timing;
768 PRIVATE(this)->nodeData[idx].traversalcount = 1;
769
770 // 2) set for type
771 SbProfilingNodeTypeKey typekey = PRIVATE(this)->nodeData[idx].nodetype;
772 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData>::iterator typeit =
773 PRIVATE(this)->nodeTypeData.find(typekey);
774 if (typeit != PRIVATE(this)->nodeTypeData.end()) {
775 typeit->second.totaltime += timing;
776 typeit->second.count += 1;
777 if (typeit->second.maximumtime < timing) {
778 typeit->second.maximumtime = timing;
779 }
780 } else {
781 SbTypeProfilingData data;
782 data.totaltime = timing;
783 data.maximumtime = timing;
784 data.count = 1;
785 PRIVATE(this)->nodeTypeData.insert(std::pair<SbProfilingNodeTypeKey, SbTypeProfilingData>(typekey, data));
786 }
787
788 // 3) set for name
789
790 // should we include timings at all named nodes up through the path
791 // all the way to the root?
792 const bool inclusive = false;
793
794 int parentidx = idx;
795 while (parentidx != -1) {
796 SbProfilingNodeNameKey namekey =
797 PRIVATE(this)->nodeData[parentidx].nodename;
798 if (namekey != SbName::empty().getString()) {
799 std::map<SbProfilingNodeNameKey, SbNameProfilingData>::iterator nameit =
800 PRIVATE(this)->nodeNameData.find(namekey);
801 if (nameit != PRIVATE(this)->nodeNameData.end()) {
802 nameit->second.totaltime += timing;
803 if (idx == parentidx) { // entry at named node level
804 // DISABLED: we won't know the "unit" time for this aggregate
805 // time-sum so we can't give maximum unit time. we'll need to
806 // store total-time from preTraversal() to figure it
807 // out i think. 20080304 larsa
808 //if (nameit->second.maximumtime < timing) {
809 // nameit->second.maximumtime = timing;
810 //}
811 nameit->second.count += 1;
812 }
813 } else {
814 SbNameProfilingData data;
815 data.totaltime = timing;
816 //data.maximumtime = timing;
817 if (idx == parentidx) {
818 data.count += 1;
819 } else {
820
821 }
822 PRIVATE(this)->nodeNameData.insert(std::pair<SbProfilingNodeNameKey, SbNameProfilingData>(namekey, data));
823 }
824 if (!inclusive) break;
825 }
826 parentidx = PRIVATE(this)->nodeData[parentidx].parentidx;
827 }
828 }
829
830 /*!
831 This function will adjust node timings without touching traversal counters.
832 */
833 void
preOffsetNodeTiming(int idx,SbTime timing)834 SbProfilingData::preOffsetNodeTiming(int idx, SbTime timing)
835 {
836 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
837 // 1) adjust for path (node)
838 PRIVATE(this)->nodeData[idx].traversaltime += timing;
839
840 #if 0
841 // 2) adjust for type
842 SbProfilingNodeKey tailnode = PRIVATE(this)->nodeData[idx].node;
843 SbProfilingNodeTypeKey typekey = PRIVATE(this)->nodeData[idx].nodetype;
844 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData>::iterator typeit =
845 PRIVATE(this)->nodeTypeData.find(typekey);
846 if (typeit != PRIVATE(this)->nodeTypeData.end()) {
847 typeit->second.totaltime += timing;
848 } else {
849 SbTypeProfilingData data;
850 data.totaltime = timing;
851 PRIVATE(this)->nodeTypeData.insert(std::pair<SbProfilingNodeTypeKey, SbTypeProfilingData>(typekey, data));
852 }
853
854 // 3) adjust for name
855
856 // should we include timings at all named nodes up through the path
857 // all the way to the root?
858 const bool inclusive = false;
859
860 int parentidx = idx;
861 while (parentidx != -1) {
862 SbProfilingNodeNameKey namekey = PRIVATE(this)->nodeData[parentidx].nodename;
863 if (namekey != SbName::empty().getString()) {
864 std::map<SbProfilingNodeNameKey, SbNameProfilingData>::iterator nameit =
865 PRIVATE(this)->nodeNameData.find(namekey);
866 if (nameit != PRIVATE(this)->nodeNameData.end()) {
867 nameit->second.totaltime += timing;
868 if (idx == parentidx) { // entry at named node level
869 // DISABLED: we won't know the "unit" time for this aggregate
870 // time-sum so we can't give maximum unit time. we'll need to
871 // store total-time from preTraversal() to figure it
872 // out i think. 20080304 larsa
873 //if (nameit->second.maximumtime < timing) {
874 // nameit->second.maximumtime = timing;
875 //}
876 }
877 } else {
878 SbNameProfilingData data;
879 data.totaltime = timing;
880 PRIVATE(this)->nodeNameData.insert(std::pair<SbProfilingNodeNameKey, SbNameProfilingData>(namekey, data));
881 }
882 if (!inclusive) break;
883 }
884 parentidx = PRIVATE(this)->nodeData[parentidx].parentidx;
885 }
886 #endif
887 }
888
889 /*!
890 Returns the timing for a node.
891 */
892 SbTime
getNodeTiming(const SoPath * path,unsigned int flags) const893 SbProfilingData::getNodeTiming(const SoPath * path, unsigned int flags) const
894 {
895 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
896 int idx = this->getIndexNoCreate(fullpath, fullpath->getLength());
897 return this->getNodeTiming(idx, flags);
898 }
899
900 /*!
901 */
902
903 SbTime
getNodeTiming(int idx,unsigned int flags) const904 SbProfilingData::getNodeTiming(int idx, unsigned int flags) const
905 {
906 if (idx == -1) return SbTime::zero();
907 SbTime sum = PRIVATE(this)->nodeData[idx].traversaltime;
908 if ((flags & INCLUDE_CHILDREN) != 0) {
909 // FIXME: find all children of the node ad add to the sum
910 }
911 return sum;
912 }
913
914 /*!
915 */
916
917 void
setNodeFootprint(const SoPath * path,FootprintType footprinttype,size_t footprint)918 SbProfilingData::setNodeFootprint(const SoPath * path, FootprintType footprinttype, size_t footprint)
919 {
920 assert(path);
921 assert(static_cast<const SoFullPath *>(path)->getLength() > 0);
922
923 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
924 const int idx = this->getIndexCreate(fullpath, fullpath->getLength());
925 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
926
927 this->setNodeFootprint(idx, footprinttype, footprint);
928 }
929
930 /*!
931 */
932
933 void
setNodeFootprint(int idx,FootprintType footprinttype,size_t footprint)934 SbProfilingData::setNodeFootprint(int idx, FootprintType footprinttype, size_t footprint)
935 {
936 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
937
938 switch (footprinttype) {
939 case MEMORY_SIZE:
940 PRIVATE(this)->nodeData[idx].memorysize = footprint;
941 break;
942 case VIDEO_MEMORY_SIZE:
943 PRIVATE(this)->nodeData[idx].texturesize = footprint;
944 break;
945 default:
946 break;
947 }
948 }
949
950 /*!
951 */
952
953 size_t
getNodeFootprint(const SoPath * path,FootprintType footprinttype,unsigned int flags) const954 SbProfilingData::getNodeFootprint(const SoPath * path, FootprintType footprinttype, unsigned int flags) const
955 {
956 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
957 const int idx = this->getIndexNoCreate(fullpath, fullpath->getLength());
958 if (idx == -1) return 0;
959
960 return this->getNodeFootprint(idx, footprinttype, flags);
961 }
962
963 /*!
964 */
965
966 size_t
getNodeFootprint(int idx,FootprintType footprinttype,unsigned int flags) const967 SbProfilingData::getNodeFootprint(int idx, FootprintType footprinttype, unsigned int flags) const
968 {
969 assert(idx >= 0);
970 size_t footprint = 0;
971 switch (footprinttype) {
972 case MEMORY_SIZE:
973 footprint = PRIVATE(this)->nodeData[idx].memorysize;
974 break;
975 case VIDEO_MEMORY_SIZE:
976 footprint = PRIVATE(this)->nodeData[idx].texturesize;
977 break;
978 default:
979 break;
980 }
981 if ((flags & INCLUDE_CHILDREN) != 0) {
982 // FIXME: add children data to footprint
983 }
984 return footprint;
985 }
986
987 /*!
988 */
989
990 void
setNodeFlag(const SoPath * path,NodeFlag flag,SbBool on)991 SbProfilingData::setNodeFlag(const SoPath * path, NodeFlag flag, SbBool on)
992 {
993 assert(path);
994 assert(static_cast<const SoFullPath *>(path)->getLength() > 0);
995
996 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
997 const int idx = this->getIndexCreate(fullpath, fullpath->getLength());
998 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
999 this->setNodeFlag(idx, flag, on);
1000 }
1001
1002 /*!
1003 */
1004
1005 void
setNodeFlag(int idx,NodeFlag flag,SbBool on)1006 SbProfilingData::setNodeFlag(int idx, NodeFlag flag, SbBool on)
1007 {
1008 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
1009
1010 switch (flag) {
1011 case GL_CACHED_FLAG:
1012 PRIVATE(this)->nodeData[idx].flags.glcached = on ? 1 : 0;
1013 break;
1014 case CULLED_FLAG:
1015 PRIVATE(this)->nodeData[idx].flags.culled = on ? 1 : 0;
1016 break;
1017 default:
1018 break;
1019 }
1020 }
1021
1022 /*!
1023 */
1024
1025 SbBool
getNodeFlag(const SoPath * path,NodeFlag flag) const1026 SbProfilingData::getNodeFlag(const SoPath * path, NodeFlag flag) const
1027 {
1028 const SoFullPath * fullpath = static_cast<const SoFullPath *>(path);
1029 const int idx = this->getIndexNoCreate(fullpath, fullpath->getLength());
1030 if (idx == -1) return 0;
1031 return this->getNodeFlag(idx, flag);
1032 }
1033
1034 /*!
1035 */
1036
1037 SbBool
getNodeFlag(int idx,NodeFlag flag) const1038 SbProfilingData::getNodeFlag(int idx, NodeFlag flag) const
1039 {
1040 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
1041
1042 switch (flag) {
1043 case GL_CACHED_FLAG:
1044 return PRIVATE(this)->nodeData[idx].flags.glcached ? TRUE : FALSE;
1045 break;
1046 case CULLED_FLAG:
1047 return PRIVATE(this)->nodeData[idx].flags.culled ? TRUE : FALSE;
1048 break;
1049 default:
1050 break;
1051 }
1052 return FALSE;
1053 }
1054
1055 /*!
1056 */
1057 SoType
getNodeType(int idx) const1058 SbProfilingData::getNodeType(int idx) const
1059 {
1060 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
1061 return SoType::fromKey(PRIVATE(this)->nodeData[idx].nodetype);
1062 }
1063
1064 /*!
1065 */
1066 SbName
getNodeName(int idx) const1067 SbProfilingData::getNodeName(int idx) const
1068 {
1069 assert(idx >= 0 && idx < static_cast<int>(PRIVATE(this)->nodeData.size()));
1070 return SbName(PRIVATE(this)->nodeData[idx].nodename);
1071 }
1072
1073 /*!
1074 */
1075
1076 int
getLongestNameLength(void) const1077 SbProfilingData::getLongestNameLength(void) const
1078 {
1079 int longest = 0;
1080 std::map<SbProfilingNodeNameKey, SbNameProfilingData>::const_iterator it =
1081 PRIVATE(this)->nodeNameData.begin();
1082 while (it != PRIVATE(this)->nodeNameData.end()) {
1083 const int len = strlen(it->first);
1084 if (len > longest) longest = len;
1085 ++it;
1086 }
1087 return longest;
1088 }
1089
1090 /*!
1091 */
1092
1093 int
getLongestTypeNameLength(void) const1094 SbProfilingData::getLongestTypeNameLength(void) const
1095 {
1096 int longest = 0;
1097 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData>::const_iterator it =
1098 PRIVATE(this)->nodeTypeData.begin();
1099 while (it != PRIVATE(this)->nodeTypeData.end()) {
1100 SoType type = SoType::fromKey(it->first);
1101 const int len = strlen(type.getName().getString());
1102 if (len > longest) longest = len;
1103 ++it;
1104 }
1105 return longest;
1106 }
1107
1108 /*!
1109 */
1110
1111 int
getNumNodeEntries(void) const1112 SbProfilingData::getNumNodeEntries(void) const
1113 {
1114 return PRIVATE(this)->nodeData.size();
1115 }
1116
1117 /*!
1118 */
1119 void
reportAll(SbProfilingDataCB * callback,void * userdata) const1120 SbProfilingData::reportAll(SbProfilingDataCB * callback, void * userdata) const
1121 {
1122 std::vector<SbNodeProfilingData>::const_iterator it = PRIVATE(this)->nodeData.begin();
1123 const int numnodedata = PRIVATE(this)->nodeData.size();
1124 for (int idx = 0; idx < numnodedata; ++idx) {
1125 SbList<SoNode *> pointers;
1126 SbList<int> indices;
1127
1128 for (int nodeidx = idx;
1129 nodeidx != -1;
1130 nodeidx = PRIVATE(this)->nodeData[nodeidx].parentidx) {
1131 pointers.append(static_cast<SoNode *>(PRIVATE(this)->nodeData[nodeidx].node));
1132 indices.append(PRIVATE(this)->nodeData[nodeidx].childidx);
1133 }
1134
1135 // reverse lists
1136 const int pathlen = pointers.getLength();
1137 for (int c = 0; c < (pathlen / 2); ++c) {
1138 SoNode * tempnode = pointers[c];
1139 pointers[c] = pointers[pathlen-1-c];
1140 pointers[pathlen-1-c] = tempnode;
1141 int tempidx = indices[c];
1142 indices[c] = indices[pathlen-1-c];
1143 indices[pathlen-1-c] = tempidx;
1144 }
1145
1146 callback(userdata, *this, pointers, indices, idx);
1147 ++idx;
1148 }
1149 }
1150
1151 /*!
1152 Returns the amount of memory allocated for this data structure.
1153 */
1154 size_t
getProfilingDataSize(void) const1155 SbProfilingData::getProfilingDataSize(void) const
1156 {
1157 size_t nodestatsize =
1158 PRIVATE(this)->nodeData.capacity() * sizeof(SbNodeProfilingData);
1159 size_t typestatsize =
1160 PRIVATE(this)->nodeTypeData.size() * sizeof(SbTypeProfilingData);
1161 size_t namestatsize =
1162 PRIVATE(this)->nodeNameData.size() * sizeof(SbNameProfilingData);
1163 return nodestatsize + typestatsize + namestatsize + sizeof(SbProfilingDataP);
1164 }
1165
1166 /*!
1167 */
1168
1169 void
getStatsForTypesKeyList(SbList<SbProfilingNodeTypeKey> & keys_out) const1170 SbProfilingData::getStatsForTypesKeyList(SbList<SbProfilingNodeTypeKey> & keys_out) const
1171 {
1172 keys_out.truncate(0);
1173 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData>::const_iterator it =
1174 PRIVATE(this)->nodeTypeData.begin();
1175 while (it != PRIVATE(this)->nodeTypeData.end()) {
1176 keys_out.append(it->first);
1177 ++it;
1178 }
1179 }
1180
1181 /*!
1182 */
1183
1184 void
getStatsForType(SbProfilingNodeTypeKey type,SbTime & totaltime,SbTime & maxtime,uint32_t & count) const1185 SbProfilingData::getStatsForType(SbProfilingNodeTypeKey type,
1186 SbTime & totaltime, SbTime & maxtime,
1187 uint32_t & count) const
1188 {
1189 std::map<SbProfilingNodeTypeKey, SbTypeProfilingData>::const_iterator it =
1190 PRIVATE(this)->nodeTypeData.find(type);
1191 assert(it != PRIVATE(this)->nodeTypeData.end());
1192 totaltime = it->second.totaltime;
1193 maxtime = it->second.maximumtime;
1194 count = it->second.count;
1195 }
1196
1197 // *************************************************************************
1198
1199 /*!
1200 */
1201
1202 void
getStatsForNamesKeyList(SbList<SbProfilingNodeNameKey> & keys_out) const1203 SbProfilingData::getStatsForNamesKeyList(SbList<SbProfilingNodeNameKey> & keys_out) const
1204 {
1205 keys_out.truncate(0);
1206 std::map<SbProfilingNodeNameKey, SbNameProfilingData>::const_iterator it =
1207 PRIVATE(this)->nodeNameData.begin();
1208 while (it != PRIVATE(this)->nodeNameData.end()) {
1209 keys_out.append(it->first);
1210 ++it;
1211 }
1212 }
1213
1214 /*!
1215 */
1216
1217 void
getStatsForName(SbProfilingNodeNameKey name,SbTime & totaltime,SbTime & maxtime,uint32_t & count) const1218 SbProfilingData::getStatsForName(SbProfilingNodeNameKey name,
1219 SbTime & totaltime, SbTime & maxtime,
1220 uint32_t & count) const
1221 {
1222 std::map<SbProfilingNodeNameKey, SbNameProfilingData>::const_iterator it =
1223 PRIVATE(this)->nodeNameData.find(name);
1224 assert(it != PRIVATE(this)->nodeNameData.end());
1225 totaltime = it->second.totaltime;
1226 // FIXME: maximum time for when grouping into name is not yet supported
1227 maxtime = it->second.maximumtime;
1228 count = it->second.count;
1229 }
1230
1231 // *************************************************************************
1232
1233 int
operator ==(const SbProfilingData & rhs) const1234 SbProfilingData::operator == (const SbProfilingData & rhs) const
1235 {
1236 if (this->actionType != rhs.actionType) return FALSE;
1237 if (this->actionStartTime != rhs.actionStartTime) return FALSE;
1238 if (this->actionStopTime != rhs.actionStopTime) return FALSE;
1239 if (PRIVATE(this)->nodeData.size() != PRIVATE(&rhs)->nodeData.size())
1240 return FALSE;
1241
1242 for (int c = PRIVATE(this)->nodeData.size() - 1; c >= 0; --c) {
1243 if (PRIVATE(this)->nodeData[c] != PRIVATE(&rhs)->nodeData[c])
1244 return FALSE;
1245 }
1246
1247 // NOTE: the type and name info maps are not checked, because they
1248 // are just aggregates of the nodedata records and would be equal if
1249 // the node data is.
1250
1251 return TRUE;
1252 }
1253
1254 int
operator !=(const SbProfilingData & rhs) const1255 SbProfilingData::operator != (const SbProfilingData & rhs) const
1256 {
1257 return !((*this) == rhs);
1258 }
1259
1260 // *************************************************************************
1261
1262 #undef PRIVATE
1263