SH-3275 WIP interesting Update viewer metrics system to be more flexible

fixed memory leak
fixed glitching of fast timer display
master
Richard Linden 2013-01-27 21:35:20 -08:00
parent 09ee16c61d
commit 2c68d5367c
8 changed files with 196 additions and 165 deletions

View File

@ -425,6 +425,13 @@ void TimeBlockAccumulator::reset( const TimeBlockAccumulator* other )
mTotalTimeCounter = 0;
mChildTimeCounter = 0;
mCalls = 0;
if (other)
{
mLastCaller = other->mLastCaller;
mActiveCount = other->mActiveCount;
mMoveUpTree = other->mMoveUpTree;
mParent = other->mParent;
}
}
} // namespace LLTrace

View File

@ -243,8 +243,6 @@ namespace LLTrace
: public LLInstanceTracker<TraceType<ACCUMULATOR>, std::string>
{
public:
typedef typename MeanValueType<TraceType<ACCUMULATOR> >::type mean_t;
TraceType(const char* name, const char* description = NULL)
: LLInstanceTracker<TraceType<ACCUMULATOR>, std::string>(name),
mName(name),
@ -468,25 +466,37 @@ namespace LLTrace
: public TraceType<TimeBlockAccumulator>
{
public:
typedef F32 mean_t;
TraceType(const char* name, const char* description = "")
: TraceType<TimeBlockAccumulator>(name, description)
{}
};
template<>
struct MeanValueType<TraceType<TimeBlockAccumulator::CallCountAspect> >
{
typedef F64 type;
};
template<>
class TraceType<TimeBlockAccumulator::SelfTimeAspect>
: public TraceType<TimeBlockAccumulator>
{
public:
typedef F32 mean_t;
TraceType(const char* name, const char* description = "")
: TraceType<TimeBlockAccumulator>(name, description)
{}
};
template<>
struct MeanValueType<TraceType<TimeBlockAccumulator::SelfTimeAspect> >
{
typedef LLUnit<LLUnits::Seconds, F64> type;
};
class TimeBlock;
class TimeBlockTreeNode
{

View File

@ -109,8 +109,18 @@ void Recording::handleSplitTo(Recording& other)
{
stop();
other.restart();
syncTo(other);
}
void Recording::syncTo(Recording& other)
{
other.mCountsFloat.write()->reset(mCountsFloat);
other.mMeasurementsFloat.write()->reset(mMeasurementsFloat);
other.mCounts.write()->reset(mCounts);
other.mMeasurements.write()->reset(mMeasurements);
other.mStackTimers.write()->reset(mStackTimers);
other.mMemStats.write()->reset(mMemStats);
//TODO: figure out how to get seamless handoff of timing stats
}

View File

@ -119,6 +119,7 @@ namespace LLTrace
// gather data from recording, ignoring time relationship (for example, pulling data from slave threads)
void mergeRecording(const Recording& other);
// grab latest recorded data
void update();
// Timer accessors
@ -217,6 +218,8 @@ namespace LLTrace
LLUnit<LLUnits::Seconds, F64> getDuration() const { return LLUnit<LLUnits::Seconds, F64>(mElapsedSeconds); }
void syncTo(Recording& other);
private:
friend class ThreadRecorder;
@ -337,9 +340,9 @@ namespace LLTrace
}
template <typename T>
typename TraceType<T>::mean_t getPeriodMean(const TraceType<T>& stat) const
typename MeanValueType<TraceType<T> >::type getPeriodMean(const TraceType<T>& stat) const
{
typename TraceType<T>::mean_t mean = 0.0;
typename MeanValueType<TraceType<T> >::type mean = 0.0;
for (S32 i = 0; i < mNumPeriods; i++)
{
if (mRecordingPeriods[i].getDuration() > 0.f)
@ -352,9 +355,9 @@ namespace LLTrace
}
template <typename T>
typename TraceType<T>::mean_t getPeriodMeanPerSec(const TraceType<T>& stat) const
typename MeanValueType<TraceType<T> >::type getPeriodMeanPerSec(const TraceType<T>& stat) const
{
typename TraceType<T>::mean_t mean = 0.0;
typename MeanValueType<TraceType<T> >::type mean = 0.0;
for (S32 i = 0; i < mNumPeriods; i++)
{
if (mRecordingPeriods[i].getDuration() > 0.f)

View File

@ -76,7 +76,7 @@ ThreadRecorder::~ThreadRecorder()
while(mActiveRecordings.size())
{
mActiveRecordings.front().mTargetRecording->stop();
mActiveRecordings.front()->mTargetRecording->stop();
}
set_thread_recorder(NULL);
delete[] mTimeBlockTreeNodes;
@ -94,31 +94,37 @@ TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index)
void ThreadRecorder::activate( Recording* recording )
{
mActiveRecordings.push_front(ActiveRecording(recording));
mActiveRecordings.front().mBaseline.makePrimary();
ActiveRecording* active_recording = new ActiveRecording(recording);
if (!mActiveRecordings.empty())
{
mActiveRecordings.front()->mBaseline.syncTo(active_recording->mBaseline);
}
mActiveRecordings.push_front(active_recording);
mActiveRecordings.front()->mBaseline.makePrimary();
}
std::list<ThreadRecorder::ActiveRecording>::iterator ThreadRecorder::update( Recording* recording )
ThreadRecorder::active_recording_list_t::iterator ThreadRecorder::update( Recording* recording )
{
std::list<ActiveRecording>::iterator it, end_it;
active_recording_list_t::iterator it, end_it;
for (it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
it != end_it;
++it)
{
std::list<ActiveRecording>::iterator next_it = it;
active_recording_list_t::iterator next_it = it;
++next_it;
// if we have another recording further down in the stack...
if (next_it != mActiveRecordings.end())
{
// ...push our gathered data down to it
next_it->mBaseline.appendRecording(it->mBaseline);
(*next_it)->mBaseline.appendRecording((*it)->mBaseline);
}
// copy accumulated measurements into result buffer and clear accumulator (mBaseline)
it->moveBaselineToTarget();
(*it)->moveBaselineToTarget();
if (it->mTargetRecording == recording)
if ((*it)->mTargetRecording == recording)
{
// found the recording, so return it
break;
@ -142,17 +148,18 @@ AccumulatorBuffer<MemStatAccumulator> gMemStats;
void ThreadRecorder::deactivate( Recording* recording )
{
std::list<ActiveRecording>::iterator it = update(recording);
active_recording_list_t::iterator it = update(recording);
if (it != mActiveRecordings.end())
{
// and if we've found the recording we wanted to update
std::list<ActiveRecording>::iterator next_it = it;
active_recording_list_t::iterator next_it = it;
++next_it;
if (next_it != mActiveRecordings.end())
{
next_it->mTargetRecording->makePrimary();
(*next_it)->mTargetRecording->makePrimary();
}
delete *it;
mActiveRecordings.erase(it);
}
}
@ -244,7 +251,7 @@ void MasterThreadRecorder::pullFromSlaveThreads()
LLMutexLock lock(&mSlaveListMutex);
Recording& target_recording = mActiveRecordings.front().mBaseline;
Recording& target_recording = mActiveRecordings.front()->mBaseline;
for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
it != end_it;
++it)

View File

@ -39,13 +39,14 @@ namespace LLTrace
{
protected:
struct ActiveRecording;
typedef std::list<ActiveRecording*> active_recording_list_t;
public:
ThreadRecorder();
virtual ~ThreadRecorder();
void activate(Recording* recording);
std::list<struct ActiveRecording>::iterator update(Recording* recording);
active_recording_list_t::iterator update(Recording* recording);
void deactivate(Recording* recording);
virtual void pushToMaster() = 0;
@ -63,11 +64,12 @@ namespace LLTrace
void moveBaselineToTarget();
};
Recording mThreadRecording;
std::list<ActiveRecording> mActiveRecordings;
class BlockTimer* mRootTimer;
TimeBlockTreeNode* mTimeBlockTreeNodes;
size_t mNumTimeBlockTreeNodes;
active_recording_list_t mActiveRecordings;
class BlockTimer* mRootTimer;
TimeBlockTreeNode* mTimeBlockTreeNodes;
size_t mNumTimeBlockTreeNodes;
};
class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder

View File

@ -107,7 +107,7 @@ LLFastTimerView::LLFastTimerView(const LLSD& key)
mRecording(&get_frame_recording()),
mPauseHistory(false)
{
mBarRects = new std::vector<LLRect>[MAX_VISIBLE_HISTORY + 1];
mTimerBars = new std::vector<TimerBar>[MAX_VISIBLE_HISTORY + 1];
}
LLFastTimerView::~LLFastTimerView()
@ -117,7 +117,7 @@ LLFastTimerView::~LLFastTimerView()
delete mRecording;
}
mRecording = NULL;
delete [] mBarRects;
delete [] mTimerBars;
}
void LLFastTimerView::onPause()
@ -282,7 +282,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
++it, ++i)
{
// is mouse over bar for this timer?
if (mBarRects[mHoverBarIndex][i].pointInRect(x, y))
if (mTimerBars[mHoverBarIndex][i].mRect.pointInRect(x, y))
{
mHoverID = (*it);
if (mHoverTimer != *it)
@ -294,7 +294,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
mHoverTimer = (*it);
}
mToolTipRect = mBarRects[mHoverBarIndex][i];
mToolTipRect = mTimerBars[mHoverBarIndex][i].mRect;
}
if ((*it)->getCollapsed())
@ -1311,11 +1311,11 @@ S32 LLFastTimerView::drawHelp( S32 y )
} return y;
}
void LLFastTimerView::drawTicks( LLUnit<LLUnits::Seconds, F64> total_time )
void LLFastTimerView::drawTicks()
{
// Draw MS ticks
{
LLUnit<LLUnits::Milliseconds, U32> ms = total_time;
LLUnit<LLUnits::Milliseconds, U32> ms = mTotalTimeDisplay;
std::string tick_label;
S32 x;
S32 barw = mBarRect.getWidth();
@ -1377,172 +1377,70 @@ void LLFastTimerView::drawBorders( S32 y, const S32 x_start, S32 bar_height, S32
}
}
LLUnit<LLUnits::Seconds, F64> LLFastTimerView::getTotalTime()
void LLFastTimerView::updateTotalTime()
{
LLUnit<LLUnits::Seconds, F64> total_time;
switch(mDisplayMode)
{
case 0:
total_time = mRecording->getPeriodMean(FTM_FRAME)*2;
mTotalTimeDisplay = mRecording->getPeriodMean(FTM_FRAME)*2;
break;
case 1:
total_time = mAllTimeMax;
mTotalTimeDisplay = mAllTimeMax;
break;
case 2:
// Calculate the max total ticks for the current history
total_time = mRecording->getPeriodMax(FTM_FRAME);
mTotalTimeDisplay = mRecording->getPeriodMax(FTM_FRAME);
break;
default:
total_time = LLUnit<LLUnits::Milliseconds, F32>(100);
mTotalTimeDisplay = LLUnit<LLUnits::Milliseconds, F32>(100);
break;
}
return total_time;
}
void LLFastTimerView::drawBars()
{
LLUnit<LLUnits::Seconds, F64> total_time = getTotalTime();
if (total_time <= 0.0) return;
updateTotalTime();
if (mTotalTimeDisplay <= 0.0) return;
LLPointer<LLUIImage> box_imagep = LLUI::getUIImage("Rounded_Square");
LLLocalClipRect clip(mBarRect);
S32 bar_height = (mBarRect.mTop - MARGIN - LINE_GRAPH_HEIGHT) / (MAX_VISIBLE_HISTORY + 2);
S32 vpad = llmax(1, bar_height / 4); // spacing between bars
bar_height -= vpad;
drawTicks(total_time);
drawTicks();
S32 y = mBarRect.mTop - ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 4);
drawBorders(y, mBarRect.mLeft, bar_height, vpad);
// Draw bars for each history entry
// Special: -1 = show running average
gGL.getTexUnit(0)->bind(box_imagep->getImage());
LLPointer<LLUIImage> bar_image = LLUI::getUIImage("Rounded_Square");
gGL.getTexUnit(0)->bind(bar_image->getImage());
const S32 histmax = llmin(mRecording->getNumPeriods()+1, MAX_VISIBLE_HISTORY);
for (S32 j = -1; j < histmax && y > LINE_GRAPH_HEIGHT; j++)
{
mBarRects[llmax(j, 0)].clear();
int sublevel_dx[FTV_MAX_DEPTH];
int sublevel_left[FTV_MAX_DEPTH];
int sublevel_right[FTV_MAX_DEPTH];
S32 tidx = (j >= 0)
S32 history_index = (j >= 0)
? j + 1 + mScrollIndex
: -1;
S32 rect_index = llmax(j, 0);
mTimerBars[rect_index].clear();
// draw the bars for each stat
std::vector<S32> xpos;
S32 deltax = 0;
xpos.push_back(mBarRect.mLeft);
updateTimerBars(&FTM_FRAME, LLRect(mBarRect.mLeft, 0, mBarRect.mRight, -bar_height), mTimerBars[rect_index], history_index);
TimeBlock* prev_id = NULL;
S32 i = 0;
for(timer_tree_iterator_t it = begin_timer_tree(FTM_FRAME);
it != end_timer_tree();
++it, ++i)
for (std::vector<TimerBar>::iterator it = mTimerBars[rect_index].begin(), end_it = mTimerBars[rect_index].end();
it != end_it;
++it)
{
TimeBlock* idp = (*it);
F32 frac = tidx == -1
? (mRecording->getPeriodMean(*idp) / total_time)
: (mRecording->getPrevRecordingPeriod(tidx).getSum(*idp).value() / total_time.value());
TimerBar& timer_bar = *it;
gGL.color4fv(timer_bar.mColor.mV);
gl_segmented_rect_2d_fragment_tex(timer_bar.mRect.mLeft, timer_bar.mRect.mTop, timer_bar.mRect.mRight, timer_bar.mRect.mBottom,
bar_image->getTextureWidth(), bar_image->getTextureHeight(),
16,
timer_bar.mStartFraction, timer_bar.mEndFraction);
S32 dx = llround(frac * (F32)mBarRect.getWidth());
S32 prev_delta_x = deltax;
deltax = dx;
const int level = get_depth(idp) - 1;
while ((S32)xpos.size() > level + 1)
{
xpos.pop_back();
}
LLRect bar_rect;
bar_rect.setLeftTopAndSize(xpos.back(), y, dx, bar_height);
mBarRects[llmax(j, 0)].push_back(bar_rect);
if (level == 0)
{
sublevel_left[level] = mBarRect.mLeft;
sublevel_dx[level] = dx;
sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
}
else if (prev_id && get_depth(prev_id) < get_depth(idp))
{
F64 sublevelticks = 0;
for (TimeBlock::child_const_iter it = prev_id->beginChildren();
it != prev_id->endChildren();
++it)
{
sublevelticks += (tidx == -1)
? mRecording->getPeriodMean(**it).value()
: mRecording->getPrevRecordingPeriod(tidx).getSum(**it).value();
}
F32 subfrac = (F32)sublevelticks / (F32)total_time.value();
sublevel_dx[level] = (int)(subfrac * (F32)mBarRect.getWidth() + .5f);
if (mDisplayCenter == ALIGN_CENTER)
{
bar_rect.mLeft += (prev_delta_x - sublevel_dx[level])/2;
}
else if (mDisplayCenter == ALIGN_RIGHT)
{
bar_rect.mLeft += (prev_delta_x - sublevel_dx[level]);
}
sublevel_left[level] = bar_rect.mLeft;
sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
}
xpos.back() = bar_rect.mRight;
xpos.push_back(bar_rect.mLeft);
if (bar_rect.getWidth() > 0)
{
LLColor4 color = sTimerColors[idp];
S32 scale_offset = 0;
BOOL is_child_of_hover_item = (idp == mHoverID);
TimeBlock* next_parent = idp->getParent();
while(!is_child_of_hover_item && next_parent)
{
is_child_of_hover_item = (mHoverID == next_parent);
if (next_parent->getParent() == next_parent) break;
next_parent = next_parent->getParent();
}
if (idp == mHoverID)
{
scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
//color = lerp(color, LLColor4::black, -0.4f);
}
else if (mHoverID != NULL && !is_child_of_hover_item)
{
color = lerp(color, LLColor4::grey, 0.8f);
}
gGL.color4fv(color.mV);
F32 start_fragment = llclamp((F32)(bar_rect.mLeft - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
F32 end_fragment = llclamp((F32)(bar_rect.mRight - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
gl_segmented_rect_2d_fragment_tex(
sublevel_left[level],
bar_rect.mTop - level + scale_offset,
sublevel_right[level],
bar_rect.mBottom + level - scale_offset,
box_imagep->getTextureWidth(), box_imagep->getTextureHeight(),
16,
start_fragment, end_fragment);
}
if ((*it)->getCollapsed())
{
it.skipDescendants();
}
prev_id = idp;
}
y -= (bar_height + vpad);
if (j < 0)
y -= bar_height;
@ -1550,14 +1448,99 @@ void LLFastTimerView::drawBars()
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
LLRect LLFastTimerView::updateTimerBars(LLTrace::TimeBlock* time_block, LLRect bar_rect, std::vector<TimerBar>& bars, S32 history_index)
{
F32 self_time_frame_fraction = history_index == -1
? (mRecording->getPeriodMean(time_block->selfTime()) / mTotalTimeDisplay)
: (mRecording->getPrevRecordingPeriod(history_index).getSum(time_block->selfTime()) / mTotalTimeDisplay);
S32 self_time_width = llround(self_time_frame_fraction * (F32)mBarRect.getWidth());
LLRect child_rect = bar_rect;
if (mDisplayCenter == ALIGN_CENTER)
{
child_rect.mLeft += self_time_width / 2;
self_time_width -= self_time_width / 2;
}
else if (mDisplayCenter == ALIGN_RIGHT)
{
child_rect.mLeft += self_time_width;
self_time_width = 0;
}
if (child_rect.getHeight() > 3)
{
child_rect.mTop -= 1;
child_rect.mBottom += 1;
}
child_rect.mRight = child_rect.mLeft;
// reserve a spot for this bar to be rendered before its children
// even though we don't know its size yet
S32 first_child_left = child_rect.mLeft;
S32 bar_rect_index = bars.size();
bars.push_back(TimerBar());
for (TimeBlock::child_iter it = time_block->beginChildren(), end_it = time_block->endChildren(); it != end_it; ++it)
{
child_rect = updateTimerBars(*it, child_rect, bars, history_index);
// advance for next child
child_rect.mLeft = child_rect.mRight;
}
// now loop through children and figure out portion of bar image covered by each bar, now that we know the
// sum of all children
//S32 total_width_children = child_rect.mRight - first_child_left;
//S32 child_bar_index = bar_rect_index + 1;
//F32 bar_fraction_start = 0.f;
//for (TimeBlock::child_iter it = time_block->beginChildren(), end_it = time_block->endChildren();
// it != end_it;
// ++it)
//{
// TimerBar& child_timer_bar = bars[child_bar_index];
// child_timer_bar.mStartFraction = bar_fraction_start;
// child_timer_bar.mEndFraction = (F32)child_timer_bar.mRect.getWidth() / (F32)total_width_children;
// bar_fraction_start = child_timer_bar.mEndFraction;
// // advance for next child
// child_bar_index++;
//}
bar_rect.mRight = child_rect.mRight + self_time_width;
if (bar_rect.getWidth() > 0)
{
LLColor4 color = sTimerColors[time_block];
S32 scale_offset = 0;
BOOL is_child_of_hover_item = (time_block == mHoverID);
TimeBlock* next_parent = time_block->getParent();
while(!is_child_of_hover_item && next_parent)
{
is_child_of_hover_item = (mHoverID == next_parent);
if (next_parent->getParent() == next_parent) break;
next_parent = next_parent->getParent();
}
if (time_block == mHoverID)
{
scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
}
else if (mHoverID != NULL && !is_child_of_hover_item)
{
color = lerp(color, LLColor4::grey, 0.8f);
}
bar_rect.mTop += scale_offset;
bar_rect.mBottom -= scale_offset;
bars[bar_rect_index].mRect = bar_rect;
bars[bar_rect_index].mColor = color;
}
return bar_rect;
}

View File

@ -70,7 +70,7 @@ protected:
virtual void onClickCloseBtn();
private:
void drawTicks(LLUnit<LLUnits::Seconds, F64> total_time);
void drawTicks();
void drawLineGraph();
void drawLegend(S32 y);
S32 drawHelp(S32 y);
@ -79,10 +79,18 @@ private:
void printLineStats();
void generateUniqueColors();
LLUnit<LLUnits::Seconds, F64> getTotalTime();
void updateTotalTime();
struct TimerBar
{
LLRect mRect;
LLColor4 mColor;
F32 mStartFraction,
mEndFraction;
};
LLRect updateTimerBars(LLTrace::TimeBlock* time_block, LLRect bar_rect, std::vector<TimerBar>& bars, S32 history_index);
std::vector<LLRect>* mBarRects;
std::vector<TimerBar>* mTimerBars;
S32 mDisplayMode;
typedef enum child_alignment
@ -96,7 +104,8 @@ private:
ChildAlignment mDisplayCenter;
bool mDisplayCalls,
mDisplayHz;
LLUnit<LLUnits::Seconds, F64> mAllTimeMax;
LLUnit<LLUnits::Seconds, F64> mAllTimeMax,
mTotalTimeDisplay;
LLRect mBarRect;
S32 mScrollIndex;
LLTrace::TimeBlock* mHoverID;