Merge branch 'master' of git://github.com/Shyotl/SingularityViewer
Conflicts: indra/newview/llstartup.cpp indra/newview/llviewerregion.cpp
This commit is contained in:
@@ -35,7 +35,7 @@ set(llcommon_SOURCE_FILES
|
||||
llerrorthread.cpp
|
||||
llevent.cpp
|
||||
lleventtimer.cpp
|
||||
llfasttimer.cpp
|
||||
llfasttimer_class.cpp
|
||||
llfile.cpp
|
||||
llfindlocale.cpp
|
||||
llfixedbuffer.cpp
|
||||
@@ -54,6 +54,7 @@ set(llcommon_SOURCE_FILES
|
||||
llmetrics.cpp
|
||||
llmortician.cpp
|
||||
lloptioninterface.cpp
|
||||
llptrto.cpp
|
||||
llprocesslauncher.cpp
|
||||
llprocessor.cpp
|
||||
llqueuedthread.cpp
|
||||
@@ -136,6 +137,7 @@ set(llcommon_HEADER_FILES
|
||||
llextendedstatus.h
|
||||
lleventtimer.h
|
||||
llfasttimer.h
|
||||
llfasttimer_class.h
|
||||
llfile.h
|
||||
llfindlocale.h
|
||||
llfixedbuffer.h
|
||||
@@ -171,6 +173,7 @@ set(llcommon_HEADER_FILES
|
||||
llprocessor.h
|
||||
llptrskiplist.h
|
||||
llptrskipmap.h
|
||||
llptrto.h
|
||||
llqueuedthread.h
|
||||
llrand.h
|
||||
llrefcount.h
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* @file llfasttimer.cpp
|
||||
* @brief Implementation of the fast timer.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llfasttimer.h"
|
||||
#include "llmemory.h"
|
||||
#include "llprocessor.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include "lltimer.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// statics
|
||||
|
||||
LLFastTimer::EFastTimerType LLFastTimer::sCurType = LLFastTimer::FTM_OTHER;
|
||||
int LLFastTimer::sCurDepth = 0;
|
||||
U64 LLFastTimer::sStart[LLFastTimer::FTM_MAX_DEPTH];
|
||||
U64 LLFastTimer::sCounter[LLFastTimer::FTM_NUM_TYPES];
|
||||
U64 LLFastTimer::sCountHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
|
||||
U64 LLFastTimer::sCountAverage[LLFastTimer::FTM_NUM_TYPES];
|
||||
U64 LLFastTimer::sCalls[LLFastTimer::FTM_NUM_TYPES];
|
||||
U64 LLFastTimer::sCallHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
|
||||
U64 LLFastTimer::sCallAverage[LLFastTimer::FTM_NUM_TYPES];
|
||||
S32 LLFastTimer::sCurFrameIndex = -1;
|
||||
S32 LLFastTimer::sLastFrameIndex = -1;
|
||||
int LLFastTimer::sPauseHistory = 0;
|
||||
int LLFastTimer::sResetHistory = 0;
|
||||
|
||||
U64 LLFastTimer::sClockResolution = calc_clock_frequency(50U); // Resolution of get_clock_count()
|
||||
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
{
|
||||
return sClockResolution >> 8;
|
||||
}
|
||||
|
||||
void LLFastTimer::reset()
|
||||
{
|
||||
countsPerSecond(); // good place to calculate clock frequency
|
||||
|
||||
if (sCurDepth != 0)
|
||||
{
|
||||
llerrs << "LLFastTimer::Reset() when sCurDepth != 0" << llendl;
|
||||
}
|
||||
if (sPauseHistory)
|
||||
{
|
||||
sResetHistory = 1;
|
||||
}
|
||||
else if (sResetHistory)
|
||||
{
|
||||
sCurFrameIndex = -1;
|
||||
sResetHistory = 0;
|
||||
}
|
||||
else if (sCurFrameIndex >= 0)
|
||||
{
|
||||
int hidx = sCurFrameIndex % FTM_HISTORY_NUM;
|
||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
||||
{
|
||||
sCountHistory[hidx][i] = sCounter[i];
|
||||
sCountAverage[i] = (sCountAverage[i]*sCurFrameIndex + sCounter[i]) / (sCurFrameIndex+1);
|
||||
sCallHistory[hidx][i] = sCalls[i];
|
||||
sCallAverage[i] = (sCallAverage[i]*sCurFrameIndex + sCalls[i]) / (sCurFrameIndex+1);
|
||||
}
|
||||
sLastFrameIndex = sCurFrameIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
||||
{
|
||||
sCountAverage[i] = 0;
|
||||
sCallAverage[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sCurFrameIndex++;
|
||||
|
||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
||||
{
|
||||
sCounter[i] = 0;
|
||||
sCalls[i] = 0;
|
||||
}
|
||||
sCurDepth = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Important note: These implementations must be FAST!
|
||||
//
|
||||
|
||||
// shift off lower 8 bits for lower resolution but longer term timing
|
||||
// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
|
||||
|
||||
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
||||
// On windows these use QueryPerformanceCounter, which is arguably fine and also works on amd architectures.
|
||||
U32 LLFastTimer::getCPUClockCount32()
|
||||
{
|
||||
return get_clock_count() >> 8;
|
||||
}
|
||||
|
||||
U64 LLFastTimer::getCPUClockCount64()
|
||||
{
|
||||
return get_clock_count();
|
||||
}
|
||||
@@ -1,306 +1,35 @@
|
||||
/**
|
||||
/**
|
||||
* @file llfasttimer.h
|
||||
* @brief Declaration of a fast timer.
|
||||
* @brief Inline implementations of fast timers.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLFASTTIMER_H
|
||||
#define LL_LLFASTTIMER_H
|
||||
#ifndef LL_FASTTIMER_H
|
||||
#define LL_FASTTIMER_H
|
||||
|
||||
#define FAST_TIMER_ON 1
|
||||
|
||||
|
||||
class LL_COMMON_API LLFastTimer
|
||||
{
|
||||
public:
|
||||
enum EFastTimerType
|
||||
{
|
||||
// high level
|
||||
FTM_FRAME,
|
||||
FTM_UPDATE,
|
||||
FTM_RENDER,
|
||||
FTM_SWAP,
|
||||
FTM_CLIENT_COPY,
|
||||
FTM_IDLE,
|
||||
FTM_SLEEP,
|
||||
|
||||
// general timers
|
||||
FT_STRING_FORMAT,
|
||||
|
||||
// common messaging components
|
||||
FTM_PUMP,
|
||||
FTM_CURL,
|
||||
FTM_PUMPIO,
|
||||
|
||||
// common simulation components
|
||||
FTM_UPDATE_ANIMATION,
|
||||
FTM_UPDATE_TERRAIN,
|
||||
FTM_UPDATE_PRIMITIVES,
|
||||
FTM_UPDATE_PARTICLES,
|
||||
FTM_SIMULATE_PARTICLES,
|
||||
FTM_UPDATE_SKY,
|
||||
FTM_UPDATE_TEXTURES,
|
||||
FTM_UPDATE_WLPARAM,
|
||||
FTM_UPDATE_WATER,
|
||||
FTM_UPDATE_CLOUDS,
|
||||
FTM_UPDATE_GRASS,
|
||||
FTM_UPDATE_TREE,
|
||||
FTM_UPDATE_AVATAR,
|
||||
FTM_UPDATE_RIGGED_VOLUME,
|
||||
FTM_SKIN_RIGGED,
|
||||
FTM_RIGGED_OCTREE,
|
||||
|
||||
// common render components
|
||||
FTM_SHADOW_GEOMETRY,
|
||||
FTM_SHADOW_RENDER,
|
||||
FTM_SHADOW_TERRAIN,
|
||||
FTM_SHADOW_AVATAR,
|
||||
FTM_SHADOW_SIMPLE,
|
||||
FTM_SHADOW_ALPHA,
|
||||
FTM_SHADOW_TREE,
|
||||
|
||||
FTM_RENDER_GEOMETRY,
|
||||
FTM_RENDER_TERRAIN,
|
||||
FTM_RENDER_SIMPLE,
|
||||
FTM_RENDER_FULLBRIGHT,
|
||||
FTM_RENDER_GLOW,
|
||||
FTM_RENDER_GRASS,
|
||||
FTM_RENDER_INVISIBLE,
|
||||
FTM_RENDER_SHINY,
|
||||
FTM_RENDER_BUMP,
|
||||
FTM_RENDER_TREES,
|
||||
FTM_RENDER_CHARACTERS,
|
||||
FTM_RENDER_OCCLUSION,
|
||||
FTM_RENDER_ALPHA,
|
||||
FTM_RENDER_CLOUDS,
|
||||
FTM_RENDER_HUD,
|
||||
FTM_RENDER_PARTICLES,
|
||||
FTM_RENDER_WATER,
|
||||
FTM_RENDER_WL_SKY,
|
||||
FTM_RENDER_FAKE_VBO_UPDATE,
|
||||
FTM_RENDER_TIMER,
|
||||
FTM_RENDER_UI,
|
||||
FTM_RENDER_BLOOM,
|
||||
FTM_RENDER_BLOOM_FBO,
|
||||
FTM_RENDER_FONTS,
|
||||
|
||||
// deferred rendering
|
||||
FTM_RENDER_DEFERRED,
|
||||
FTM_BIND_DEFERRED,
|
||||
FTM_SUN_SHADOW,
|
||||
FTM_SOFTEN_SHADOW,
|
||||
FTM_EDGE_DETECTION,
|
||||
FTM_GI_TRACE,
|
||||
FTM_GI_GATHER,
|
||||
FTM_ATMOSPHERICS,
|
||||
FTM_LOCAL_LIGHTS,
|
||||
FTM_FULLSCREEN_LIGHTS,
|
||||
FTM_PROJECTORS,
|
||||
FTM_POST,
|
||||
|
||||
FTM_VISIBLE_CLOUD,
|
||||
|
||||
// newview specific
|
||||
FTM_MESSAGES,
|
||||
FTM_MOUSEHANDLER,
|
||||
FTM_KEYHANDLER,
|
||||
FTM_REBUILD,
|
||||
FTM_STATESORT,
|
||||
FTM_STATESORT_DRAWABLE,
|
||||
FTM_STATESORT_POSTSORT,
|
||||
FTM_MESH_UPDATE,
|
||||
FTM_MESH_LOCK1,
|
||||
FTM_MESH_LOCK2,
|
||||
FTM_LOAD_MESH_LOD,
|
||||
FTM_REBUILD_VBO,
|
||||
FTM_REBUILD_VOLUME_VB,
|
||||
FTM_FACE_GET_GEOM,
|
||||
FTM_FACE_GEOM_POSITION,
|
||||
FTM_FACE_GEOM_NORMAL,
|
||||
FTM_FACE_GEOM_TEXTURE,
|
||||
FTM_FACE_GEOM_COLOR,
|
||||
FTM_FACE_GEOM_EMISSIVE,
|
||||
FTM_FACE_GEOM_WEIGHTS,
|
||||
FTM_FACE_GEOM_BINORMAL,
|
||||
FTM_FACE_GEOM_INDEX,
|
||||
FTM_REBUILD_BRIDGE_VB,
|
||||
FTM_REBUILD_HUD_VB,
|
||||
FTM_REBUILD_TERRAIN_VB,
|
||||
FTM_REBUILD_WATER_VB,
|
||||
FTM_REBUILD_TREE_VB,
|
||||
FTM_REBUILD_PARTICLE_VB,
|
||||
FTM_REBUILD_CLOUD_VB,
|
||||
FTM_REBUILD_GRASS_VB,
|
||||
FTM_REBUILD_NONE_VB,
|
||||
FTM_REBUILD_OCCLUSION_VB,
|
||||
FTM_POOLS,
|
||||
FTM_POOLRENDER,
|
||||
FTM_IDLE_CB,
|
||||
FTM_WORLD_UPDATE,
|
||||
FTM_UPDATE_MOVE,
|
||||
FTM_OCTREE_BALANCE,
|
||||
FTM_UPDATE_LIGHTS,
|
||||
FTM_CULL,
|
||||
FTM_CULL_REBOUND,
|
||||
FTM_FRUSTUM_CULL,
|
||||
FTM_GEO_UPDATE,
|
||||
FTM_GEO_RESERVE,
|
||||
FTM_GEO_LIGHT,
|
||||
FTM_GEO_SHADOW,
|
||||
FTM_GEO_SKY,
|
||||
FTM_GEN_VOLUME,
|
||||
FTM_GEN_TRIANGLES,
|
||||
FTM_GEN_FLEX,
|
||||
FTM_AUDIO_UPDATE,
|
||||
FTM_RESET_DRAWORDER,
|
||||
FTM_OBJECTLIST_UPDATE,
|
||||
FTM_AVATAR_UPDATE,
|
||||
FTM_JOINT_UPDATE,
|
||||
FTM_ATTACHMENT_UPDATE,
|
||||
FTM_LOD_UPDATE,
|
||||
FTM_REGION_UPDATE,
|
||||
FTM_CLEANUP,
|
||||
FTM_NETWORK,
|
||||
FTM_IDLE_NETWORK,
|
||||
FTM_CREATE_OBJECT,
|
||||
FTM_LOAD_AVATAR,
|
||||
FTM_PROCESS_MESSAGES,
|
||||
FTM_PROCESS_OBJECTS,
|
||||
FTM_PROCESS_IMAGES,
|
||||
FTM_IMAGE_UPDATE,
|
||||
FTM_IMAGE_CREATE,
|
||||
FTM_IMAGE_DECODE,
|
||||
FTM_IMAGE_READBACK,
|
||||
FTM_IMAGE_MARK_DIRTY,
|
||||
FTM_PIPELINE,
|
||||
FTM_VFILE_WAIT,
|
||||
FTM_FLEXIBLE_UPDATE,
|
||||
FTM_OCCLUSION_READBACK,
|
||||
FTM_HUD_EFFECTS,
|
||||
FTM_HUD_UPDATE,
|
||||
FTM_INVENTORY,
|
||||
FTM_AUTO_SELECT,
|
||||
FTM_ARRANGE,
|
||||
FTM_FILTER,
|
||||
FTM_REFRESH,
|
||||
FTM_SORT,
|
||||
FTM_PICK,
|
||||
FTM_STATEMACHINE,
|
||||
|
||||
// Temp
|
||||
FTM_TEMP1,
|
||||
FTM_TEMP2,
|
||||
FTM_TEMP3,
|
||||
FTM_TEMP4,
|
||||
FTM_TEMP5,
|
||||
FTM_TEMP6,
|
||||
FTM_TEMP7,
|
||||
FTM_TEMP8,
|
||||
FTM_TEMP9,
|
||||
FTM_TEMP10,
|
||||
FTM_TEMP11,
|
||||
FTM_TEMP12,
|
||||
FTM_TEMP13,
|
||||
FTM_TEMP14,
|
||||
FTM_TEMP15,
|
||||
|
||||
FTM_OTHER, // Special, used by display code
|
||||
|
||||
FTM_NUM_TYPES
|
||||
};
|
||||
enum { FTM_HISTORY_NUM = 60 };
|
||||
enum { FTM_MAX_DEPTH = 64 };
|
||||
|
||||
public:
|
||||
static EFastTimerType sCurType;
|
||||
|
||||
LLFastTimer(EFastTimerType type)
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
mType = type;
|
||||
sCurType = type;
|
||||
// These don't get counted, because they use CPU clockticks
|
||||
//gTimerBins[gCurTimerBin]++;
|
||||
//LLTimer::sNumTimerCalls++;
|
||||
|
||||
U64 cpu_clocks = getCPUClockCount32();
|
||||
|
||||
sStart[sCurDepth] = cpu_clocks;
|
||||
sCurDepth++;
|
||||
#endif
|
||||
};
|
||||
~LLFastTimer()
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
U64 end,delta;
|
||||
int i;
|
||||
|
||||
// These don't get counted, because they use CPU clockticks
|
||||
//gTimerBins[gCurTimerBin]++;
|
||||
//LLTimer::sNumTimerCalls++;
|
||||
end = getCPUClockCount32();
|
||||
|
||||
sCurDepth--;
|
||||
delta = end - sStart[sCurDepth];
|
||||
sCounter[mType] += delta;
|
||||
sCalls[mType]++;
|
||||
// Subtract delta from parents
|
||||
for (i=0; i<sCurDepth; i++)
|
||||
sStart[i] += delta;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void reset();
|
||||
static U64 countsPerSecond();
|
||||
|
||||
public:
|
||||
static int sCurDepth;
|
||||
static U64 sStart[FTM_MAX_DEPTH];
|
||||
static U64 sCounter[FTM_NUM_TYPES];
|
||||
static U64 sCalls[FTM_NUM_TYPES];
|
||||
static U64 sCountAverage[FTM_NUM_TYPES];
|
||||
static U64 sCallAverage[FTM_NUM_TYPES];
|
||||
static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
|
||||
static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
|
||||
|
||||
static int sPauseHistory;
|
||||
static int sResetHistory;
|
||||
|
||||
static U32 getCPUClockCount32();
|
||||
static U64 getCPUClockCount64();
|
||||
|
||||
static U64 sClockResolution;
|
||||
static S32 sCurFrameIndex;
|
||||
static S32 sLastFrameIndex;
|
||||
|
||||
EFastTimerType mType;
|
||||
};
|
||||
// Implementation of getCPUClockCount32() and getCPUClockCount64 are now in llfastertimer_class.cpp.
|
||||
|
||||
// pull in the actual class definition
|
||||
#include "llfasttimer_class.h"
|
||||
|
||||
#endif // LL_LLFASTTIMER_H
|
||||
|
||||
803
indra/llcommon/llfasttimer_class.cpp
Normal file
803
indra/llcommon/llfasttimer_class.cpp
Normal file
@@ -0,0 +1,803 @@
|
||||
/**
|
||||
* @file llfasttimer_class.cpp
|
||||
* @brief Implementation of the fast timer.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llfasttimer.h"
|
||||
|
||||
#include "llmemory.h"
|
||||
#include "llprocessor.h"
|
||||
#include "llsingleton.h"
|
||||
#include "lltreeiterators.h"
|
||||
#include "llsdserialize.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include "lltimer.h"
|
||||
#elif LL_LINUX || LL_SOLARIS
|
||||
#include <sys/time.h>
|
||||
#include <sched.h>
|
||||
#include "lltimer.h"
|
||||
#elif LL_DARWIN
|
||||
#include <sys/time.h>
|
||||
#include "lltimer.h" // get_clock_count()
|
||||
#else
|
||||
#error "architecture not supported"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// statics
|
||||
|
||||
S32 LLFastTimer::sCurFrameIndex = -1;
|
||||
S32 LLFastTimer::sLastFrameIndex = -1;
|
||||
U64 LLFastTimer::sLastFrameTime = LLFastTimer::getCPUClockCount64();
|
||||
bool LLFastTimer::sPauseHistory = 0;
|
||||
bool LLFastTimer::sResetHistory = 0;
|
||||
LLFastTimer::CurTimerData LLFastTimer::sCurTimerData;
|
||||
BOOL LLFastTimer::sLog = FALSE;
|
||||
std::string LLFastTimer::sLogName = "";
|
||||
BOOL LLFastTimer::sMetricLog = FALSE;
|
||||
LLMutex* LLFastTimer::sLogLock = NULL;
|
||||
std::queue<LLSD> LLFastTimer::sLogQueue;
|
||||
|
||||
#define USE_RDTSC 0
|
||||
|
||||
#if LL_LINUX || LL_SOLARIS
|
||||
U64 LLFastTimer::sClockResolution = 1000000000; // Nanosecond resolution
|
||||
#else
|
||||
U64 LLFastTimer::sClockResolution = 1000000; // Microsecond resolution
|
||||
#endif
|
||||
|
||||
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
|
||||
U64 LLFastTimer::sTimerCycles = 0;
|
||||
U32 LLFastTimer::sTimerCalls = 0;
|
||||
|
||||
|
||||
// FIXME: move these declarations to the relevant modules
|
||||
|
||||
// helper functions
|
||||
typedef LLTreeDFSPostIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_bottom_up_iterator_t;
|
||||
|
||||
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& id)
|
||||
{
|
||||
return timer_tree_bottom_up_iterator_t(&id,
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||
}
|
||||
|
||||
static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
|
||||
{
|
||||
return timer_tree_bottom_up_iterator_t();
|
||||
}
|
||||
|
||||
typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_dfs_iterator_t;
|
||||
|
||||
|
||||
static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
|
||||
{
|
||||
return timer_tree_dfs_iterator_t(&id,
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||
}
|
||||
|
||||
static timer_tree_dfs_iterator_t end_timer_tree()
|
||||
{
|
||||
return timer_tree_dfs_iterator_t();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// factory class that creates NamedTimers via static DeclareTimer objects
|
||||
class NamedTimerFactory : public LLSingleton<NamedTimerFactory>
|
||||
{
|
||||
public:
|
||||
NamedTimerFactory()
|
||||
: mActiveTimerRoot(NULL),
|
||||
mTimerRoot(NULL),
|
||||
mAppTimer(NULL),
|
||||
mRootFrameState(NULL)
|
||||
{}
|
||||
|
||||
/*virtual */ void initSingleton()
|
||||
{
|
||||
mTimerRoot = new LLFastTimer::NamedTimer("root");
|
||||
|
||||
mActiveTimerRoot = new LLFastTimer::NamedTimer("Frame");
|
||||
mActiveTimerRoot->setCollapsed(false);
|
||||
|
||||
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
|
||||
mRootFrameState->mParent = &mTimerRoot->getFrameState();
|
||||
mActiveTimerRoot->setParent(mTimerRoot);
|
||||
|
||||
mAppTimer = new LLFastTimer(mRootFrameState);
|
||||
}
|
||||
|
||||
~NamedTimerFactory()
|
||||
{
|
||||
std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
|
||||
|
||||
delete mAppTimer;
|
||||
delete mActiveTimerRoot;
|
||||
delete mTimerRoot;
|
||||
delete mRootFrameState;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer& createNamedTimer(const std::string& name)
|
||||
{
|
||||
timer_map_t::iterator found_it = mTimers.find(name);
|
||||
if (found_it != mTimers.end())
|
||||
{
|
||||
return *found_it->second;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
|
||||
timer->setParent(mTimerRoot);
|
||||
mTimers.insert(std::make_pair(name, timer));
|
||||
|
||||
return *timer;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer* getTimerByName(const std::string& name)
|
||||
{
|
||||
timer_map_t::iterator found_it = mTimers.find(name);
|
||||
if (found_it != mTimers.end())
|
||||
{
|
||||
return found_it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer* getActiveRootTimer() { return mActiveTimerRoot; }
|
||||
LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
|
||||
const LLFastTimer* getAppTimer() { return mAppTimer; }
|
||||
LLFastTimer::FrameState& getRootFrameState() { return *mRootFrameState; }
|
||||
|
||||
typedef std::map<std::string, LLFastTimer::NamedTimer*> timer_map_t;
|
||||
timer_map_t::iterator beginTimers() { return mTimers.begin(); }
|
||||
timer_map_t::iterator endTimers() { return mTimers.end(); }
|
||||
S32 timerCount() { return mTimers.size(); }
|
||||
|
||||
private:
|
||||
timer_map_t mTimers;
|
||||
|
||||
LLFastTimer::NamedTimer* mActiveTimerRoot;
|
||||
LLFastTimer::NamedTimer* mTimerRoot;
|
||||
LLFastTimer* mAppTimer;
|
||||
LLFastTimer::FrameState* mRootFrameState;
|
||||
};
|
||||
|
||||
void update_cached_pointers_if_changed()
|
||||
{
|
||||
// detect when elements have moved and update cached pointers
|
||||
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
|
||||
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
|
||||
{
|
||||
LLFastTimer::DeclareTimer::updateCachedPointers();
|
||||
}
|
||||
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
|
||||
}
|
||||
|
||||
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
|
||||
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
|
||||
{
|
||||
mTimer.setCollapsed(!open);
|
||||
mFrameState = &mTimer.getFrameState();
|
||||
update_cached_pointers_if_changed();
|
||||
}
|
||||
|
||||
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
|
||||
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
|
||||
{
|
||||
mFrameState = &mTimer.getFrameState();
|
||||
update_cached_pointers_if_changed();
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::DeclareTimer::updateCachedPointers()
|
||||
{
|
||||
// propagate frame state pointers to timer declarations
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
// update cached pointer
|
||||
it->mFrameState = &it->mTimer.getFrameState();
|
||||
}
|
||||
|
||||
// also update frame states of timers on stack
|
||||
LLFastTimer* cur_timerp = LLFastTimer::sCurTimerData.mCurTimer;
|
||||
while(cur_timerp->mLastTimerData.mCurTimer != cur_timerp)
|
||||
{
|
||||
cur_timerp->mFrameState = &cur_timerp->mFrameState->mTimer->getFrameState();
|
||||
cur_timerp = cur_timerp->mLastTimerData.mCurTimer;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
#if (LL_DARWIN || LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
{
|
||||
return sClockResolution >> 8;
|
||||
}
|
||||
#else // windows or x86-mac or x86-linux or x86-solaris
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
{
|
||||
#if USE_RDTSC || !LL_WINDOWS
|
||||
//getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz
|
||||
static U64 sCPUClockFrequency = U64(LLProcessorInfo().getCPUFrequency()*1000000.0);
|
||||
|
||||
// we drop the low-order byte in our timers, so report a lower frequency
|
||||
#else
|
||||
// If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
|
||||
// Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
|
||||
// since that would change displayed MHz stats for CPUs
|
||||
static bool firstcall = true;
|
||||
static U64 sCPUClockFrequency;
|
||||
if (firstcall)
|
||||
{
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
|
||||
firstcall = false;
|
||||
}
|
||||
#endif
|
||||
return sCPUClockFrequency >> 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
|
||||
: mActiveCount(0),
|
||||
mCalls(0),
|
||||
mSelfTimeCounter(0),
|
||||
mParent(NULL),
|
||||
mLastCaller(NULL),
|
||||
mMoveUpTree(false),
|
||||
mTimer(timerp)
|
||||
{}
|
||||
|
||||
|
||||
LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
|
||||
: mName(name),
|
||||
mCollapsed(true),
|
||||
mParent(NULL),
|
||||
mTotalTimeCounter(0),
|
||||
mCountAverage(0),
|
||||
mCallAverage(0),
|
||||
mNeedsSorting(false)
|
||||
{
|
||||
info_list_t& frame_state_list = getFrameStateList();
|
||||
mFrameStateIndex = frame_state_list.size();
|
||||
getFrameStateList().push_back(FrameState(this));
|
||||
|
||||
mCountHistory = new U32[HISTORY_NUM];
|
||||
memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
mCallHistory = new U32[HISTORY_NUM];
|
||||
memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer::~NamedTimer()
|
||||
{
|
||||
delete[] mCountHistory;
|
||||
delete[] mCallHistory;
|
||||
}
|
||||
|
||||
std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
|
||||
{
|
||||
F64 ms_multiplier = 1000.0 / (F64)LLFastTimer::countsPerSecond();
|
||||
if (history_idx < 0)
|
||||
{
|
||||
// by default, show average number of call
|
||||
return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getCountAverage() * ms_multiplier), (S32)getCallAverage());
|
||||
}
|
||||
else
|
||||
{
|
||||
return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
|
||||
}
|
||||
}
|
||||
|
||||
void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
|
||||
{
|
||||
llassert_always(parent != this);
|
||||
llassert_always(parent != NULL);
|
||||
|
||||
if (mParent)
|
||||
{
|
||||
// subtract our accumulated from previous parent
|
||||
for (S32 i = 0; i < HISTORY_NUM; i++)
|
||||
{
|
||||
mParent->mCountHistory[i] -= mCountHistory[i];
|
||||
}
|
||||
|
||||
// subtract average timing from previous parent
|
||||
mParent->mCountAverage -= mCountAverage;
|
||||
|
||||
std::vector<NamedTimer*>& children = mParent->getChildren();
|
||||
std::vector<NamedTimer*>::iterator found_it = std::find(children.begin(), children.end(), this);
|
||||
if (found_it != children.end())
|
||||
{
|
||||
children.erase(found_it);
|
||||
}
|
||||
}
|
||||
|
||||
mParent = parent;
|
||||
if (parent)
|
||||
{
|
||||
getFrameState().mParent = &parent->getFrameState();
|
||||
parent->getChildren().push_back(this);
|
||||
parent->mNeedsSorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
S32 LLFastTimer::NamedTimer::getDepth()
|
||||
{
|
||||
S32 depth = 0;
|
||||
NamedTimer* timerp = mParent;
|
||||
while(timerp)
|
||||
{
|
||||
depth++;
|
||||
timerp = timerp->mParent;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::NamedTimer::processTimes()
|
||||
{
|
||||
if (sCurFrameIndex < 0) return;
|
||||
|
||||
buildHierarchy();
|
||||
accumulateTimings();
|
||||
}
|
||||
|
||||
// sort timer info structs by depth first traversal order
|
||||
struct SortTimersDFS
|
||||
{
|
||||
bool operator()(const LLFastTimer::FrameState& i1, const LLFastTimer::FrameState& i2)
|
||||
{
|
||||
return i1.mTimer->getFrameStateIndex() < i2.mTimer->getFrameStateIndex();
|
||||
}
|
||||
};
|
||||
|
||||
// sort child timers by name
|
||||
struct SortTimerByName
|
||||
{
|
||||
bool operator()(const LLFastTimer::NamedTimer* i1, const LLFastTimer::NamedTimer* i2)
|
||||
{
|
||||
return i1->getName() < i2->getName();
|
||||
}
|
||||
};
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::buildHierarchy()
|
||||
{
|
||||
if (sCurFrameIndex < 0 ) return;
|
||||
|
||||
// set up initial tree
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||
|
||||
// bootstrap tree construction by attaching to last timer to be on stack
|
||||
// when this timer was called
|
||||
if (timer.getFrameState().mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
|
||||
{
|
||||
timer.setParent(timer.getFrameState().mLastCaller->mTimer);
|
||||
// no need to push up tree on first use, flag can be set spuriously
|
||||
timer.getFrameState().mMoveUpTree = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bump timers up tree if they've been flagged as being in the wrong place
|
||||
// do this in a bottom up order to promote descendants first before promoting ancestors
|
||||
// this preserves partial order derived from current frame's observations
|
||||
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree_bottom_up();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = *it;
|
||||
// skip root timer
|
||||
if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||
|
||||
if (timerp->getFrameState().mMoveUpTree)
|
||||
{
|
||||
// since ancestors have already been visited, reparenting won't affect tree traversal
|
||||
//step up tree, bringing our descendants with us
|
||||
//llinfos << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
|
||||
// " to child of " << timerp->getParent()->getParent()->getName() << llendl;
|
||||
timerp->setParent(timerp->getParent()->getParent());
|
||||
timerp->getFrameState().mMoveUpTree = false;
|
||||
|
||||
// don't bubble up any ancestors until descendants are done bubbling up
|
||||
it.skipAncestors();
|
||||
}
|
||||
}
|
||||
|
||||
// sort timers by time last called, so call graph makes sense
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
if (timerp->mNeedsSorting)
|
||||
{
|
||||
std::sort(timerp->getChildren().begin(), timerp->getChildren().end(), SortTimerByName());
|
||||
}
|
||||
timerp->mNeedsSorting = false;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::accumulateTimings()
|
||||
{
|
||||
U32 cur_time = getCPUClockCount32();
|
||||
|
||||
// walk up stack of active timers and accumulate current time while leaving timing structures active
|
||||
LLFastTimer* cur_timer = sCurTimerData.mCurTimer;
|
||||
// root defined by parent pointing to self
|
||||
CurTimerData* cur_data = &sCurTimerData;
|
||||
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||
{
|
||||
U32 cumulative_time_delta = cur_time - cur_timer->mStartTime;
|
||||
U32 self_time_delta = cumulative_time_delta - cur_data->mChildTime;
|
||||
cur_data->mChildTime = 0;
|
||||
cur_timer->mFrameState->mSelfTimeCounter += self_time_delta;
|
||||
cur_timer->mStartTime = cur_time;
|
||||
|
||||
cur_data = &cur_timer->mLastTimerData;
|
||||
cur_data->mChildTime += cumulative_time_delta;
|
||||
|
||||
cur_timer = cur_timer->mLastTimerData.mCurTimer;
|
||||
}
|
||||
|
||||
// traverse tree in DFS post order, or bottom up
|
||||
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getActiveRootTimer());
|
||||
it != end_timer_tree_bottom_up();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
timerp->mTotalTimeCounter = timerp->getFrameState().mSelfTimeCounter;
|
||||
for (child_const_iter child_it = timerp->beginChildren(); child_it != timerp->endChildren(); ++child_it)
|
||||
{
|
||||
timerp->mTotalTimeCounter += (*child_it)->mTotalTimeCounter;
|
||||
}
|
||||
|
||||
S32 cur_frame = sCurFrameIndex;
|
||||
if (cur_frame >= 0)
|
||||
{
|
||||
// update timer history
|
||||
int hidx = cur_frame % HISTORY_NUM;
|
||||
|
||||
timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
|
||||
timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
|
||||
timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
|
||||
timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::NamedTimer::resetFrame()
|
||||
{
|
||||
if (sLog)
|
||||
{ //output current frame counts to performance log
|
||||
|
||||
static S32 call_count = 0;
|
||||
if (call_count % 100 == 0)
|
||||
{
|
||||
llinfos << "countsPerSecond (32 bit): " << countsPerSecond() << llendl;
|
||||
llinfos << "get_clock_count (64 bit): " << get_clock_count() << llendl;
|
||||
llinfos << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << llendl;
|
||||
llinfos << "getCPUClockCount32() " << getCPUClockCount32() << llendl;
|
||||
llinfos << "getCPUClockCount64() " << getCPUClockCount64() << llendl;
|
||||
llinfos << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << llendl;
|
||||
}
|
||||
call_count++;
|
||||
|
||||
F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
|
||||
|
||||
F64 total_time = 0;
|
||||
LLSD sd;
|
||||
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
FrameState& info = timer.getFrameState();
|
||||
sd[timer.getName()]["Time"] = (LLSD::Real) (info.mSelfTimeCounter*iclock_freq);
|
||||
sd[timer.getName()]["Calls"] = (LLSD::Integer) info.mCalls;
|
||||
|
||||
// computing total time here because getting the root timer's getCountHistory
|
||||
// doesn't work correctly on the first frame
|
||||
total_time = total_time + info.mSelfTimeCounter * iclock_freq;
|
||||
}
|
||||
}
|
||||
|
||||
sd["Total"]["Time"] = (LLSD::Real) total_time;
|
||||
sd["Total"]["Calls"] = (LLSD::Integer) 1;
|
||||
|
||||
{
|
||||
LLMutexLock lock(sLogLock);
|
||||
sLogQueue.push(sd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tag timers by position in depth first traversal of tree
|
||||
S32 index = 0;
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
|
||||
timerp->mFrameStateIndex = index;
|
||||
index++;
|
||||
|
||||
llassert_always(timerp->mFrameStateIndex < (S32)getFrameStateList().size());
|
||||
}
|
||||
|
||||
// sort timers by DFS traversal order to improve cache coherency
|
||||
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
|
||||
|
||||
// update pointers into framestatelist now that we've sorted it
|
||||
DeclareTimer::updateCachedPointers();
|
||||
|
||||
// reset for next frame
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
|
||||
FrameState& info = timer.getFrameState();
|
||||
info.mSelfTimeCounter = 0;
|
||||
info.mCalls = 0;
|
||||
info.mLastCaller = NULL;
|
||||
info.mMoveUpTree = false;
|
||||
// update parent pointer in timer state struct
|
||||
if (timer.mParent)
|
||||
{
|
||||
info.mParent = &timer.mParent->getFrameState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sTimerCycles = 0;
|
||||
//sTimerCalls = 0;
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::reset()
|
||||
{
|
||||
resetFrame(); // reset frame data
|
||||
|
||||
// walk up stack of active timers and reset start times to current time
|
||||
// effectively zeroing out any accumulated time
|
||||
U32 cur_time = getCPUClockCount32();
|
||||
|
||||
// root defined by parent pointing to self
|
||||
CurTimerData* cur_data = &sCurTimerData;
|
||||
LLFastTimer* cur_timer = cur_data->mCurTimer;
|
||||
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||
{
|
||||
cur_timer->mStartTime = cur_time;
|
||||
cur_data->mChildTime = 0;
|
||||
|
||||
cur_data = &cur_timer->mLastTimerData;
|
||||
cur_timer = cur_data->mCurTimer;
|
||||
}
|
||||
|
||||
// reset all history
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
if (&timer != NamedTimerFactory::instance().getRootTimer())
|
||||
{
|
||||
timer.setParent(NamedTimerFactory::instance().getRootTimer());
|
||||
}
|
||||
|
||||
timer.mCountAverage = 0;
|
||||
timer.mCallAverage = 0;
|
||||
memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
}
|
||||
}
|
||||
|
||||
sLastFrameIndex = 0;
|
||||
sCurFrameIndex = 0;
|
||||
}
|
||||
|
||||
//static
|
||||
LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
|
||||
{
|
||||
if (!sTimerInfos)
|
||||
{
|
||||
sTimerInfos = new info_list_t();
|
||||
}
|
||||
return *sTimerInfos;
|
||||
}
|
||||
|
||||
|
||||
U32 LLFastTimer::NamedTimer::getHistoricalCount(S32 history_index) const
|
||||
{
|
||||
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||
return mCountHistory[history_idx];
|
||||
}
|
||||
|
||||
U32 LLFastTimer::NamedTimer::getHistoricalCalls(S32 history_index ) const
|
||||
{
|
||||
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||
return mCallHistory[history_idx];
|
||||
}
|
||||
|
||||
LLFastTimer::FrameState& LLFastTimer::NamedTimer::getFrameState() const
|
||||
{
|
||||
llassert_always(mFrameStateIndex >= 0);
|
||||
if (this == NamedTimerFactory::instance().getActiveRootTimer())
|
||||
{
|
||||
return NamedTimerFactory::instance().getRootFrameState();
|
||||
}
|
||||
return getFrameStateList()[mFrameStateIndex];
|
||||
}
|
||||
|
||||
// static
|
||||
LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
|
||||
{
|
||||
return *NamedTimerFactory::instance().getActiveRootTimer();
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::beginChildren()
|
||||
{
|
||||
return mChildren.begin();
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::endChildren()
|
||||
{
|
||||
return mChildren.end();
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>& LLFastTimer::NamedTimer::getChildren()
|
||||
{
|
||||
return mChildren;
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::nextFrame()
|
||||
{
|
||||
countsPerSecond(); // good place to calculate clock frequency
|
||||
U64 frame_time = getCPUClockCount64();
|
||||
if ((frame_time - sLastFrameTime) >> 8 > 0xffffffff)
|
||||
{
|
||||
llinfos << "Slow frame, fast timers inaccurate" << llendl;
|
||||
}
|
||||
|
||||
if (!sPauseHistory)
|
||||
{
|
||||
NamedTimer::processTimes();
|
||||
sLastFrameIndex = sCurFrameIndex++;
|
||||
}
|
||||
|
||||
// get ready for next frame
|
||||
NamedTimer::resetFrame();
|
||||
sLastFrameTime = frame_time;
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::dumpCurTimes()
|
||||
{
|
||||
// accumulate timings, etc.
|
||||
NamedTimer::processTimes();
|
||||
|
||||
F64 clock_freq = (F64)countsPerSecond();
|
||||
F64 iclock_freq = 1000.0 / clock_freq; // clock_ticks -> milliseconds
|
||||
|
||||
// walk over timers in depth order and output timings
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
|
||||
// Don't bother with really brief times, keep output concise
|
||||
if (total_time_ms < 0.1) continue;
|
||||
|
||||
std::ostringstream out_str;
|
||||
for (S32 i = 0; i < timerp->getDepth(); i++)
|
||||
{
|
||||
out_str << "\t";
|
||||
}
|
||||
|
||||
|
||||
out_str << timerp->getName() << " "
|
||||
<< std::setprecision(3) << total_time_ms << " ms, "
|
||||
<< timerp->getHistoricalCalls(0) << " calls";
|
||||
|
||||
llinfos << out_str.str() << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::reset()
|
||||
{
|
||||
NamedTimer::reset();
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
void LLFastTimer::writeLog(std::ostream& os)
|
||||
{
|
||||
while (!sLogQueue.empty())
|
||||
{
|
||||
LLSD& sd = sLogQueue.front();
|
||||
LLSDSerialize::toXML(sd, os);
|
||||
LLMutexLock lock(sLogLock);
|
||||
sLogQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
|
||||
{
|
||||
return NamedTimerFactory::instance().getTimerByName(name);
|
||||
}
|
||||
|
||||
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
|
||||
: mFrameState(state)
|
||||
{
|
||||
U32 start_time = getCPUClockCount32();
|
||||
mStartTime = start_time;
|
||||
mFrameState->mActiveCount++;
|
||||
LLFastTimer::sCurTimerData.mCurTimer = this;
|
||||
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
|
||||
LLFastTimer::sCurTimerData.mChildTime = 0;
|
||||
mLastTimerData = LLFastTimer::sCurTimerData;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Important note: These implementations must be FAST!
|
||||
//
|
||||
|
||||
|
||||
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
||||
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
|
||||
U32 LLFastTimer::getCPUClockCount32()
|
||||
{
|
||||
return (U32)(get_clock_count()>>8);
|
||||
}
|
||||
|
||||
U64 LLFastTimer::getCPUClockCount64()
|
||||
{
|
||||
return get_clock_count();
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
|
||||
#else
|
||||
std::string LLFastTimer::sClockType = "gettimeofday";
|
||||
#endif
|
||||
|
||||
277
indra/llcommon/llfasttimer_class.h
Normal file
277
indra/llcommon/llfasttimer_class.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* @file llfasttimer_class.h
|
||||
* @brief Declaration of a fast timer.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_FASTTIMER_CLASS_H
|
||||
#define LL_FASTTIMER_CLASS_H
|
||||
|
||||
#include "llinstancetracker.h"
|
||||
|
||||
#define FAST_TIMER_ON 1
|
||||
#define TIME_FAST_TIMERS 0
|
||||
#define DEBUG_FAST_TIMER_THREADS 1
|
||||
|
||||
class LLMutex;
|
||||
|
||||
#include <queue>
|
||||
#include "llsd.h"
|
||||
|
||||
LL_COMMON_API void assert_main_thread();
|
||||
|
||||
class LL_COMMON_API LLFastTimer
|
||||
{
|
||||
public:
|
||||
class NamedTimer;
|
||||
|
||||
struct LL_COMMON_API FrameState
|
||||
{
|
||||
FrameState(NamedTimer* timerp);
|
||||
|
||||
U32 mSelfTimeCounter;
|
||||
U32 mCalls;
|
||||
FrameState* mParent; // info for caller timer
|
||||
FrameState* mLastCaller; // used to bootstrap tree construction
|
||||
NamedTimer* mTimer;
|
||||
U16 mActiveCount; // number of timers with this ID active on stack
|
||||
bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
|
||||
};
|
||||
|
||||
// stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
|
||||
class LL_COMMON_API NamedTimer
|
||||
: public LLInstanceTracker<NamedTimer>
|
||||
{
|
||||
friend class DeclareTimer;
|
||||
public:
|
||||
~NamedTimer();
|
||||
|
||||
enum { HISTORY_NUM = 300 };
|
||||
|
||||
const std::string& getName() const { return mName; }
|
||||
NamedTimer* getParent() const { return mParent; }
|
||||
void setParent(NamedTimer* parent);
|
||||
S32 getDepth();
|
||||
std::string getToolTip(S32 history_index = -1);
|
||||
|
||||
typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
|
||||
child_const_iter beginChildren();
|
||||
child_const_iter endChildren();
|
||||
std::vector<NamedTimer*>& getChildren();
|
||||
|
||||
void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
|
||||
bool getCollapsed() const { return mCollapsed; }
|
||||
|
||||
U32 getCountAverage() const { return mCountAverage; }
|
||||
U32 getCallAverage() const { return mCallAverage; }
|
||||
|
||||
U32 getHistoricalCount(S32 history_index = 0) const;
|
||||
U32 getHistoricalCalls(S32 history_index = 0) const;
|
||||
|
||||
static NamedTimer& getRootNamedTimer();
|
||||
|
||||
S32 getFrameStateIndex() const { return mFrameStateIndex; }
|
||||
|
||||
FrameState& getFrameState() const;
|
||||
|
||||
private:
|
||||
friend class LLFastTimer;
|
||||
friend class NamedTimerFactory;
|
||||
|
||||
//
|
||||
// methods
|
||||
//
|
||||
NamedTimer(const std::string& name);
|
||||
// recursive call to gather total time from children
|
||||
static void accumulateTimings();
|
||||
|
||||
// updates cumulative times and hierarchy,
|
||||
// can be called multiple times in a frame, at any point
|
||||
static void processTimes();
|
||||
|
||||
static void buildHierarchy();
|
||||
static void resetFrame();
|
||||
static void reset();
|
||||
|
||||
//
|
||||
// members
|
||||
//
|
||||
S32 mFrameStateIndex;
|
||||
|
||||
std::string mName;
|
||||
|
||||
U32 mTotalTimeCounter;
|
||||
|
||||
U32 mCountAverage;
|
||||
U32 mCallAverage;
|
||||
|
||||
U32* mCountHistory;
|
||||
U32* mCallHistory;
|
||||
|
||||
// tree structure
|
||||
NamedTimer* mParent; // NamedTimer of caller(parent)
|
||||
std::vector<NamedTimer*> mChildren;
|
||||
bool mCollapsed; // don't show children
|
||||
bool mNeedsSorting; // sort children whenever child added
|
||||
};
|
||||
|
||||
// used to statically declare a new named timer
|
||||
class LL_COMMON_API DeclareTimer
|
||||
: public LLInstanceTracker<DeclareTimer>
|
||||
{
|
||||
friend class LLFastTimer;
|
||||
public:
|
||||
DeclareTimer(const std::string& name, bool open);
|
||||
DeclareTimer(const std::string& name);
|
||||
|
||||
static void updateCachedPointers();
|
||||
|
||||
private:
|
||||
NamedTimer& mTimer;
|
||||
FrameState* mFrameState;
|
||||
};
|
||||
|
||||
public:
|
||||
LLFastTimer(LLFastTimer::FrameState* state);
|
||||
|
||||
LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
|
||||
: mFrameState(timer.mFrameState)
|
||||
{
|
||||
#if TIME_FAST_TIMERS
|
||||
U64 timer_start = getCPUClockCount64();
|
||||
#endif
|
||||
#if FAST_TIMER_ON
|
||||
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||
mStartTime = getCPUClockCount32();
|
||||
|
||||
frame_state->mActiveCount++;
|
||||
frame_state->mCalls++;
|
||||
// keep current parent as long as it is active when we are
|
||||
frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
|
||||
|
||||
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
||||
mLastTimerData = *cur_timer_data;
|
||||
cur_timer_data->mCurTimer = this;
|
||||
cur_timer_data->mFrameState = frame_state;
|
||||
cur_timer_data->mChildTime = 0;
|
||||
#endif
|
||||
#if TIME_FAST_TIMERS
|
||||
U64 timer_end = getCPUClockCount64();
|
||||
sTimerCycles += timer_end - timer_start;
|
||||
#endif
|
||||
#if DEBUG_FAST_TIMER_THREADS
|
||||
#if !LL_RELEASE
|
||||
assert_main_thread();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
LL_FORCE_INLINE ~LLFastTimer()
|
||||
{
|
||||
#if TIME_FAST_TIMERS
|
||||
U64 timer_start = getCPUClockCount64();
|
||||
#endif
|
||||
#if FAST_TIMER_ON
|
||||
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||
U32 total_time = getCPUClockCount32() - mStartTime;
|
||||
|
||||
frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime;
|
||||
frame_state->mActiveCount--;
|
||||
|
||||
// store last caller to bootstrap tree creation
|
||||
// do this in the destructor in case of recursion to get topmost caller
|
||||
frame_state->mLastCaller = mLastTimerData.mFrameState;
|
||||
|
||||
// we are only tracking self time, so subtract our total time delta from parents
|
||||
mLastTimerData.mChildTime += total_time;
|
||||
|
||||
LLFastTimer::sCurTimerData = mLastTimerData;
|
||||
#endif
|
||||
#if TIME_FAST_TIMERS
|
||||
U64 timer_end = getCPUClockCount64();
|
||||
sTimerCycles += timer_end - timer_start;
|
||||
sTimerCalls++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
static LLMutex* sLogLock;
|
||||
static std::queue<LLSD> sLogQueue;
|
||||
static BOOL sLog;
|
||||
static BOOL sMetricLog;
|
||||
static std::string sLogName;
|
||||
static bool sPauseHistory;
|
||||
static bool sResetHistory;
|
||||
static U64 sTimerCycles;
|
||||
static U32 sTimerCalls;
|
||||
|
||||
typedef std::vector<FrameState> info_list_t;
|
||||
static info_list_t& getFrameStateList();
|
||||
|
||||
|
||||
// call this once a frame to reset timers
|
||||
static void nextFrame();
|
||||
|
||||
// dumps current cumulative frame stats to log
|
||||
// call nextFrame() to reset timers
|
||||
static void dumpCurTimes();
|
||||
|
||||
// call this to reset timer hierarchy, averages, etc.
|
||||
static void reset();
|
||||
|
||||
static U64 countsPerSecond();
|
||||
static S32 getLastFrameIndex() { return sLastFrameIndex; }
|
||||
static S32 getCurFrameIndex() { return sCurFrameIndex; }
|
||||
|
||||
static void writeLog(std::ostream& os);
|
||||
static const NamedTimer* getTimerByName(const std::string& name);
|
||||
|
||||
struct CurTimerData
|
||||
{
|
||||
LLFastTimer* mCurTimer;
|
||||
FrameState* mFrameState;
|
||||
U32 mChildTime;
|
||||
};
|
||||
static CurTimerData sCurTimerData;
|
||||
static std::string sClockType;
|
||||
|
||||
public:
|
||||
static U32 getCPUClockCount32();
|
||||
static U64 getCPUClockCount64();
|
||||
static U64 sClockResolution;
|
||||
|
||||
private:
|
||||
static S32 sCurFrameIndex;
|
||||
static S32 sLastFrameIndex;
|
||||
static U64 sLastFrameTime;
|
||||
static info_list_t* sTimerInfos;
|
||||
|
||||
U32 mStartTime;
|
||||
LLFastTimer::FrameState* mFrameState;
|
||||
LLFastTimer::CurTimerData mLastTimerData;
|
||||
|
||||
};
|
||||
|
||||
typedef class LLFastTimer LLFastTimer;
|
||||
|
||||
#endif // LL_LLFASTTIMER_H
|
||||
102
indra/llcommon/llptrto.cpp
Normal file
102
indra/llcommon/llptrto.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @file llptrto.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2008-08-20
|
||||
* @brief Test for llptrto.h
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llptrto.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
// other Linden headers
|
||||
#include "llmemory.h"
|
||||
|
||||
// a refcounted class
|
||||
class RCFoo: public LLRefCount
|
||||
{
|
||||
public:
|
||||
RCFoo() {}
|
||||
};
|
||||
|
||||
// a refcounted subclass
|
||||
class RCSubFoo: public RCFoo
|
||||
{
|
||||
public:
|
||||
RCSubFoo() {}
|
||||
};
|
||||
|
||||
// a refcounted class using the other refcount base class
|
||||
class TSRCFoo: public LLThreadSafeRefCount
|
||||
{
|
||||
public:
|
||||
TSRCFoo() {}
|
||||
};
|
||||
|
||||
// a non-refcounted class
|
||||
class Bar
|
||||
{
|
||||
public:
|
||||
Bar() {}
|
||||
};
|
||||
|
||||
// a non-refcounted subclass
|
||||
class SubBar: public Bar
|
||||
{
|
||||
public:
|
||||
SubBar() {}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// test LLPtrTo<>
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value));
|
||||
|
||||
// Test LLRemovePointer<>. Note that we remove both pointer variants from
|
||||
// each kind of type, regardless of whether the variant makes sense.
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value));
|
||||
|
||||
return 0;
|
||||
}
|
||||
87
indra/llcommon/llptrto.h
Normal file
87
indra/llcommon/llptrto.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file llptrto.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2008-08-19
|
||||
* @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when
|
||||
* TARGET is a subclass of LLRefCount or LLThreadSafeRefCount --
|
||||
* LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLPTRTO_H)
|
||||
#define LL_LLPTRTO_H
|
||||
|
||||
#include "llpointer.h"
|
||||
#include "llrefcount.h" // LLRefCount
|
||||
#include "llthread.h" // LLThreadSafeRefCount
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/type_traits/remove_pointer.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
/**
|
||||
* LLPtrTo<TARGET>::type is either of two things:
|
||||
*
|
||||
* * When TARGET is a subclass of either LLRefCount or LLThreadSafeRefCount,
|
||||
* LLPtrTo<TARGET>::type is LLPointer<TARGET>.
|
||||
* * Otherwise, LLPtrTo<TARGET>::type is TARGET*.
|
||||
*
|
||||
* This way, a class template can use LLPtrTo<TARGET>::type to select an
|
||||
* appropriate pointer type to store.
|
||||
*/
|
||||
template <class T, class ENABLE=void>
|
||||
struct LLPtrTo
|
||||
{
|
||||
typedef T* type;
|
||||
};
|
||||
|
||||
/// specialize for subclasses of LLRefCount
|
||||
template <class T>
|
||||
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type>
|
||||
{
|
||||
typedef LLPointer<T> type;
|
||||
};
|
||||
|
||||
/// specialize for subclasses of LLThreadSafeRefCount
|
||||
template <class T>
|
||||
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type>
|
||||
{
|
||||
typedef LLPointer<T> type;
|
||||
};
|
||||
|
||||
/**
|
||||
* LLRemovePointer<PTRTYPE>::type gets you the underlying (pointee) type.
|
||||
*/
|
||||
template <typename PTRTYPE>
|
||||
struct LLRemovePointer
|
||||
{
|
||||
typedef typename boost::remove_pointer<PTRTYPE>::type type;
|
||||
};
|
||||
|
||||
/// specialize for LLPointer<SOMECLASS>
|
||||
template <typename SOMECLASS>
|
||||
struct LLRemovePointer< LLPointer<SOMECLASS> >
|
||||
{
|
||||
typedef SOMECLASS type;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLPTRTO_H) */
|
||||
@@ -43,6 +43,9 @@
|
||||
#include <winnls.h> // for WideCharToMultiByte
|
||||
#endif
|
||||
|
||||
LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
|
||||
|
||||
|
||||
std::string ll_safe_string(const char* in)
|
||||
{
|
||||
if(in) return std::string(in);
|
||||
@@ -1196,7 +1199,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
|
||||
template<>
|
||||
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
|
||||
{
|
||||
LLFastTimer ft(LLFastTimer::FT_STRING_FORMAT);
|
||||
LLFastTimer ft(FT_STRING_FORMAT);
|
||||
S32 res = 0;
|
||||
|
||||
std::string output;
|
||||
@@ -1269,7 +1272,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
|
||||
template<>
|
||||
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
|
||||
{
|
||||
LLFastTimer ft(LLFastTimer::FT_STRING_FORMAT);
|
||||
LLFastTimer ft(FT_STRING_FORMAT);
|
||||
S32 res = 0;
|
||||
|
||||
if (!substitutions.isMap())
|
||||
|
||||
Reference in New Issue
Block a user