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