string ofxTimeMeasurements::getTimeStringForTM(TimeMeasurement* tm) { float time; if (tm->measuring){ string anim; switch ((int(ofGetFrameNum() * 0.2f))%6) { case 0: anim = " "; break; case 1: anim = ". "; break; case 2: anim = ".. "; break; case 3: anim = "..."; break; case 4: anim = " .."; break; case 5: anim = " ."; break; } //return " Running " + anim; return string((ofGetFrameNum()% 6 < 3 ) ? " > " : " ") + formatTime( TM_GET_MICROS() - tm->microsecondsStart, 1) + anim; }else{ string timeString; char percentChar[64]; if (!tm->settings.enabled){ return " DISABLED!"; }else{ if(tm->accumulating){ timeString = formatTime(tm->microsecondsAccum, msPrecision); time = tm->microsecondsAccum / 1000.0f; }else{ timeString = formatTime(tm->avgDuration, msPrecision); time = tm->avgDuration / 1000.0f; } float percent = 100.0f * time / (1000.0f / desiredFrameRate); bool over = false; if (percent > 100.0f){ percent = 100.0f; over = true; } int originalLen = timeString.length(); int expectedLen = 8; for(int i = 0; i < expectedLen - originalLen; i++){ timeString = " " + timeString; } if (over){ sprintf(percentChar, int(ofGetFrameNum() * 0.8)%5 < 3 ? " >100": " 100"); }else{ sprintf(percentChar, "% 5.1f", percent); } } return timeString + percentChar + "%" ; } }
float ofxTimeMeasurements::stopMeasuring(string ID, bool accumulate){ float ret = 0.0f; if (!enabled) return ret; uint64_t timeNow = TM_GET_MICROS(); //get the time before the lock() to avoid affecting //the measurement as much as possible Poco::Thread * thread = Poco::Thread::current(); mutex.lock(); core::tree<string> &tr = threadInfo[thread].tree; //easier to read, tr is our tree from now on core::tree<string>::iterator & tit = threadInfo[thread].tit; if (tit != tr.begin()){ if( tit != tr.end()){ tit = tit.out(); } } unordered_map<string,TimeMeasurement*>::iterator it; it = times.find(ID); if ( it == times.end() ){ //not found! ofLog( OF_LOG_WARNING, "ID (%s)not found at stopMeasuring(). Make sure you called" " startMeasuring with that ID first.", ID.c_str()); }else{ TimeMeasurement* t = times[ID]; if ( times[ID]->measuring ){ t->measuring = false; t->error = false; t->acrossFrames = (t->frame != ofGetFrameNum() && thread == NULL); //we only care about across-frames in main thread t->microsecondsStop = timeNow; ret = t->duration = t->microsecondsStop - t->microsecondsStart; t->avgDuration = (1.0f - timeAveragePercent) * t->avgDuration + t->duration * timeAveragePercent; if (accumulate){ t->microsecondsAccum += t->duration; } }else{ //wrong use, start first, then stop t->error = true; ofLog( OF_LOG_WARNING, "Can't stopMeasuring(%s). Make sure you called startMeasuring" " with that ID first.", ID.c_str()); } } ret = ret / 1000.0f; mutex.unlock(); return ret; //convert to ms }
void ofxTimeMeasurements::draw(int x, int y) { if (!enabled){ //drawString(ofToString(fr, msPrecision), 10, fontSize); return; } float fr = ofGetFrameRate(); uint64_t timeNow; if(internalBenchmark){ timeNow = TM_GET_MICROS(); } currentFrameNum = ofGetFrameNum(); drawLines.clear(); float percentTotal = 0.0f; float timePerFrame = 1000.0f / desiredFrameRate; mutex.lock(); vector<TimeMeasurement*> toResetUpdatedLastFrameFlag; //update time stuff, build draw lists for( unordered_map<string,TimeMeasurement*>::iterator ii = times.begin(); ii != times.end(); ++ii ){ TimeMeasurement* t = ii->second; string key = ii->first; if(!t->measuring){ if (t->life > 0.01){ t->life *= idleTimeColorDecay; //decrease life }else{ //life decays very slow when very low t->life *= deadThreadExtendedLifeDecSpeed; //decrease life very slowly } } // if (!t->updatedLastFrame && averaging){ // if we didnt update that time, make it tend to zero slowly // t->avgDuration = (1.0f - timeAveragePercent) * t->avgDuration; // } toResetUpdatedLastFrameFlag.push_back(t); } unordered_map<ThreadId, ThreadInfo>::iterator ii; vector<ThreadId> expiredThreads; //lets make sure the Main Thread is always on top vector< ThreadContainer > sortedThreadList; for( ii = threadInfo.begin(); ii != threadInfo.end(); ++ii ){ //walk all thread trees ThreadContainer cont; cont.id = ii->first; cont.info = &ii->second; if (isMainThread(ii->first)){ //is main thread sortedThreadList.insert(sortedThreadList.begin(), cont); }else{ sortedThreadList.push_back(cont); } } std::sort(sortedThreadList.begin(), sortedThreadList.end(), compareThreadPairs); #if defined(USE_OFX_HISTORYPLOT) vector<ofxHistoryPlot*> plotsToDraw; #endif for( int k = 0; k < sortedThreadList.size(); k++ ){ //walk all thread trees ThreadId thread = sortedThreadList[k].id; core::tree<string> &tr = sortedThreadList[k].info->tree; ThreadInfo & tinfo = threadInfo[thread]; PrintedLine header; header.formattedKey = "+ " + *tr; header.color = tinfo.color; header.lineBgColor = ofColor(header.color, dimColorA * 2); //header twice as alpha header.key = *tr; //key for selection, is thread name drawLines.push_back(header); //add header to drawLines int numAlive = 0; int numAdded = 0; core::tree<string>::iterator wholeTreeWalker = tr.in(); bool finishedWalking = false; float winW = ofGetWidth(); while( !finishedWalking ){ string key = *wholeTreeWalker; TimeMeasurement * t = times[*wholeTreeWalker]; if(t->thread == thread){ #if defined(USE_OFX_HISTORYPLOT) bool plotActive = false; ofxHistoryPlot* plot = plots[key]; if(plot){ if(t->settings.plotting){ if(t->updatedLastFrame){ //update plot res every now and then if(currentFrameNum%120 == 1) plot->setMaxHistory(MIN(maxPlotSamples, winW * plotResolution)); if (!freeze) { if (t->accumulating) { plot->update(t->microsecondsAccum / 1000.0f); } else { plot->update(t->avgDuration / 1000.0f); } } } plotsToDraw.push_back(plot); plotActive = true; } } #endif bool visible = t->settings.visible; bool alive = t->life > 0.0001; if(alive){ numAlive++; } if (visible && (removeExpiredTimings ? alive : true)){ PrintedLine l; l.key = key; l.tm = t; l.lineBgColor = ofColor(tinfo.color, dimColorA); int depth = wholeTreeWalker.level(); for(int i = 0; i < depth; ++i) l.formattedKey += " "; if (wholeTreeWalker.size() == 0){ l.formattedKey += "-"; }else{ l.formattedKey += "+"; } l.formattedKey += key + string(t->accumulating ? "[" + ofToString(t->numAccumulations)+ "]" : "" ); l.isAccum = t->accumulating; l.time = getTimeStringForTM(t); if(drawPercentageAsGraph){ l.percentGraph = getPctForTM(t); } l.color = tinfo.color * ((1.0 - idleTimeColorFadePercent) + idleTimeColorFadePercent * t->life); if (!t->settings.enabled){ if(t->ifClause){ l.color = disabledTextColor; }else{ l.color = disabledTextColor.getInverted(); } } #if defined(USE_OFX_HISTORYPLOT) if(plotActive){ l.plotColor = ofColor(plots[key]->getColor(), 200); } #endif if (menuActive && t->key == selection){ if(currentFrameNum%5 < 4){ l.color.invert(); l.lineBgColor = ofColor(tinfo.color, dimColorA * 1.5); } } drawLines.push_back(l); numAdded++; } //only update() and draw() count to the final % if(key == TIME_MEASUREMENTS_DRAW_KEY || key == TIME_MEASUREMENTS_UPDATE_KEY){ percentTotal += (t->avgDuration * 0.1f) / timePerFrame; } //reset accumulator t->accumulating = false; t->numAccumulations = 0; t->microsecondsAccum = 0; } //control the iterator to walk the tree "recursively" without doing so. if(wholeTreeWalker.size()){ wholeTreeWalker = wholeTreeWalker.in(); }else{ if ( wholeTreeWalker.next() == wholeTreeWalker.end() ){ wholeTreeWalker = wholeTreeWalker.out(); while( wholeTreeWalker.next() == wholeTreeWalker.end() && wholeTreeWalker != tr){ wholeTreeWalker = wholeTreeWalker.out(); } if(wholeTreeWalker == tr){ finishedWalking = true; }else{ wholeTreeWalker++; } }else{ ++wholeTreeWalker; } } } #if defined(USE_OFX_HISTORYPLOT) numActivePlots = plotsToDraw.size(); #endif if (numAlive == 0 && removeExpiredThreads){ //drop that whole section if all entries in it are not alive for(int i = 0; i < numAdded + 1; i++){ if(drawLines.size() > 0){ int delID = drawLines.size() - 1; //clear selection if needed if (selection == drawLines[delID].key){ selection = TIME_MEASUREMENTS_UPDATE_KEY; } drawLines.erase(drawLines.begin() + delID); } } expiredThreads.push_back(thread); } } //delete expired threads for(int i = 0; i < expiredThreads.size(); i++){ unordered_map<ThreadId, ThreadInfo>::iterator treeIt = threadInfo.find(expiredThreads[i]); if (treeIt != threadInfo.end()) threadInfo.erase(treeIt); } mutex.unlock(); updateLongestLabel(); //find headers int tempMaxW = -1; vector<int> headerLocations; for( int i = 0; i < drawLines.size(); i++ ){ if (drawLines[i].tm){ //its a measurement //add padding to draw in columns for(int j = drawLines[i].formattedKey.length(); j < longestLabel; j++){ drawLines[i].formattedKey += " "; } if (!drawLines[i].tm->error){ drawLines[i].shouldDrawPctGraph = true; drawLines[i].fullLine = drawLines[i].formattedKey + " " + drawLines[i].time; }else{ drawLines[i].shouldDrawPctGraph = true; drawLines[i].fullLine = drawLines[i].formattedKey + " Error!" ; } int len = drawLines[i].fullLine.length(); if(len > tempMaxW) tempMaxW = len; if(drawLines[i].tm->measuring) drawLines[i].shouldDrawPctGraph = false; }else{ //its a header drawLines[i].fullLine = drawLines[i].formattedKey; drawLines[i].shouldDrawPctGraph = false; headerLocations.push_back(i); } } int numInstructionLines = 0; if(menuActive){ //add instructions line if menu active PrintedLine l; //title line l.color = hilightColor; l.lineBgColor = ofColor(hilightColor, dimColorA * 2); l.fullLine = " KEYBOARD COMMANDS "; //len = 23 int numPad = 2 + ceil((getWidth() - charW * (23)) / charW); for(int i = 0; i < floor(numPad/2.0); i++ ) l.fullLine = "#" + l.fullLine; for(int i = 0; i < ceil(numPad/2.0); i++ ) l.fullLine += "#"; l.fullLine = " " + l.fullLine; drawLines.push_back(l); numInstructionLines++; //key command lines l.lineBgColor = ofColor(hilightColor, dimColorA); l.fullLine = " 'UP/DOWN' select measur."; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'LFT/RGHT' expand/collaps"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'RET' toggle code section"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'A' average measurements"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'F' freeze measurements"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'L' cycle widget location"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'PG_DWN' en/disable addon"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'B' internal benchmark"; drawLines.push_back(l); numInstructionLines++; l.fullLine = " 'V' expand all"; drawLines.push_back(l); numInstructionLines++; #if defined USE_OFX_HISTORYPLOT l.fullLine = " 'P' plot selectd measur."; drawLines.push_back(l); numInstructionLines++; #endif } maxW = tempMaxW; ofSetupScreen(); //mmmm---- ofPushStyle(); ofSetRectMode(OF_RECTMODE_CORNER); ofSetDrawBitmapMode(OF_BITMAPMODE_SIMPLE); ofEnableAlphaBlending(); ofPushMatrix(); ofScale(uiScale,uiScale); ofFill(); //draw all plots #if defined(USE_OFX_HISTORYPLOT) //int numCols = plotsToDraw.size() float highest = FLT_MIN; for(auto plot : plotsToDraw){ if(allPlotsTogether){ //lets find the range that covers all the plots float high = plot->getHigerRange(); if (high > highest) highest = high; plot->setDrawTitle(false); plot->setDrawBackground(false); plot->setShowSmoothedCurve(false); }else{ plot->setDrawTitle(true); plot->setDrawBackground(true); plot->setLowerRange(0); plot->setShowSmoothedCurve(true); } } float canvasW = ofGetWidth(); float canvasH = ofGetHeight(); if(allPlotsTogether && plotsToDraw.size()){ ofSetColor(0, 230); ofDrawRectangle(0, canvasH - plotHeight, canvasW / uiScale, plotHeight); } for(int i = 0; i < plotsToDraw.size(); i++){ int y = (plotBaseY == 0 ? canvasH : plotBaseY) / uiScale - plotHeight * (i + 1); if(allPlotsTogether){ plotsToDraw[i]->setRange(0, highest); y = ((plotBaseY == 0 ? canvasH : plotBaseY) - plotHeight) / uiScale; } plotsToDraw[i]->draw(0, y, canvasW / uiScale, plotHeight); if(!allPlotsTogether){ ofSetColor(99); if(i != plotsToDraw.size() -1){ ofLine(0, y, canvasW / uiScale, y ); } } if(allPlotsTogether){ ofSetColor(plotsToDraw[i]->getColor()); deque<float>& vals = plotsToDraw[i]->getValues(); float val = 0.0f; if(!vals.empty()) val = vals.back(); string msg = plotsToDraw[i]->getVariableName() + " " + ofToString(val, 2); drawString(msg, canvasW - charW * msg.size() - 2, ofGetHeight() - plotHeight - 4 - charH * (plotsToDraw.size() -1 - i)); } } #endif float totalW = getWidth(); float totalH = getHeight(); //draw bg rect ofSetColor(bgColor); ofDrawRectangle(x, y + 1, totalW, totalH); //draw all lines for(int i = 0; i < drawLines.size(); i++){ ofSetColor(drawLines[i].lineBgColor); ofRectangle lineRect = ofRectangle(x, y + 2 + i * charH, totalW, charH + (drawLines[i].tm ? 0 : 1)); ofDrawRectangle(lineRect); if(drawLines[i].isAccum && drawLines[i].tm != NULL){ ofSetColor(drawLines[i].color, 128); ofDrawRectangle(x + totalW, y + 3 + i * charH, -5, charH - 1 ); } ofSetColor(drawLines[i].color); drawString(drawLines[i].fullLine, x , y + (i + 1) * charH); if(drawLines[i].plotColor.a > 0){ //plot highlight on the sides ofSetColor(drawLines[i].plotColor); float y1 = y + 2.4f + i * charH; ofDrawTriangle( x, y1, x, y1 + charH, x + charW * 0.7f, y1 + charH * 0.5f); } if(drawPercentageAsGraph && drawLines[i].shouldDrawPctGraph && drawLines[i].percentGraph > 0.02f){ float ww = charW * 5.5; float xx = lineRect.x + lineRect.width - charW * 7; float pct = MIN(drawLines[i].percentGraph, 1.0); unsigned char a = 64; ofColor gC; if(drawLines[i].percentGraph > 1.0){ gC = ofColor(255,0,0, (currentFrameNum%4 > 2) ? 1.5 * a : a); }else{ gC = ofColor(drawLines[i].lineBgColor, a) * (1.0f - pct) + ofColor(255,0,0,a) * pct; } ofSetColor(gC); ofDrawRectangle( xx, lineRect.y + 0.2 * lineRect.height , ww * pct, lineRect.height * 0.65 ); } } if(internalBenchmark){ float offset = 0; if(drawLocation == TIME_MEASUREMENTS_TOP_LEFT || drawLocation == TIME_MEASUREMENTS_TOP_RIGHT || drawLocation == TIME_MEASUREMENTS_CUSTOM_LOCATION ){ offset = (drawLines.size() + 2.5) * charH; } ofSetColor(0); ofDrawRectangle(x, offset + y - charH, totalW, charH); ofSetColor(currentFrameNum%3 ? 255 : 64); drawString(" Meas: " + ofToString(wastedTimeAvg / 1000.f, 2) + "ms " + " Draw: " + ofToString(wastedTimeDrawingAvg / 1000.f, 2) + "ms ", x, offset + y - charH * 0.12); } if (freeze) { if(currentFrameNum%5 < 4) ofSetColor(frozenColor); else ofSetColor(ofColor::white); drawString("Frozen! 'F'", x + totalW - 13 * charW, y + charH ); } {//lines ofSetColor(hilightColor); ofMesh lines; ofSetLineWidth(0.1); lines.setMode(OF_PRIMITIVE_LINES); float fuzzyFix = 0.5; float yy = y+1 + fuzzyFix; lines.addVertex(ofVec2f(x, yy)); lines.addVertex(ofVec2f(x + totalW, yy)); yy = y + totalH - charH - 3 + fuzzyFix; lines.addVertex(ofVec2f(x, yy)); lines.addVertex(ofVec2f(x + totalW, yy)); yy = y + totalH + fuzzyFix; lines.addVertex(ofVec2f(x, yy)); lines.addVertex(ofVec2f(x + totalW, yy)); if(menuActive){ yy = y + totalH + fuzzyFix - (numInstructionLines + 1) * charH - 3; lines.addVertex(ofVec2f(x, yy)); lines.addVertex(ofVec2f(x + totalW, yy)); yy = y + totalH + fuzzyFix - (numInstructionLines) * charH - 3; lines.addVertex(ofVec2f(x, yy)); lines.addVertex(ofVec2f(x + totalW, yy)); } lines.draw(); }//lines //print bottom line, fps and stuff bool missingFrames = ( fr < desiredFrameRate - 1.0 ); // tolerance of 1 fps TODO! static char msg[128]; sprintf(msg, "%2.1f fps % 5.1f%%", fr, percentTotal ); if(missingFrames){ ofSetColor(170,33,33); //reddish fps below desired fps }else{ ofSetColor(hilightColor); } int len = strlen(msg); string pad = " "; int diff = (maxW - len) - 1; for(int i = 0; i < diff; i++) pad += " "; int lastLine = ( drawLines.size() + 1 ) * charH + 2; drawString( pad + msg, x, y + lastLine ); //show activate menu key if(menuActive ) ofSetColor(hilightColor.getInverted()); else ofSetColor(hilightColor); drawString(" '" + ofToString(char(activateKey)) + "'", x, y + lastLine); //show averaging warning if (averaging) { if (currentFrameNum % 5 < 2) ofSetColor(hilightColor); else ofSetColor(ofColor::limeGreen); drawString(" avg!", x + charW * 3.5, y + lastLine); } for(int i = 0; i < toResetUpdatedLastFrameFlag.size(); i++){ toResetUpdatedLastFrameFlag[i]->updatedLastFrame = false; } ofPopMatrix(); ofPopStyle(); if(internalBenchmark){ wastedTimeDrawingThisFrame += TM_GET_MICROS() - timeNow; wastedTimeAvg = wastedTimeThisFrame * 0.025f + 0.975f * wastedTimeAvg; wastedTimeDrawingAvg = wastedTimeDrawingThisFrame * 0.025f + 0.975f * wastedTimeDrawingAvg; wastedTimeThisFrame = wastedTimeDrawingThisFrame = 0; } }
float ofxTimeMeasurements::stopMeasuring(const string & ID, bool accumulate){ if (!enabled) return 0.0f; float ret = 0.0f; string localID = ID; uint64_t timeNow = TM_GET_MICROS(); //get the time before the lock() to avoid affecting ThreadId thread = getThreadID(); bool bIsMainThread = isMainThread(thread); mutex.lock(); unordered_map<ThreadId, ThreadInfo>::iterator threadIt = threadInfo.find(thread); if(threadIt == threadInfo.end()){ //thread not found! mutex.unlock(); return 0.0f; } ThreadInfo & tinfo = threadIt->second; if(tinfo.order > 0){ localID = "T" + ofToString(tinfo.order) + ":" + localID; } core::tree<string> & tr = tinfo.tree; //easier to read, tr is our tree from now on core::tree<string>::iterator & tit = tinfo.tit; if (tit.out() != tr.end()){ tit = tit.out(); }else{ ofLogError("ofxTimeMeasurements") << "tree climbing too high up! (" << localID << ")"; } unordered_map<string,TimeMeasurement*>::iterator it; it = times.find(localID); if ( it == times.end() ){ //not found! ofLogWarning("ofxTimeMeasurements") << "ID ("<< localID << ")not found at stopMeasuring(). Make sure you called startMeasuring with that ID first."; }else{ TimeMeasurement* t = it->second; if ( t->measuring ){ t->measuring = false; t->thread = thread; t->error = false; t->acrossFrames = (bIsMainThread && t->frame != currentFrameNum); //we only care about across-frames in main thread t->microsecondsStop = timeNow; ret = t->duration = timeNow - t->microsecondsStart; if (!freeze) { if (!averaging) { t->avgDuration = t->duration; } else { t->avgDuration = (1.0f - timeAveragePercent) * t->avgDuration + t->duration * timeAveragePercent; } } if (accumulate && !freeze){ t->microsecondsAccum += t->avgDuration; } }else{ //wrong use, start first, then stop t->error = true; ofLogWarning("ofxTimeMeasurements") << "Can't stopMeasuring(" << localID << "). Make sure you called startMeasuring() with that ID first."; } } mutex.unlock(); if(internalBenchmark){ wastedTimeThisFrame += TM_GET_MICROS() - timeNow; } return ret / 1000.0f; //convert to ms }
bool ofxTimeMeasurements::startMeasuring(const string & ID, bool accumulate, bool ifClause){ string localID = ID; if (!enabled) return true; uint64_t wastedTime; if(internalBenchmark){ wastedTime = TM_GET_MICROS(); } if (!settingsLoaded){ loadSettings(); settingsLoaded = true; } string threadName = "Thread"; ThreadId thread = getThreadID(); bool bIsMainThread = isMainThread(thread); if(!bIsMainThread){ if(Poco::Thread::current()){ threadName = Poco::Thread::current()->getName(); } } mutex.lock(); unordered_map<ThreadId, ThreadInfo>::iterator threadIt = threadInfo.find(thread); ThreadInfo * tinfo = NULL; core::tree<string> *tr = NULL; bool newThread = threadIt == threadInfo.end(); if (newThread){ //new thread! //cout << "NewThread! " << ID << " " << &thread << endl; threadInfo[thread] = ThreadInfo(); tinfo = &threadInfo[thread]; tr = &tinfo->tree; //easier to read, tr is our tree from now on if (!bIsMainThread){ tinfo->color = threadColorTable[numThreads%(threadColorTable.size())]; numThreads++; }else{ //main thread tinfo->color = hilightColor; } tinfo->order = numThreads; string tName = bIsMainThread ? "Main Thread" : ("T" + ofToString(tinfo->order) + ": " + threadName); //init the iterator *tr = tName; //thread name is root tinfo->tit = (core::tree<string>::iterator)*tr; }else{ tinfo = &threadIt->second; tr = &(tinfo->tree); //easier to read, tr is our tree from now on } if(tinfo->order > 0){ localID = "T" + ofToString(tinfo->order) + ":" + localID; } //see if we had an actual measurement, or its a new one unordered_map<string, TimeMeasurement*>::iterator tit = times.find(localID); TimeMeasurement* t; if(tit == times.end()){ //if it wasnt in the tree, append it times[localID] = t = new TimeMeasurement(); unordered_map<string, TimeMeasurementSettings>::iterator it2 = settings.find(localID); if (it2 != settings.end()){ t->settings = settings[localID]; if(tinfo->tit.out() == tinfo->tit.end()){ //if we are the tree root - we cant be hidden! t->settings.visible = true; } } tinfo->tit = tinfo->tit.push_back(localID); }else{ core::tree<string>::iterator temptit = tr->tree_find_depth(localID); if(temptit != tr->end()){ tinfo->tit = temptit; }else{ //cout << "gotcha!" << endl; //this is the rare case where we already had a measurement for this ID, //but it must be assigned to another old thread bc we cant find it! //so we re-add that ID for this thread and update the tree iterator tinfo->tit = tinfo->tit.push_back(localID); } t = tit->second; } t->key = localID; t->life = 1.0f; // t->measuring = true; t->ifClause = ifClause; t->microsecondsStop = 0; t->accumulating = accumulate; if(accumulate) t->numAccumulations++; t->error = false; t->frame = currentFrameNum; t->updatedLastFrame = true; t->microsecondsStart = TM_GET_MICROS(); t->thread = thread; mutex.unlock(); if(internalBenchmark){ wastedTimeThisFrame += t->microsecondsStart - wastedTime; } return t->settings.enabled; }
bool ofxTimeMeasurements::startMeasuring(string ID, bool accumulate, ofColor color){ if (!enabled) return true; if (!settingsLoaded){ loadSettings(); settingsLoaded = true; } Poco::Thread * thread = Poco::Thread::current(); bool isMainThread = (mainThreadID == thread); uint64_t timeNow = TM_GET_MICROS(); //get the time before the lock() to avoid affecting //the measurement as much as possible mutex.lock(); unordered_map<Poco::Thread*, ThreadInfo>::iterator threadIt = threadInfo.find(thread); ThreadInfo & tinfo = threadInfo[thread]; core::tree<string> &tr = tinfo.tree; //easier to read, tr is our tree from now on if (threadIt == threadInfo.end()){ //new thread! //string tName = isMainThread ? "mainThread" : string("Thread " + ofToString(threadCounter)); string tName = isMainThread ? "Main Thread" : string(Poco::Thread::current()->getName() + " (" + ofToString(numThreads) + ")"); //init the iterator *tr = tName; //thread name is root tinfo.tit = (core::tree<string>::iterator)tr; tinfo.order = numThreads; if (thread){ if(color.a == 0 && color.r == 0 && color.g == 0 && color.b == 0){ tinfo.color = threadColorTable[numThreads%(threadColorTable.size())]; }else{ tinfo.color = color; } numThreads++; }else{ tinfo.color = hilightColor; } } //see if the new measurement already was in tree core::tree<string>::iterator searchIt = tr.tree_find_depth(ID); if(searchIt == tr.end()){ //if it wasnt in the tree, append it if (ID == TIME_MEASUREMENTS_SETUP_KEY || ID == TIME_MEASUREMENTS_UPDATE_KEY || ID == TIME_MEASUREMENTS_DRAW_KEY ){ //setup update and draw are always at root level! if (tr.size()){ tinfo.tit = tr.push_back(ID); }else{ tinfo.tit = tr.insert(ID); } }else{ if(tinfo.tit == tr.end()){ ofLogError() << "ofxTimeMeasurements error - unordered tree cant climb back up!"; //TODO handle this better! tinfo.tit = tr.push_back(ID); }else{ tinfo.tit = tinfo.tit.push_back(ID); } } }else{ tinfo.tit = searchIt; } //see if we had an actual measurement, or its a new one unordered_map<string, TimeMeasurement*>::iterator tit = times.find(ID); if (tit == times.end()){ //not found, let alloc a new TimeMeasurement times[ID] = new TimeMeasurement(); //keyOrder[ keyOrder.size() ] = ID; unordered_map<string, TimeMeasurementSettings>::iterator it2 = settings.find(ID); if (it2 != settings.end()){ times[ID]->settings = settings[ID]; } } TimeMeasurement* t = times[ID]; t->key = ID; t->life = 1.0f; // t->measuring = true; t->microsecondsStart = timeNow; t->microsecondsStop = 0; t->accumulating = accumulate; t->error = false; t->frame = ofGetFrameNum(); t->measuring = true; t->updatedLastFrame = true; mutex.unlock(); return t->settings.enabled; }