Pull sync to viewer-development

meow-7.2.2
Merov Linden 2011-01-13 11:30:56 -08:00
commit ce55885aa3
17 changed files with 726 additions and 131 deletions

View File

@ -92,6 +92,17 @@ LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int shari
#endif
}
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
if (file)
{
ret_value = fclose(file);
}
return ret_value;
}
int LLFile::remove(const std::string& filename)
{
#if LL_WINDOWS

View File

@ -71,6 +71,8 @@ public:
static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
static int close(LLFILE * file);
// perms is a permissions mask like 0777 or 0700. In most cases it will
// be overridden by the user's umask. It is ignored on Windows.
static int mkdir(const std::string& filename, int perms = 0700);

View File

@ -530,6 +530,7 @@ set(viewer_SOURCE_FILES
llviewerregion.cpp
llviewershadermgr.cpp
llviewerstats.cpp
llviewerstatsrecorder.cpp
llviewertexteditor.cpp
llviewertexture.cpp
llviewertextureanim.cpp
@ -1063,6 +1064,7 @@ set(viewer_HEADER_FILES
llviewerregion.h
llviewershadermgr.h
llviewerstats.h
llviewerstatsrecorder.h
llviewertexteditor.h
llviewertexture.h
llviewertextureanim.h

View File

@ -44,6 +44,7 @@
#include "llagentwearables.h"
#include "llwindow.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
#include "llmd5.h"
#include "llpumpio.h"
#include "llmimetypes.h"
@ -667,6 +668,10 @@ bool LLAppViewer::init()
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::initClass();
#endif
// *NOTE:Mani - LLCurl::initClass is not thread safe.
// Called before threads are created.
LLCurl::initClass();
@ -990,6 +995,8 @@ bool LLAppViewer::init()
LLAgentLanguage::init();
return true;
}
@ -1710,6 +1717,10 @@ bool LLAppViewer::cleanup()
}
LLMetricPerformanceTesterBasic::cleanClass() ;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::cleanupClass();
#endif
llinfos << "Cleaning up Media and Textures" << llendflush;
//Note:

View File

@ -2578,6 +2578,49 @@ void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color)
gGL.end();
}
void renderUpdateType(LLDrawable* drawablep)
{
LLViewerObject* vobj = drawablep->getVObj();
if (!vobj || OUT_UNKNOWN == vobj->getLastUpdateType())
{
return;
}
LLGLEnable blend(GL_BLEND);
switch (vobj->getLastUpdateType())
{
case OUT_FULL:
glColor4f(0,1,0,0.5f);
break;
case OUT_TERSE_IMPROVED:
glColor4f(0,1,1,0.5f);
break;
case OUT_FULL_COMPRESSED:
if (vobj->getLastUpdateCached())
{
glColor4f(1,0,0,0.5f);
}
else
{
glColor4f(1,1,0,0.5f);
}
break;
case OUT_FULL_CACHED:
glColor4f(0,0,1,0.5f);
break;
default:
llwarns << "Unknown update_type " << vobj->getLastUpdateType() << llendl;
break;
};
S32 num_faces = drawablep->getNumFaces();
if (num_faces)
{
for (S32 i = 0; i < num_faces; ++i)
{
pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
}
}
}
void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
{
@ -3018,6 +3061,10 @@ public:
{
renderRaycast(drawable);
}
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_UPDATE_TYPE))
{
renderUpdateType(drawable);
}
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
@ -3180,6 +3227,7 @@ void LLSpatialPartition::renderDebug()
LLPipeline::RENDER_DEBUG_OCCLUSION |
LLPipeline::RENDER_DEBUG_LIGHTS |
LLPipeline::RENDER_DEBUG_BATCH_SIZE |
LLPipeline::RENDER_DEBUG_UPDATE_TYPE |
LLPipeline::RENDER_DEBUG_BBOXES |
LLPipeline::RENDER_DEBUG_POINTS |
LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |

View File

@ -906,6 +906,10 @@ U32 info_display_from_string(std::string info_display)
{
return LLPipeline::RENDER_DEBUG_BATCH_SIZE;
}
else if ("update type" == info_display)
{
return LLPipeline::RENDER_DEBUG_UPDATE_TYPE;
}
else if ("texture anim" == info_display)
{
return LLPipeline::RENDER_DEBUG_TEXTURE_ANIM;

View File

@ -234,7 +234,9 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
mState(0),
mMedia(NULL),
mClickAction(0),
mAttachmentItemID(LLUUID::null)
mAttachmentItemID(LLUUID::null),
mLastUpdateType(OUT_UNKNOWN),
mLastUpdateCached(FALSE)
{
if (!is_global)
{
@ -5403,6 +5405,26 @@ void LLViewerObject::setAttachmentItemID(const LLUUID &id)
mAttachmentItemID = id;
}
EObjectUpdateType LLViewerObject::getLastUpdateType() const
{
return mLastUpdateType;
}
void LLViewerObject::setLastUpdateType(EObjectUpdateType last_update_type)
{
mLastUpdateType = last_update_type;
}
BOOL LLViewerObject::getLastUpdateCached() const
{
return mLastUpdateCached;
}
void LLViewerObject::setLastUpdateCached(BOOL last_update_cached)
{
mLastUpdateCached = last_update_cached;
}
const LLUUID &LLViewerObject::extractAttachmentItemID()
{
LLUUID item_id = LLUUID::null;

View File

@ -77,6 +77,7 @@ typedef enum e_object_update_type
OUT_TERSE_IMPROVED,
OUT_FULL_COMPRESSED,
OUT_FULL_CACHED,
OUT_UNKNOWN,
} EObjectUpdateType;
@ -693,8 +694,15 @@ public:
const LLUUID &getAttachmentItemID() const;
void setAttachmentItemID(const LLUUID &id);
const LLUUID &extractAttachmentItemID(); // find&set the inventory item ID of the attached object
EObjectUpdateType getLastUpdateType() const;
void setLastUpdateType(EObjectUpdateType last_update_type);
BOOL getLastUpdateCached() const;
void setLastUpdateCached(BOOL last_update_cached);
private:
LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory.
EObjectUpdateType mLastUpdateType;
BOOL mLastUpdateCached;
};
///////////////////

View File

@ -56,6 +56,7 @@
#include "llresmgr.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
#include "llvoavatarself.h"
#include "lltoolmgr.h"
#include "lltoolpie.h"
@ -302,8 +303,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
// have to transform to absolute coordinates.
num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
// I don't think this case is ever hit. TODO* Test this.
if (!cached && !compressed && update_type != OUT_FULL)
{
//llinfos << "TEST: !cached && !compressed && update_type != OUT_FULL" << llendl;
gTerseObjectUpdates += num_objects;
S32 size;
if (mesgsys->getReceiveCompressedSize())
@ -314,7 +317,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
{
size = mesgsys->getReceiveSize();
}
// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
//llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
}
else
{
@ -345,9 +348,14 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
U8 compressed_dpbuffer[2048];
LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
LLDataPacker *cached_dpp = NULL;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(regionp);
#endif
for (i = 0; i < num_objects; i++)
{
// timer is unused?
LLTimer update_timer;
BOOL justCreated = FALSE;
@ -359,9 +367,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
// Lookup data packer and add this id to cache miss lists if necessary.
cached_dpp = regionp->getDP(id, crc);
U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE;
cached_dpp = regionp->getDP(id, crc, cache_miss_type);
if (cached_dpp)
{
// Cache Hit.
cached_dpp->reset();
cached_dpp->unpackUUID(fullid, "ID");
cached_dpp->unpackU32(local_id, "LocalID");
@ -369,6 +379,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
}
else
{
// Cache Miss.
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordCacheMissEvent(id, update_type, cache_miss_type);
#endif
continue; // no data packer, skip this object
}
}
@ -380,13 +395,15 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
compressed_dp.reset();
U32 flags = 0;
if (update_type != OUT_TERSE_IMPROVED)
if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?
{
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
}
// I don't think we ever use this flag from the server. DK 2010/12/09
if (flags & FLAGS_ZLIB_COMPRESSED)
{
//llinfos << "TEST: flags & FLAGS_ZLIB_COMPRESSED" << llendl;
compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);
uncompressed_length = 2048;
@ -402,7 +419,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
}
if (update_type != OUT_TERSE_IMPROVED)
if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?
{
compressed_dp.unpackUUID(fullid, "ID");
compressed_dp.unpackU32(local_id, "LocalID");
@ -422,7 +439,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
}
}
}
else if (update_type != OUT_FULL)
else if (update_type != OUT_FULL) // !compressed, !OUT_FULL ==> OUT_FULL_CACHED only?
{
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
getUUIDFromLocal(fullid,
@ -435,7 +452,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
mNumUnknownUpdates++;
}
}
else
else // OUT_FULL only?
{
mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
@ -467,12 +484,12 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
gMessageSystem->getSenderPort());
if (objectp->mLocalID != local_id)
{ // Update local ID in object with the one sent from the region
{ // Update local ID in object with the one sent from the region
objectp->mLocalID = local_id;
}
if (objectp->getRegion() != regionp)
{ // Object changed region, so update it
{ // Object changed region, so update it
objectp->updateRegion(regionp); // for LLVOAvatar
}
}
@ -483,18 +500,24 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
{
if (update_type == OUT_TERSE_IMPROVED)
{
// llinfos << "terse update for an unknown object:" << fullid << llendl;
// llinfos << "terse update for an unknown object (compressed):" << fullid << llendl;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type);
#endif
continue;
}
}
else if (cached)
else if (cached) // Cache hit only?
{
}
else
{
if (update_type != OUT_FULL)
{
// llinfos << "terse update for an unknown object:" << fullid << llendl;
//llinfos << "terse update for an unknown object:" << fullid << llendl;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type);
#endif
continue;
}
@ -504,7 +527,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
if (mDeadObjects.find(fullid) != mDeadObjects.end())
{
mNumDeadObjectUpdates++;
// llinfos << "update for a dead object:" << fullid << llendl;
//llinfos << "update for a dead object:" << fullid << llendl;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type);
#endif
continue;
}
#endif
@ -512,6 +538,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
if (!objectp)
{
llinfos << "createObject failure for object: " << fullid << llendl;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type);
#endif
continue;
}
justCreated = TRUE;
@ -524,19 +554,26 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;
}
bool bCached = false;
if (compressed)
{
if (update_type != OUT_TERSE_IMPROVED)
if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?
{
objectp->mLocalID = local_id;
}
processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated);
if (update_type != OUT_TERSE_IMPROVED)
if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?
{
bCached = true;
#if LL_RECORD_VIEWER_STATS
LLViewerRegion::eCacheUpdateResult result = objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
LLViewerStatsRecorder::instance()->recordCacheFullUpdate(local_id, update_type, result, objectp);
#else
objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
#endif
}
}
else if (cached)
else if (cached) // Cache hit only?
{
objectp->mLocalID = local_id;
processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
@ -549,8 +586,17 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
}
processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);
}
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->recordObjectUpdateEvent(local_id, update_type, objectp);
#endif
objectp->setLastUpdateType(update_type);
objectp->setLastUpdateCached(bCached);
}
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->endObjectUpdateEvents();
#endif
LLVOAvatar::cullAvatarsByPixelArea();
}
@ -681,12 +727,12 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
// update global timer
F32 last_time = gFrameTimeSeconds;
U64 time = totalTime(); // this will become the new gFrameTime when the update is done
U64 time = totalTime(); // this will become the new gFrameTime when the update is done
// Time _can_ go backwards, for example if the user changes the system clock.
// It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.
// llassert(time > gFrameTime);
F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC;
gFrameTime = time;
gFrameTime = time;
F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;
gFrameTimeSeconds = (F32)time_since_start;
@ -788,7 +834,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
{
std::string id_str;
objectp->mID.toString(id_str);
std::string tmpstr = std::string("Par: ") + id_str;
std::string tmpstr = std::string("Par: ") + id_str;
addDebugBeacon(objectp->getPositionAgent(),
tmpstr,
LLColor4(1.f,0.f,0.f,1.f),
@ -808,12 +854,12 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
std::string tmpstr;
if (objectp->getParent())
{
tmpstr = std::string("ChP: ") + id_str;
tmpstr = std::string("ChP: ") + id_str;
text_color = LLColor4(0.f, 1.f, 0.f, 1.f);
}
else
{
tmpstr = std::string("ChNoP: ") + id_str;
tmpstr = std::string("ChNoP: ") + id_str;
text_color = LLColor4(1.f, 0.f, 0.f, 1.f);
}
id = sIndexAndLocalIDToUUID[oi.mParentInfo];
@ -1519,8 +1565,8 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
llinfos << "Agent: " << objectp->getPositionAgent() << llendl;
addDebugBeacon(objectp->getPositionAgent(),"");
#endif
gPipeline.markMoved(objectp->mDrawable);
objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
gPipeline.markMoved(objectp->mDrawable);
objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
// Flag the object as no longer orphaned
childp->mOrphaned = FALSE;

View File

@ -59,6 +59,7 @@
#include "llurldispatcher.h"
#include "llviewerobjectlist.h"
#include "llviewerparceloverlay.h"
#include "llviewerstatsrecorder.h"
#include "llvlmanager.h"
#include "llvlcomposition.h"
#include "llvocache.h"
@ -1032,7 +1033,7 @@ void LLViewerRegion::getInfo(LLSD& info)
info["Region"]["Handle"]["y"] = (LLSD::Integer)y;
}
void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)
LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)
{
U32 local_id = objectp->getLocalID();
U32 crc = objectp->getCRC();
@ -1046,35 +1047,36 @@ void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinary
{
// Record a hit
entry->recordDupe();
return CACHE_UPDATE_DUPE;
}
else
{
// Update the cache entry
mCacheMap.erase(local_id);
delete entry;
entry = new LLVOCacheEntry(local_id, crc, dp);
mCacheMap[local_id] = entry;
}
}
else
{
// we haven't seen this object before
// Create new entry and add to map
if (mCacheMap.size() > MAX_OBJECT_CACHE_ENTRIES)
{
mCacheMap.erase(mCacheMap.begin());
}
// Update the cache entry
mCacheMap.erase(local_id);
delete entry;
entry = new LLVOCacheEntry(local_id, crc, dp);
mCacheMap[local_id] = entry;
return CACHE_UPDATE_CHANGED;
}
return ;
// we haven't seen this object before
// Create new entry and add to map
eCacheUpdateResult result = CACHE_UPDATE_ADDED;
if (mCacheMap.size() > MAX_OBJECT_CACHE_ENTRIES)
{
mCacheMap.erase(mCacheMap.begin());
result = CACHE_UPDATE_REPLACED;
}
entry = new LLVOCacheEntry(local_id, crc, dp);
mCacheMap[local_id] = entry;
return result;
}
// Get data packer for this object, if we have cached data
// AND the CRC matches. JC
LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc)
LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc, U8 &cache_miss_type)
{
llassert(mCacheLoaded);
@ -1087,17 +1089,20 @@ LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc)
{
// Record a hit
entry->recordHit();
cache_miss_type = CACHE_MISS_TYPE_NONE;
return entry->getDP(crc);
}
else
{
// llinfos << "CRC miss for " << local_id << llendl;
cache_miss_type = CACHE_MISS_TYPE_CRC;
mCacheMissCRC.put(local_id);
}
}
else
{
// llinfos << "Cache miss for " << local_id << llendl;
cache_miss_type = CACHE_MISS_TYPE_FULL;
mCacheMissFull.put(local_id);
}
return NULL;
@ -1119,9 +1124,6 @@ void LLViewerRegion::requestCacheMisses()
S32 blocks = 0;
S32 i;
const U8 CACHE_MISS_TYPE_FULL = 0;
const U8 CACHE_MISS_TYPE_CRC = 1;
// Send full cache miss updates. For these, we KNOW we don't
// have a viewer object.
for (i = 0; i < full_count; i++)
@ -1184,6 +1186,11 @@ void LLViewerRegion::requestCacheMisses()
mCacheDirty = TRUE ;
// llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl;
#if LL_RECORD_VIEWER_STATS
LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(this);
LLViewerStatsRecorder::instance()->recordRequestCacheMissesEvent(full_count + crc_count);
LLViewerStatsRecorder::instance()->endObjectUpdateEvents();
#endif
}
void LLViewerRegion::dumpCache()

View File

@ -51,7 +51,7 @@
// Surface id's
#define LAND 1
#define WATER 2
const U32 MAX_OBJECT_CACHE_ENTRIES = 10000;
const U32 MAX_OBJECT_CACHE_ENTRIES = 50000;
class LLEventPoll;
@ -275,9 +275,24 @@ public:
void getInfo(LLSD& info);
typedef enum
{
CACHE_MISS_TYPE_FULL = 0,
CACHE_MISS_TYPE_CRC,
CACHE_MISS_TYPE_NONE
} eCacheMissType;
typedef enum
{
CACHE_UPDATE_DUPE = 0,
CACHE_UPDATE_CHANGED,
CACHE_UPDATE_ADDED,
CACHE_UPDATE_REPLACED
} eCacheUpdateResult;
// handle a full update message
void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp);
LLDataPacker *getDP(U32 local_id, U32 crc);
eCacheUpdateResult cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp);
LLDataPacker *getDP(U32 local_id, U32 crc, U8 &cache_miss_type);
void requestCacheMisses();
void addCacheMissFull(const U32 local_id);

View File

@ -0,0 +1,258 @@
/**
* @file llviewerstatsrecorder.cpp
* @brief record info about viewer events to a metrics log file
*
* $LicenseInfo:firstyear=2010&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 "llviewerprecompiledheaders.h"
#include "llviewerstatsrecorder.h"
#if LL_RECORD_VIEWER_STATS
#include "llfile.h"
#include "llviewerregion.h"
#include "llviewerobject.h"
// To do - something using region name or global position
#if LL_WINDOWS
static const std::string STATS_FILE_NAME("C:\\ViewerObjectCacheStats.csv");
#else
static const std::string STATS_FILE_NAME("/tmp/viewerstats.csv");
#endif
LLViewerStatsRecorder* LLViewerStatsRecorder::sInstance = NULL;
LLViewerStatsRecorder::LLViewerStatsRecorder() :
mObjectCacheFile(NULL),
mTimer(),
mRegionp(NULL),
mStartTime(0.f),
mProcessingTime(0.f)
{
if (NULL != sInstance)
{
llerrs << "Attempted to create multiple instances of LLViewerStatsRecorder!" << llendl;
}
sInstance = this;
clearStats();
}
LLViewerStatsRecorder::~LLViewerStatsRecorder()
{
if (mObjectCacheFile != NULL)
{
LLFile::close(mObjectCacheFile);
mObjectCacheFile = NULL;
}
}
// static
void LLViewerStatsRecorder::initClass()
{
sInstance = new LLViewerStatsRecorder();
}
// static
void LLViewerStatsRecorder::cleanupClass()
{
delete sInstance;
sInstance = NULL;
}
void LLViewerStatsRecorder::initStatsRecorder(LLViewerRegion *regionp)
{
if (mObjectCacheFile == NULL)
{
mStartTime = LLTimer::getTotalTime();
mObjectCacheFile = LLFile::fopen(STATS_FILE_NAME, "wb");
if (mObjectCacheFile)
{ // Write column headers
std::ostringstream data_msg;
data_msg << "EventTime, "
<< "ProcessingTime, "
<< "CacheHits, "
<< "CacheFullMisses, "
<< "CacheCrcMisses, "
<< "FullUpdates, "
<< "TerseUpdates, "
<< "CacheMissRequests, "
<< "CacheMissResponses, "
<< "CacheUpdateDupes, "
<< "CacheUpdateChanges, "
<< "CacheUpdateAdds, "
<< "CacheUpdateReplacements, "
<< "UpdateFailures"
<< "\n";
fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile );
}
}
}
void LLViewerStatsRecorder::beginObjectUpdateEvents(LLViewerRegion *regionp)
{
initStatsRecorder(regionp);
mRegionp = regionp;
mProcessingTime = LLTimer::getTotalTime();
clearStats();
}
void LLViewerStatsRecorder::clearStats()
{
mObjectCacheHitCount = 0;
mObjectCacheMissFullCount = 0;
mObjectCacheMissCrcCount = 0;
mObjectFullUpdates = 0;
mObjectTerseUpdates = 0;
mObjectCacheMissRequests = 0;
mObjectCacheMissResponses = 0;
mObjectCacheUpdateDupes = 0;
mObjectCacheUpdateChanges = 0;
mObjectCacheUpdateAdds = 0;
mObjectCacheUpdateReplacements = 0;
mObjectUpdateFailures = 0;
}
void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type)
{
mObjectUpdateFailures++;
}
void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type)
{
if (LLViewerRegion::CACHE_MISS_TYPE_FULL == cache_miss_type)
{
mObjectCacheMissFullCount++;
}
else
{
mObjectCacheMissCrcCount++;
}
}
void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp)
{
switch (update_type)
{
case OUT_FULL:
mObjectFullUpdates++;
break;
case OUT_TERSE_IMPROVED:
mObjectTerseUpdates++;
break;
case OUT_FULL_COMPRESSED:
mObjectCacheMissResponses++;
break;
case OUT_FULL_CACHED:
mObjectCacheHitCount++;
break;
default:
llwarns << "Unknown update_type" << llendl;
break;
};
}
void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp)
{
switch (update_result)
{
case LLViewerRegion::CACHE_UPDATE_DUPE:
mObjectCacheUpdateDupes++;
break;
case LLViewerRegion::CACHE_UPDATE_CHANGED:
mObjectCacheUpdateChanges++;
break;
case LLViewerRegion::CACHE_UPDATE_ADDED:
mObjectCacheUpdateAdds++;
break;
case LLViewerRegion::CACHE_UPDATE_REPLACED:
mObjectCacheUpdateReplacements++;
break;
default:
llwarns << "Unknown update_result type" << llendl;
break;
};
}
void LLViewerStatsRecorder::recordRequestCacheMissesEvent(S32 count)
{
mObjectCacheMissRequests += count;
}
void LLViewerStatsRecorder::endObjectUpdateEvents()
{
llinfos << "ILX: "
<< mObjectCacheHitCount << " hits, "
<< mObjectCacheMissFullCount << " full misses, "
<< mObjectCacheMissCrcCount << " crc misses, "
<< mObjectFullUpdates << " full updates, "
<< mObjectTerseUpdates << " terse updates, "
<< mObjectCacheMissRequests << " cache miss requests, "
<< mObjectCacheMissResponses << " cache miss responses, "
<< mObjectCacheUpdateDupes << " cache update dupes, "
<< mObjectCacheUpdateChanges << " cache update changes, "
<< mObjectCacheUpdateAdds << " cache update adds, "
<< mObjectCacheUpdateReplacements << " cache update replacements, "
<< mObjectUpdateFailures << " update failures"
<< llendl;
S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures;
if (mObjectCacheFile != NULL &&
total_objects > 0)
{
std::ostringstream data_msg;
F32 processing32 = (F32) ((LLTimer::getTotalTime() - mProcessingTime) / 1000.0);
data_msg << getTimeSinceStart()
<< ", " << processing32
<< ", " << mObjectCacheHitCount
<< ", " << mObjectCacheMissFullCount
<< ", " << mObjectCacheMissCrcCount
<< ", " << mObjectFullUpdates
<< ", " << mObjectTerseUpdates
<< ", " << mObjectCacheMissRequests
<< ", " << mObjectCacheMissResponses
<< ", " << mObjectCacheUpdateDupes
<< ", " << mObjectCacheUpdateChanges
<< ", " << mObjectCacheUpdateAdds
<< ", " << mObjectCacheUpdateReplacements
<< ", " << mObjectUpdateFailures
<< "\n";
fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile );
}
clearStats();
}
F32 LLViewerStatsRecorder::getTimeSinceStart()
{
return (F32) ((LLTimer::getTotalTime() - mStartTime) / 1000.0);
}
#endif

View File

@ -0,0 +1,97 @@
/**
* @file llviewerstatsrecorder.h
* @brief record info about viewer events to a metrics log file
*
* $LicenseInfo:firstyear=2010&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 LLVIEWERSTATSRECORDER_H
#define LLVIEWERSTATSRECORDER_H
// This is a diagnostic class used to record information from the viewer
// for analysis.
// This is normally 0. Set to 1 to enable viewer stats recording
#define LL_RECORD_VIEWER_STATS 0
#if LL_RECORD_VIEWER_STATS
#include "llframetimer.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
class LLMutex;
class LLViewerRegion;
class LLViewerObject;
class LLViewerStatsRecorder
{
public:
LLViewerStatsRecorder();
~LLViewerStatsRecorder();
static void initClass();
static void cleanupClass();
static LLViewerStatsRecorder* instance() {return sInstance; }
void initStatsRecorder(LLViewerRegion *regionp);
void beginObjectUpdateEvents(LLViewerRegion *regionp);
void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type);
void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type);
void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp);
void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp);
void recordRequestCacheMissesEvent(S32 count);
void endObjectUpdateEvents();
F32 getTimeSinceStart();
private:
static LLViewerStatsRecorder* sInstance;
LLFILE * mObjectCacheFile; // File to write data into
LLFrameTimer mTimer;
LLViewerRegion* mRegionp;
F64 mStartTime;
F64 mProcessingTime;
S32 mObjectCacheHitCount;
S32 mObjectCacheMissFullCount;
S32 mObjectCacheMissCrcCount;
S32 mObjectFullUpdates;
S32 mObjectTerseUpdates;
S32 mObjectCacheMissRequests;
S32 mObjectCacheMissResponses;
S32 mObjectCacheUpdateDupes;
S32 mObjectCacheUpdateChanges;
S32 mObjectCacheUpdateAdds;
S32 mObjectCacheUpdateReplacements;
S32 mObjectUpdateFailures;
void clearStats();
};
#endif // LL_RECORD_VIEWER_STATS
#endif // LLVIEWERSTATSRECORDER_H

View File

@ -40,6 +40,7 @@ BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)
return apr_file->write(src, n_bytes) == n_bytes ;
}
//---------------------------------------------------------------------------
// LLVOCacheEntry
//---------------------------------------------------------------------------
@ -212,8 +213,8 @@ BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
if(success)
{
success = check_write(apr_file, (void*)mBuffer, size);
}
}
}
return success ;
}
@ -224,7 +225,8 @@ BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
// Format string used to construct filename for the object cache
static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
const U32 NUM_ENTRIES_TO_PURGE = 16 ;
// Throw out 1/20 (5%) of our cache entries if we run out of room.
const U32 ENTRIES_PURGE_FACTOR = 20;
const char* object_cache_dirname = "objectcache";
const char* header_filename = "object.cache";
@ -259,7 +261,6 @@ void LLVOCache::destroyClass()
LLVOCache::LLVOCache():
mInitialized(FALSE),
mReadOnly(TRUE),
mNumEntries(0),
mCacheSize(1)
{
mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
@ -286,8 +287,15 @@ void LLVOCache::setDirNames(ELLPath location)
void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
{
if(mInitialized || !mEnabled)
if(!mEnabled)
{
llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
return ;
}
if(mInitialized)
{
llwarns << "Cache already initialized." << llendl;
return ;
}
@ -299,7 +307,6 @@ void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
mCacheSize = size;
mMetaInfo.mVersion = cache_version;
readCacheHeader();
mInitialized = TRUE ;
@ -321,12 +328,14 @@ void LLVOCache::removeCache(ELLPath location)
{
if(mReadOnly)
{
llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
return ;
}
std::string delem = gDirUtilp->getDirDelimiter();
std::string mask = delem + "*";
std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
llinfos << "Removing cache at " << cache_dir << llendl;
gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
LLFile::rmdir(cache_dir);
@ -339,11 +348,13 @@ void LLVOCache::removeCache()
llassert_always(mInitialized) ;
if(mReadOnly)
{
llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
return ;
}
std::string delem = gDirUtilp->getDirDelimiter();
std::string mask = delem + "*";
llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
clearCacheInMemory() ;
@ -352,16 +363,8 @@ void LLVOCache::removeCache()
void LLVOCache::clearCacheInMemory()
{
if(!mHeaderEntryQueue.empty())
{
for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
{
delete *iter ;
}
mHeaderEntryQueue.clear();
mHandleEntryMap.clear();
mNumEntries = 0 ;
}
std::for_each(mHandleEntryMap.begin(), mHandleEntryMap.end(), DeletePairedPointer());
mHandleEntryMap.clear();
}
void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)
@ -379,6 +382,7 @@ void LLVOCache::removeFromCache(U64 handle)
{
if(mReadOnly)
{
llwarns << "Not removing cache for handle " << handle << ": Cache is currently in read-only mode." << llendl;
return ;
}
@ -387,24 +391,28 @@ void LLVOCache::removeFromCache(U64 handle)
LLAPRFile::remove(filename, mLocalAPRFilePoolp);
}
BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes)
BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error)
{
if(!check_read(apr_file, src, n_bytes))
{
delete apr_file ;
removeCache() ;
if (remove_cache_on_error)
{
removeCache() ;
}
return FALSE ;
}
return TRUE ;
}
BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes)
BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error)
{
if(!check_write(apr_file, src, n_bytes))
{
delete apr_file ;
removeCache() ;
if (remove_cache_on_error)
{
removeCache() ;
}
return FALSE ;
}
@ -415,7 +423,8 @@ void LLVOCache::readCacheHeader()
{
if(!mEnabled)
{
return ;
llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
return;
}
//clear stale info.
@ -423,33 +432,35 @@ void LLVOCache::readCacheHeader()
if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
{
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
//read the meta element
if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
bool remove_cache_on_error = false;
if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo), remove_cache_on_error))
{
return ;
llwarns << "Error reading meta information from cache header." << llendl;
delete apr_file;
return;
}
HeaderEntryInfo* entry ;
mNumEntries = 0 ;
while(mNumEntries < mCacheSize)
for(U32 entry_index = 0; entry_index < mCacheSize; ++entry_index)
{
entry = new HeaderEntryInfo() ;
if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo)))
if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo), remove_cache_on_error))
{
llwarns << "Error reading cache header entry. (entry_index=" << entry_index << ")" << llendl;
delete entry ;
return ;
break;
}
else if(!entry->mTime) //end of the cache.
{
delete entry ;
return ;
break;
}
entry->mIndex = mNumEntries++ ;
mHeaderEntryQueue.insert(entry) ;
mHandleEntryMap[entry->mHandle] = entry ;
entry->mIndex = entry_index;
mHandleEntryMap[entry->mHandle] = entry;
}
delete apr_file ;
@ -462,40 +473,57 @@ void LLVOCache::readCacheHeader()
void LLVOCache::writeCacheHeader()
{
if(mReadOnly || !mEnabled)
if (!mEnabled)
{
return ;
}
llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
return;
}
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
if(mReadOnly)
{
llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
return;
}
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
//write the meta element
if(!checkWrite(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
{
return ;
llwarns << "Error writing meta information to cache header." << llendl;
delete apr_file;
return;
}
mNumEntries = 0 ;
for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; iter != mHeaderEntryQueue.end(); ++iter)
U32 entry_index = 0;
handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
for(handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
iter != iter_end;
++iter)
{
(*iter)->mIndex = mNumEntries++ ;
if(!checkWrite(apr_file, (void*)*iter, sizeof(HeaderEntryInfo)))
HeaderEntryInfo* entry = iter->second;
entry->mIndex = entry_index++;
if(!checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)))
{
return ;
llwarns << "Failed to write cache header for entry " << entry->mHandle << " (entry_index = " << entry_index << ")" << llendl;
delete apr_file;
return;
}
}
mNumEntries = mHeaderEntryQueue.size() ;
if(mNumEntries < mCacheSize)
// Why do we need to fill the cache header with default entries? DK 2010-12-14
// It looks like we currently rely on the file being pre-allocated so we can seek during updateEntry().
if(entry_index < mCacheSize)
{
HeaderEntryInfo* entry = new HeaderEntryInfo() ;
for(U32 i = mNumEntries ; i < mCacheSize; i++)
for(; entry_index < mCacheSize; ++entry_index)
{
//fill the cache with the default entry.
if(!checkWrite(apr_file, entry, sizeof(HeaderEntryInfo)))
{
llwarns << "Failed to fill cache header with default entries (entry_index = " << entry_index << "). Switching to read-only mode." << llendl;
mReadOnly = TRUE ; //disable the cache.
return ;
break;
}
}
delete entry ;
@ -505,16 +533,19 @@ void LLVOCache::writeCacheHeader()
BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
{
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_WRITE|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
apr_file->seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
return checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
BOOL result = checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
delete apr_file;
return result;
}
void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
{
if(!mEnabled)
{
llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
return ;
}
llassert_always(mInitialized);
@ -522,22 +553,24 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
if(iter == mHandleEntryMap.end()) //no cache
{
llwarns << "No handle map entry for " << handle << llendl;
return ;
}
std::string filename;
getObjectCacheFilename(handle, filename);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
LLUUID cache_id ;
if(!checkRead(apr_file, cache_id.mData, UUID_BYTES))
{
llwarns << "Error reading cache_id from " << filename << llendl;
delete apr_file;
return ;
}
if(cache_id != id)
{
llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
llwarns << "Cache ID (" << cache_id << ") doesn't match id for this region (" << id << "), discarding. handle = " << handle << llendl;
delete apr_file ;
return ;
}
@ -545,6 +578,8 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
S32 num_entries;
if(!checkRead(apr_file, &num_entries, sizeof(S32)))
{
llwarns << "Error reading num_entries from " << filename << llendl;
delete apr_file;
return ;
}
@ -553,13 +588,12 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
LLVOCacheEntry* entry = new LLVOCacheEntry(apr_file);
if (!entry->getLocalID())
{
llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
llwarns << "Aborting cache file load for " << filename << ", cache file corruption! (entry number = " << i << ")" << llendl;
delete entry ;
break;
}
cache_entry_map[entry->getLocalID()] = entry;
}
num_entries = cache_entry_map.size() ;
delete apr_file ;
return ;
@ -567,86 +601,104 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
void LLVOCache::purgeEntries()
{
U32 limit = mCacheSize - NUM_ENTRIES_TO_PURGE ;
while(mHeaderEntryQueue.size() > limit)
U32 limit = mCacheSize - (mCacheSize / ENTRIES_PURGE_FACTOR);
limit = llclamp(limit, (U32)1, mCacheSize);
// Construct a vector of entries out of the map so we can sort by time.
std::vector<HeaderEntryInfo*> header_vector;
handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
for (handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
iter != iter_end;
++iter)
{
header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
HeaderEntryInfo* entry = *iter ;
header_vector.push_back(iter->second);
}
// Sort by time, oldest first.
std::sort(header_vector.begin(), header_vector.end(), header_entry_less());
while(header_vector.size() > limit)
{
HeaderEntryInfo* entry = header_vector.front();
removeFromCache(entry->mHandle) ;
mHandleEntryMap.erase(entry->mHandle) ;
mHeaderEntryQueue.erase(iter) ;
delete entry ;
removeFromCache(entry->mHandle);
mHandleEntryMap.erase(entry->mHandle);
header_vector.erase(header_vector.begin());
delete entry;
}
writeCacheHeader() ;
// *TODO: Verify that we can avoid re-reading the cache header. DK 2010-12-14
readCacheHeader() ;
mNumEntries = mHandleEntryMap.size() ;
}
void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
{
if(!mEnabled)
{
llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
return ;
}
llassert_always(mInitialized);
if(mReadOnly)
{
llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
return ;
}
U32 num_handle_entries = mHandleEntryMap.size();
HeaderEntryInfo* entry;
handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
if(iter == mHandleEntryMap.end()) //new entry
{
if(mNumEntries >= mCacheSize)
{
if(num_handle_entries >= mCacheSize)
{
purgeEntries() ;
num_handle_entries = mHandleEntryMap.size();
}
entry = new HeaderEntryInfo();
entry->mHandle = handle ;
entry->mTime = time(NULL) ;
entry->mIndex = mNumEntries++ ;
mHeaderEntryQueue.insert(entry) ;
entry->mIndex = num_handle_entries++;
mHandleEntryMap[handle] = entry ;
}
else
{
// Update access time.
entry = iter->second ;
entry->mTime = time(NULL) ;
//resort
mHeaderEntryQueue.erase(entry) ;
mHeaderEntryQueue.insert(entry) ;
}
//update cache header
if(!updateEntry(entry))
{
llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
return ; //update failed.
}
if(!dirty_cache)
{
llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
return ; //nothing changed, no need to update.
}
//write to cache file
std::string filename;
getObjectCacheFilename(handle, filename);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
if(!checkWrite(apr_file, (void*)id.mData, UUID_BYTES))
{
llwarns << "Error writing id to " << filename << llendl;
delete apr_file;
return ;
}
S32 num_entries = cache_entry_map.size() ;
if(!checkWrite(apr_file, &num_entries, sizeof(S32)))
{
llwarns << "Error writing num_entries to " << filename << llendl;
delete apr_file;
return ;
}
@ -654,10 +706,10 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
{
if(!iter->second->writeToFile(apr_file))
{
llwarns << "Aborting cache file write for " << filename << ", error writing to file!" << llendl;
//failed
delete apr_file ;
removeCache() ;
return ;
break;
}
}

View File

@ -95,10 +95,13 @@ private:
{
bool operator()(const HeaderEntryInfo* lhs, const HeaderEntryInfo* rhs) const
{
return lhs->mTime < rhs->mTime; // older entry in front of queue (set)
if (lhs->mTime == rhs->mTime)
{
return lhs->mHandle < rhs->mHandle;
}
return lhs->mTime < rhs->mTime; // older entry in front
}
};
typedef std::set<HeaderEntryInfo*, header_entry_less> header_entry_queue_t;
typedef std::map<U64, HeaderEntryInfo*> handle_entry_map_t;
private:
LLVOCache() ;
@ -125,8 +128,8 @@ private:
void removeCache() ;
void purgeEntries();
BOOL updateEntry(const HeaderEntryInfo* entry);
BOOL checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes) ;
BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes) ;
BOOL checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error = true) ;
BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error = true) ;
private:
BOOL mEnabled;
@ -134,11 +137,9 @@ private:
BOOL mReadOnly ;
HeaderMetaInfo mMetaInfo;
U32 mCacheSize;
U32 mNumEntries;
std::string mHeaderFileName ;
std::string mObjectCacheDirName;
LLVolatileAPRPool* mLocalAPRFilePoolp ;
header_entry_queue_t mHeaderEntryQueue;
handle_entry_map_t mHandleEntryMap;
static LLVOCache* sInstance ;

View File

@ -424,6 +424,7 @@ public:
RENDER_DEBUG_AVATAR_VOLUME = 0x0100000,
RENDER_DEBUG_BUILD_QUEUE = 0x0200000,
RENDER_DEBUG_AGENT_TARGET = 0x0400000,
RENDER_DEBUG_UPDATE_TYPE = 0x0800000,
};
public:

View File

@ -2152,6 +2152,16 @@
function="Advanced.ToggleInfoDisplay"
parameter="render batches" />
</menu_item_check>
<menu_item_check
label="Update Type"
name="Update Type">
<menu_item_check.on_check
function="Advanced.CheckInfoDisplay"
parameter="update type" />
<menu_item_check.on_click
function="Advanced.ToggleInfoDisplay"
parameter="update type" />
</menu_item_check>
<menu_item_check
label="Texture Anim"
name="Texture Anim">