summaryrefslogtreecommitdiff
path: root/indra/newview/lltexturecache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/lltexturecache.cpp')
-rw-r--r--indra/newview/lltexturecache.cpp664
1 files changed, 440 insertions, 224 deletions
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index 051c189013..6a213309a0 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -2,31 +2,25 @@
* @file lltexturecache.cpp
* @brief Object which handles local texture caching
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&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$
*/
@@ -51,7 +45,8 @@
// cache/textures/[0-F]/UUID.texture
// Actual texture body files
-const S32 TEXTURE_CACHE_ENTRY_SIZE = 1024;
+//note: there is no good to define 1024 for TEXTURE_CACHE_ENTRY_SIZE while FIRST_PACKET_SIZE is 600 on sim side.
+const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE;//1024;
const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit
const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate)
@@ -390,6 +385,7 @@ bool LLTextureCacheRemoteWorker::doRead()
}
else
{
+ //llinfos << "texture " << mID.asString() << " found in local_assets" << llendl;
mImageSize = local_size;
mImageLocal = TRUE;
}
@@ -400,7 +396,8 @@ bool LLTextureCacheRemoteWorker::doRead()
// Second state / stage : identify the cache or not...
if (!done && (mState == CACHE))
{
- idx = mCache->getHeaderCacheEntry(mID, mImageSize);
+ LLTextureCache::Entry entry ;
+ idx = mCache->getHeaderCacheEntry(mID, entry);
if (idx < 0)
{
// The texture is *not* cached. We're done here...
@@ -409,6 +406,7 @@ bool LLTextureCacheRemoteWorker::doRead()
}
else
{
+ mImageSize = entry.mImageSize ;
// If the read offset is bigger than the header cache, we read directly from the body
// Note that currently, we *never* read with offset from the cache, so the result is *always* HEADER
mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
@@ -530,13 +528,14 @@ bool LLTextureCacheRemoteWorker::doRead()
bool LLTextureCacheRemoteWorker::doWrite()
{
bool done = false;
- S32 idx = -1;
+ S32 idx = -1;
// First state / stage : check that what we're trying to cache is in an OK shape
if (mState == INIT)
{
llassert_always(mOffset == 0); // We currently do not support write offsets
llassert_always(mDataSize > 0); // Things will go badly wrong if mDataSize is nul or negative...
+ llassert_always(mImageSize >= mDataSize);
mState = CACHE;
}
@@ -546,14 +545,19 @@ bool LLTextureCacheRemoteWorker::doWrite()
if (!done && (mState == CACHE))
{
bool alreadyCached = false;
- S32 cur_imagesize = 0;
+ LLTextureCache::Entry entry ;
+
// Checks if this image is already in the entry list
- idx = mCache->getHeaderCacheEntry(mID, cur_imagesize);
- if (idx >= 0 && (cur_imagesize >= 0))
+ idx = mCache->getHeaderCacheEntry(mID, entry);
+ if(idx < 0)
+ {
+ idx = mCache->setHeaderCacheEntry(mID, entry, mImageSize, mDataSize); // create the new entry.
+ }
+ else
{
- alreadyCached = true; // already there and non empty
+ alreadyCached = mCache->updateEntry(idx, entry, mImageSize, mDataSize); // update the existing entry.
}
- idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry
+
if (idx < 0)
{
llwarns << "LLTextureCacheWorker: " << mID
@@ -563,10 +567,6 @@ bool LLTextureCacheRemoteWorker::doWrite()
}
else
{
- if (cur_imagesize > 0 && (mImageSize != cur_imagesize))
- {
- alreadyCached = false; // re-write the header if the size changed in all cases
- }
if (alreadyCached && (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE))
{
// Small texture already cached case: we're done with writing
@@ -629,7 +629,7 @@ bool LLTextureCacheRemoteWorker::doWrite()
{
llassert(mDataSize > TEXTURE_CACHE_ENTRY_SIZE); // wouldn't make sense to be here otherwise...
S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE;
- if ((file_size > 0) && mCache->updateTextureEntryList(mID, file_size))
+
{
// build the cache file name from the UUID
std::string filename = mCache->getTextureFileName(mID);
@@ -647,10 +647,7 @@ bool LLTextureCacheRemoteWorker::doWrite()
done = true;
}
}
- else
- {
- mDataSize = 0; // no data written
- }
+
// Nothing else to do at that point...
done = true;
}
@@ -741,7 +738,7 @@ LLTextureCache::LLTextureCache(bool threaded)
mHeaderMutex(NULL),
mListMutex(NULL),
mHeaderAPRFile(NULL),
- mReadOnly(FALSE),
+ mReadOnly(TRUE), //do not allow to change the texture cache until setReadOnly() is called.
mTexturesSizeTotal(0),
mDoPurge(FALSE)
{
@@ -749,6 +746,8 @@ LLTextureCache::LLTextureCache(bool threaded)
LLTextureCache::~LLTextureCache()
{
+ clearDeleteList() ;
+ writeUpdatedEntries() ;
}
//////////////////////////////////////////////////////////////////////////////
@@ -756,6 +755,9 @@ LLTextureCache::~LLTextureCache()
//virtual
S32 LLTextureCache::update(U32 max_time_ms)
{
+ static LLFrameTimer timer ;
+ static const F32 MAX_TIME_INTERVAL = 300.f ; //seconds.
+
S32 res;
res = LLWorkerThread::update(max_time_ms);
@@ -791,6 +793,12 @@ S32 LLTextureCache::update(U32 max_time_ms)
responder->completed(success);
}
+ if(!res && timer.getElapsedTimeF32() > MAX_TIME_INTERVAL)
+ {
+ timer.reset() ;
+ writeUpdatedEntries() ;
+ }
+
return res;
}
@@ -813,58 +821,6 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id)
return filename;
}
-bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize)
-{
- bool res = false;
- bool purge = false;
- {
- LLMutexLock lock(&mHeaderMutex);
- size_map_t::iterator iter1 = mTexturesSizeMap.find(id);
- if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize)
- {
- llassert_always(bodysize > 0);
-
- S32 oldbodysize = 0;
- if (iter1 != mTexturesSizeMap.end())
- {
- oldbodysize = iter1->second;
- }
-
- Entry entry;
- S32 idx = openAndReadEntry(id, entry, false);
- if (idx < 0)
- {
- llwarns << "Failed to open entry: " << id << llendl;
- removeHeaderCacheEntry(id);
- LLAPRFile::remove(getTextureFileName(id), getLocalAPRFilePool());
- return false;
- }
- else if (oldbodysize != entry.mBodySize)
- {
- // TODO: change to llwarns
- llerrs << "Entry mismatch in mTextureSizeMap / mHeaderIDMap"
- << " idx=" << idx << " oldsize=" << oldbodysize << " entrysize=" << entry.mBodySize << llendl;
- }
- entry.mBodySize = bodysize;
- writeEntryAndClose(idx, entry);
-
- mTexturesSizeTotal -= oldbodysize;
- mTexturesSizeTotal += bodysize;
-
- if (mTexturesSizeTotal > sCacheMaxTexturesSize)
- {
- purge = true;
- }
- res = true;
- }
- }
- if (purge)
- {
- mDoPurge = TRUE;
- }
- return res;
-}
-
//debug
BOOL LLTextureCache::isInCache(const LLUUID& id)
{
@@ -922,13 +878,16 @@ U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_
S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
const char* entries_filename = "texture.entries";
const char* cache_filename = "texture.cache";
-const char* textures_dirname = "textures";
+const char* old_textures_dirname = "textures";
+//change the location of the texture cache to prevent from being deleted by old version viewers.
+const char* textures_dirname = "texturecache";
void LLTextureCache::setDirNames(ELLPath location)
{
std::string delem = gDirUtilp->getDirDelimiter();
- mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
- mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
+
+ mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename);
+ mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename);
mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
}
@@ -940,16 +899,38 @@ void LLTextureCache::purgeCache(ELLPath location)
{
setDirNames(location);
llassert_always(mHeaderAPRFile == NULL);
- LLAPRFile::remove(mHeaderEntriesFileName, getLocalAPRFilePool());
- LLAPRFile::remove(mHeaderDataFileName, getLocalAPRFilePool());
+
+ //remove the legacy cache if exists
+ std::string texture_dir = mTexturesDirName ;
+ mTexturesDirName = gDirUtilp->getExpandedFilename(location, old_textures_dirname);
+ if(LLFile::isdir(mTexturesDirName))
+ {
+ std::string file_name = gDirUtilp->getExpandedFilename(location, entries_filename);
+ LLAPRFile::remove(file_name, getLocalAPRFilePool());
+
+ file_name = gDirUtilp->getExpandedFilename(location, cache_filename);
+ LLAPRFile::remove(file_name, getLocalAPRFilePool());
+
+ purgeAllTextures(true);
+ }
+ mTexturesDirName = texture_dir ;
}
+
+ //remove the current texture cache.
purgeAllTextures(true);
}
-S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
+//is called in the main thread before initCache(...) is called.
+void LLTextureCache::setReadOnly(BOOL read_only)
{
- mReadOnly = read_only;
-
+ mReadOnly = read_only ;
+}
+
+//called in the main thread.
+S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache_mismatch)
+{
+ llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized.
+
S64 header_size = (max_size * 2) / 10;
S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE;
sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries));
@@ -966,9 +947,22 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
setDirNames(location);
+ if(texture_cache_mismatch)
+ {
+ //if readonly, disable the texture cache,
+ //otherwise wipe out the texture cache.
+ purgeAllTextures(true);
+
+ if(mReadOnly)
+ {
+ return max_size ;
+ }
+ }
+
if (!mReadOnly)
{
LLFile::mkdir(mTexturesDirName);
+
const char* subdirs = "0123456789abcdef";
for (S32 i=0; i<16; i++)
{
@@ -979,6 +973,8 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
readHeaderCache();
purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it
+ llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized.
+
return max_size; // unused cache space
}
@@ -990,13 +986,20 @@ LLAPRFile* LLTextureCache::openHeaderEntriesFile(bool readonly, S32 offset)
llassert_always(mHeaderAPRFile == NULL);
apr_int32_t flags = readonly ? APR_READ|APR_BINARY : APR_READ|APR_WRITE|APR_BINARY;
mHeaderAPRFile = new LLAPRFile(mHeaderEntriesFileName, flags, getLocalAPRFilePool());
- mHeaderAPRFile->seek(APR_SET, offset);
+ if(offset > 0)
+ {
+ mHeaderAPRFile->seek(APR_SET, offset);
+ }
return mHeaderAPRFile;
}
void LLTextureCache::closeHeaderEntriesFile()
{
- llassert_always(mHeaderAPRFile != NULL);
+ if(!mHeaderAPRFile)
+ {
+ return ;
+ }
+
delete mHeaderAPRFile;
mHeaderAPRFile = NULL;
}
@@ -1010,6 +1013,12 @@ void LLTextureCache::readEntriesHeader()
LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
getLocalAPRFilePool());
}
+ else //create an empty entries header.
+ {
+ mHeaderEntriesInfo.mVersion = sHeaderCacheVersion ;
+ mHeaderEntriesInfo.mEntries = 0 ;
+ writeEntriesHeader() ;
+ }
}
void LLTextureCache::writeEntriesHeader()
@@ -1022,8 +1031,7 @@ void LLTextureCache::writeEntriesHeader()
}
}
-static S32 mHeaderEntriesMaxWriteIdx = 0;
-
+//mHeaderMutex is locked before calling this.
S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create)
{
S32 idx = -1;
@@ -1063,8 +1071,7 @@ S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create
if (iter3 != mHeaderIDMap.end() && iter3->second >= 0)
{
idx = iter3->second;
- mHeaderIDMap.erase(oldid);
- mTexturesSizeMap.erase(oldid);
+ removeCachedTexture(oldid) ;//remove the existing cached texture to release the entry index.
break;
}
}
@@ -1074,20 +1081,9 @@ S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create
}
if (idx >= 0)
{
- // Set the header index
- mHeaderIDMap[id] = idx;
- llassert_always(mTexturesSizeMap.erase(id) == 0);
- // Initialize the entry (will get written later)
- entry.init(id, time(NULL));
- // Update Header
- writeEntriesHeader();
- // Write Entry
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
- LLAPRFile* aprfile = openHeaderEntriesFile(false, offset);
- S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry));
- llassert_always(bytes_written == sizeof(Entry));
- mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx);
- closeHeaderEntriesFile();
+ entry.mID = id ;
+ entry.mImageSize = -1 ; //mark it is a brand-new entry.
+ entry.mBodySize = 0 ;
}
}
}
@@ -1096,37 +1092,153 @@ S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create
// Remove this entry from the LRU if it exists
mLRU.erase(id);
// Read the entry
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
- LLAPRFile* aprfile = openHeaderEntriesFile(true, offset);
- S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry));
- llassert_always(bytes_read == sizeof(Entry));
- llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize);
- closeHeaderEntriesFile();
+ idx_entry_map_t::iterator iter = mUpdatedEntryMap.find(idx) ;
+ if(iter != mUpdatedEntryMap.end())
+ {
+ entry = iter->second ;
+ }
+ else
+ {
+ readEntryFromHeaderImmediately(idx, entry) ;
+ }
+ if(entry.mImageSize <= entry.mBodySize)//it happens on 64-bit systems, do not know why
+ {
+ llwarns << "corrupted entry: " << id << " entry image size: " << entry.mImageSize << " entry body size: " << entry.mBodySize << llendl ;
+
+ //erase this entry and the cached texture from the cache.
+ std::string tex_filename = getTextureFileName(id);
+ removeEntry(idx, entry, tex_filename) ;
+ mUpdatedEntryMap.erase(idx) ;
+ idx = -1 ;
+ }
}
return idx;
}
-void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry)
+//mHeaderMutex is locked before calling this.
+void LLTextureCache::writeEntryToHeaderImmediately(S32& idx, Entry& entry, bool write_header)
+{
+ LLAPRFile* aprfile ;
+ S32 bytes_written ;
+ S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ if(write_header)
+ {
+ aprfile = openHeaderEntriesFile(false, 0);
+ bytes_written = aprfile->write((U8*)&mHeaderEntriesInfo, sizeof(EntriesInfo)) ;
+ if(bytes_written != sizeof(EntriesInfo))
+ {
+ clearCorruptedCache() ; //clear the cache.
+ idx = -1 ;//mark the idx invalid.
+ return ;
+ }
+
+ mHeaderAPRFile->seek(APR_SET, offset);
+ }
+ else
+ {
+ aprfile = openHeaderEntriesFile(false, offset);
+ }
+ bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry));
+ if(bytes_written != sizeof(Entry))
+ {
+ clearCorruptedCache() ; //clear the cache.
+ idx = -1 ;//mark the idx invalid.
+
+ return ;
+ }
+
+ closeHeaderEntriesFile();
+ mUpdatedEntryMap.erase(idx) ;
+}
+
+//mHeaderMutex is locked before calling this.
+void LLTextureCache::readEntryFromHeaderImmediately(S32& idx, Entry& entry)
{
+ S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ LLAPRFile* aprfile = openHeaderEntriesFile(true, offset);
+ S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry));
+ closeHeaderEntriesFile();
+
+ if(bytes_read != sizeof(Entry))
+ {
+ clearCorruptedCache() ; //clear the cache.
+ idx = -1 ;//mark the idx invalid.
+ }
+}
+
+//mHeaderMutex is locked before calling this.
+//update an existing entry time stamp, delay writing.
+void LLTextureCache::updateEntryTimeStamp(S32 idx, Entry& entry)
+{
+ static const U32 MAX_ENTRIES_WITHOUT_TIME_STAMP = (U32)(LLTextureCache::sCacheMaxEntries * 0.75f) ;
+
+ if(mHeaderEntriesInfo.mEntries < MAX_ENTRIES_WITHOUT_TIME_STAMP)
+ {
+ return ; //there are enough empty entry index space, no need to stamp time.
+ }
+
if (idx >= 0)
{
if (!mReadOnly)
{
- entry.mTime = time(NULL);
- llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize);
- if (entry.mBodySize > 0)
- {
- mTexturesSizeMap[entry.mID] = entry.mBodySize;
- }
-// llinfos << "Updating TE: " << idx << ": " << id << " Size: " << entry.mBodySize << " Time: " << entry.mTime << llendl;
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
- LLAPRFile* aprfile = openHeaderEntriesFile(false, offset);
- S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry));
- llassert_always(bytes_written == sizeof(Entry));
- mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx);
- closeHeaderEntriesFile();
+ entry.mTime = time(NULL);
+ mUpdatedEntryMap[idx] = entry ;
+ }
+ }
+}
+
+//update an existing entry, write to header file immediately.
+bool LLTextureCache::updateEntry(S32& idx, Entry& entry, S32 new_image_size, S32 new_data_size)
+{
+ S32 new_body_size = llmax(0, new_data_size - TEXTURE_CACHE_ENTRY_SIZE) ;
+
+ if(new_image_size == entry.mImageSize && new_body_size == entry.mBodySize)
+ {
+ return true ; //nothing changed.
+ }
+ else
+ {
+ bool purge = false ;
+
+ lockHeaders() ;
+
+ bool update_header = false ;
+ if(entry.mImageSize < 0) //is a brand-new entry
+ {
+ mHeaderIDMap[entry.mID] = idx;
+ mTexturesSizeMap[entry.mID] = new_body_size ;
+ mTexturesSizeTotal += new_body_size ;
+
+ // Update Header
+ update_header = true ;
+ }
+ else if (entry.mBodySize != new_body_size)
+ {
+ //already in mHeaderIDMap.
+ mTexturesSizeMap[entry.mID] = new_body_size ;
+ mTexturesSizeTotal -= entry.mBodySize ;
+ mTexturesSizeTotal += new_body_size ;
+ }
+ entry.mTime = time(NULL);
+ entry.mImageSize = new_image_size ;
+ entry.mBodySize = new_body_size ;
+
+ writeEntryToHeaderImmediately(idx, entry, update_header) ;
+
+ if (mTexturesSizeTotal > sCacheMaxTexturesSize)
+ {
+ purge = true;
+ }
+
+ unlockHeaders() ;
+
+ if (purge)
+ {
+ mDoPurge = TRUE;
}
}
+
+ return false ;
}
U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
@@ -1138,7 +1250,21 @@ U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
mFreeList.clear();
mTexturesSizeTotal = 0;
- LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
+ LLAPRFile* aprfile = NULL;
+ if(mUpdatedEntryMap.empty())
+ {
+ aprfile = openHeaderEntriesFile(true, (S32)sizeof(EntriesInfo));
+ }
+ else //update the header file first.
+ {
+ aprfile = openHeaderEntriesFile(false, 0);
+ updatedHeaderEntriesFile() ;
+ if(!aprfile)
+ {
+ return 0;
+ }
+ aprfile->seek(APR_SET, (S32)sizeof(EntriesInfo));
+ }
for (U32 idx=0; idx<num_entries; idx++)
{
Entry entry;
@@ -1152,19 +1278,15 @@ U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
}
entries.push_back(entry);
// llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl;
- if (entry.mImageSize < 0)
+ if(entry.mImageSize > entry.mBodySize)
{
- mFreeList.insert(idx);
+ mHeaderIDMap[entry.mID] = idx;
+ mTexturesSizeMap[entry.mID] = entry.mBodySize;
+ mTexturesSizeTotal += entry.mBodySize;
}
else
{
- mHeaderIDMap[entry.mID] = idx;
- if (entry.mBodySize > 0)
- {
- mTexturesSizeMap[entry.mID] = entry.mBodySize;
- mTexturesSizeTotal += entry.mBodySize;
- }
- llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize);
+ mFreeList.insert(idx);
}
}
closeHeaderEntriesFile();
@@ -1182,13 +1304,65 @@ void LLTextureCache::writeEntriesAndClose(const std::vector<Entry>& entries)
for (S32 idx=0; idx<num_entries; idx++)
{
S32 bytes_written = aprfile->write((void*)(&entries[idx]), (S32)sizeof(Entry));
- llassert_always(bytes_written == sizeof(Entry));
+ if(bytes_written != sizeof(Entry))
+ {
+ clearCorruptedCache() ; //clear the cache.
+ return ;
+ }
}
- mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1);
closeHeaderEntriesFile();
}
}
+void LLTextureCache::writeUpdatedEntries()
+{
+ lockHeaders() ;
+ if (!mReadOnly && !mUpdatedEntryMap.empty())
+ {
+ openHeaderEntriesFile(false, 0);
+ updatedHeaderEntriesFile() ;
+ closeHeaderEntriesFile();
+ }
+ unlockHeaders() ;
+}
+
+//mHeaderMutex is locked and mHeaderAPRFile is created before calling this.
+void LLTextureCache::updatedHeaderEntriesFile()
+{
+ if (!mReadOnly && !mUpdatedEntryMap.empty() && mHeaderAPRFile)
+ {
+ //entriesInfo
+ mHeaderAPRFile->seek(APR_SET, 0);
+ S32 bytes_written = mHeaderAPRFile->write((U8*)&mHeaderEntriesInfo, sizeof(EntriesInfo)) ;
+ if(bytes_written != sizeof(EntriesInfo))
+ {
+ clearCorruptedCache() ; //clear the cache.
+ return ;
+ }
+
+ //write each updated entry
+ S32 entry_size = (S32)sizeof(Entry) ;
+ S32 prev_idx = -1 ;
+ S32 delta_idx ;
+ for (idx_entry_map_t::iterator iter = mUpdatedEntryMap.begin(); iter != mUpdatedEntryMap.end(); ++iter)
+ {
+ delta_idx = iter->first - prev_idx - 1;
+ prev_idx = iter->first ;
+ if(delta_idx)
+ {
+ mHeaderAPRFile->seek(APR_CUR, delta_idx * entry_size);
+ }
+
+ bytes_written = mHeaderAPRFile->write((void*)(&iter->second), entry_size);
+ if(bytes_written != entry_size)
+ {
+ clearCorruptedCache() ; //clear the cache.
+ return ;
+ }
+ }
+ mUpdatedEntryMap.clear() ;
+ }
+}
//----------------------------------------------------------------------------
// Called from either the main thread or the worker thread
@@ -1216,12 +1390,11 @@ void LLTextureCache::readHeaderCache()
U32 empty_entries = 0;
typedef std::pair<U32, S32> lru_data_t;
std::set<lru_data_t> lru;
- std::set<LLUUID> purge_list;
+ std::set<U32> purge_list;
for (U32 i=0; i<num_entries; i++)
{
Entry& entry = entries[i];
- const LLUUID& id = entry.mID;
- if (entry.mImageSize < 0)
+ if (entry.mImageSize <= 0)
{
// This will be in the Free List, don't put it in the LRU
++empty_entries;
@@ -1234,9 +1407,8 @@ void LLTextureCache::readHeaderCache()
if (entry.mBodySize > entry.mImageSize)
{
// Shouldn't happen, failsafe only
- llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl;
- purge_list.insert(entry.mID);
- entry.mImageSize = -1; // empty/available
+ llwarns << "Bad entry: " << i << ": " << entry.mID << ": BodySize: " << entry.mBodySize << llendl;
+ purge_list.insert(i);
}
}
}
@@ -1251,14 +1423,9 @@ void LLTextureCache::readHeaderCache()
{
for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
{
- S32 idx = iter->second;
- if (entries[idx].mImageSize >= 0)
- {
- purge_list.insert(entries[idx].mID);
- entries[idx].mImageSize = -1;
- if (purge_list.size() >= entries_to_purge)
- break;
- }
+ purge_list.insert(iter->second);
+ if (purge_list.size() >= entries_to_purge)
+ break;
}
}
llassert_always(purge_list.size() >= entries_to_purge);
@@ -1268,9 +1435,7 @@ void LLTextureCache::readHeaderCache()
S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE);
for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
{
- S32 idx = iter->second;
- const LLUUID& id = entries[idx].mID;
- mLRU.insert(id);
+ mLRU.insert(entries[iter->second].mID);
// llinfos << "LRU: " << iter->first << " : " << iter->second << llendl;
if (--lru_entries <= 0)
break;
@@ -1279,12 +1444,10 @@ void LLTextureCache::readHeaderCache()
if (purge_list.size() > 0)
{
- for (std::set<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter)
+ for (std::set<U32>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter)
{
- const LLUUID& id = *iter;
- bool res = removeHeaderCacheEntry(id); // sets entry size on disk to -1
- llassert_always(res);
- LLAPRFile::remove(getTextureFileName(id), getLocalAPRFilePool());
+ std::string tex_filename = getTextureFileName(entries[*iter].mID);
+ removeEntry((S32)*iter, entries[*iter], tex_filename);
}
// If we removed any entries, we need to rebuild the entries list,
// write the header, and call this again
@@ -1292,13 +1455,14 @@ void LLTextureCache::readHeaderCache()
for (U32 i=0; i<num_entries; i++)
{
const Entry& entry = entries[i];
- if (entry.mImageSize >=0)
+ if (entry.mImageSize > 0)
{
new_entries.push_back(entry);
}
}
llassert_always(new_entries.size() <= sCacheMaxEntries);
mHeaderEntriesInfo.mEntries = new_entries.size();
+ writeEntriesHeader();
writeEntriesAndClose(new_entries);
mHeaderMutex.unlock(); // unlock the mutex before calling again
readHeaderCache(); // repeat with new entries file
@@ -1306,7 +1470,7 @@ void LLTextureCache::readHeaderCache()
}
else
{
- writeEntriesAndClose(entries);
+ //entries are not changed, nothing here.
}
}
}
@@ -1315,6 +1479,29 @@ void LLTextureCache::readHeaderCache()
//////////////////////////////////////////////////////////////////////////////
+//the header mutex is locked before calling this.
+void LLTextureCache::clearCorruptedCache()
+{
+ llwarns << "the texture cache is corrupted, need to be cleared." << llendl ;
+
+ closeHeaderEntriesFile();//close possible file handler
+ purgeAllTextures(false) ; //clear the cache.
+
+ if (!mReadOnly) //regenerate the directory tree if not exists.
+ {
+ LLFile::mkdir(mTexturesDirName);
+
+ const char* subdirs = "0123456789abcdef";
+ for (S32 i=0; i<16; i++)
+ {
+ std::string dirname = mTexturesDirName + gDirUtilp->getDirDelimiter() + subdirs[i];
+ LLFile::mkdir(dirname);
+ }
+ }
+
+ return ;
+}
+
void LLTextureCache::purgeAllTextures(bool purge_directories)
{
if (!mReadOnly)
@@ -1334,19 +1521,23 @@ void LLTextureCache::purgeAllTextures(bool purge_directories)
}
if (purge_directories)
{
+ gDirUtilp->deleteFilesInDir(mTexturesDirName, mask);
LLFile::rmdir(mTexturesDirName);
- }
+ }
}
mHeaderIDMap.clear();
mTexturesSizeMap.clear();
mTexturesSizeTotal = 0;
mFreeList.clear();
mTexturesSizeTotal = 0;
+ mUpdatedEntryMap.clear();
// Info with 0 entries
mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
mHeaderEntriesInfo.mEntries = 0;
writeEntriesHeader();
+
+ llinfos << "The entire texture cache is cleared." << llendl ;
}
void LLTextureCache::purgeTextures(bool validate)
@@ -1371,7 +1562,6 @@ void LLTextureCache::purgeTextures(bool validate)
U32 num_entries = openAndReadEntries(entries);
if (!num_entries)
{
- writeEntriesAndClose(entries);
return; // nothing to purge
}
@@ -1390,6 +1580,10 @@ void LLTextureCache::purgeTextures(bool validate)
time_idx_set.insert(std::make_pair(entries[idx].mTime, idx));
// llinfos << "TIME: " << entries[idx].mTime << " TEX: " << entries[idx].mID << " IDX: " << idx << " Size: " << entries[idx].mImageSize << llendl;
}
+ else
+ {
+ llerrs << "mTexturesSizeMap / mHeaderIDMap corrupted." << llendl ;
+ }
}
}
@@ -1441,11 +1635,8 @@ void LLTextureCache::purgeTextures(bool validate)
{
purge_count++;
LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL;
- LLAPRFile::remove(filename, getLocalAPRFilePool());
+ removeEntry(idx, entries[idx], filename) ;
cache_size -= entries[idx].mBodySize;
- mTexturesSizeTotal -= entries[idx].mBodySize;
- entries[idx].mBodySize = 0;
- mTexturesSizeMap.erase(entries[idx].mID);
}
}
@@ -1492,40 +1683,38 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
// Called from work thread
// Reads imagesize from the header, updates timestamp
-S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize)
+S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, Entry& entry)
{
- LLMutexLock lock(&mHeaderMutex);
- Entry entry;
+ LLMutexLock lock(&mHeaderMutex);
S32 idx = openAndReadEntry(id, entry, false);
if (idx >= 0)
- {
- imagesize = entry.mImageSize;
- writeEntryAndClose(idx, entry); // updates time
+ {
+ updateEntryTimeStamp(idx, entry); // updates time
}
return idx;
}
// Writes imagesize to the header, updates timestamp
-S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize)
+S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, Entry& entry, S32 imagesize, S32 datasize)
{
mHeaderMutex.lock();
- llassert_always(imagesize >= 0);
- Entry entry;
S32 idx = openAndReadEntry(id, entry, true);
+ mHeaderMutex.unlock();
+
if (idx >= 0)
{
- entry.mImageSize = imagesize;
- writeEntryAndClose(idx, entry);
- mHeaderMutex.unlock();
+ updateEntry(idx, entry, imagesize, datasize);
}
- else // retry
+
+ if(idx < 0) // retry
{
- mHeaderMutex.unlock();
readHeaderCache(); // We couldn't write an entry, so refresh the LRU
+
mHeaderMutex.lock();
llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
mHeaderMutex.unlock();
- idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion
+
+ idx = setHeaderCacheEntry(id, entry, imagesize, datasize); // assert above ensures no inf. recursion
}
return idx;
}
@@ -1573,6 +1762,11 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort)
{
worker = iter->second;
complete = worker->complete();
+
+ if(!complete && abort)
+ {
+ abortRequest(handle, true) ;
+ }
}
if (worker && (complete || abort))
{
@@ -1617,20 +1811,20 @@ bool LLTextureCache::writeComplete(handle_t handle, bool abort)
{
lockWorkers();
handle_map_t::iterator iter = mWriters.find(handle);
- llassert_always(iter != mWriters.end());
- LLTextureCacheWorker* worker = iter->second;
- if (worker->complete() || abort)
- {
- mWriters.erase(handle);
- unlockWorkers();
- worker->scheduleDelete();
- return true;
- }
- else
+ llassert(iter != mWriters.end());
+ if (iter != mWriters.end())
{
- unlockWorkers();
- return false;
+ LLTextureCacheWorker* worker = iter->second;
+ if (worker->complete() || abort)
+ {
+ mWriters.erase(handle);
+ unlockWorkers();
+ worker->scheduleDelete();
+ return true;
+ }
}
+ unlockWorkers();
+ return false;
}
void LLTextureCache::prioritizeWrite(handle_t handle)
@@ -1649,34 +1843,56 @@ void LLTextureCache::addCompleted(Responder* responder, bool success)
//////////////////////////////////////////////////////////////////////////////
-// Called from MAIN thread (endWork())
-// Ensure that mHeaderMutex is locked first!
-bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
+//called after mHeaderMutex is locked.
+void LLTextureCache::removeCachedTexture(const LLUUID& id)
{
- Entry entry;
- S32 idx = openAndReadEntry(id, entry, false);
- if (idx >= 0)
+ if(mTexturesSizeMap.find(id) != mTexturesSizeMap.end())
+ {
+ mTexturesSizeTotal -= mTexturesSizeMap[id] ;
+ mTexturesSizeMap.erase(id);
+ }
+ mHeaderIDMap.erase(id);
+ LLAPRFile::remove(getTextureFileName(id), getLocalAPRFilePool());
+}
+
+//called after mHeaderMutex is locked.
+void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename)
+{
+ if(idx >= 0) //valid entry
{
entry.mImageSize = -1;
entry.mBodySize = 0;
- writeEntryAndClose(idx, entry);
- mFreeList.insert(idx);
- mHeaderIDMap.erase(id);
- mTexturesSizeMap.erase(id);
- return true;
+ mHeaderIDMap.erase(entry.mID);
+ mTexturesSizeMap.erase(entry.mID);
+
+ mTexturesSizeTotal -= entry.mBodySize;
+ mFreeList.insert(idx);
}
- return false;
+
+ LLAPRFile::remove(filename, getLocalAPRFilePool());
}
-void LLTextureCache::removeFromCache(const LLUUID& id)
+bool LLTextureCache::removeFromCache(const LLUUID& id)
{
//llwarns << "Removing texture from cache: " << id << llendl;
+ bool ret = false ;
if (!mReadOnly)
{
- LLMutexLock lock(&mHeaderMutex);
- removeHeaderCacheEntry(id);
- LLAPRFile::remove(getTextureFileName(id), getLocalAPRFilePool());
+ lockHeaders() ;
+
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
+ std::string tex_filename = getTextureFileName(id);
+ removeEntry(idx, entry, tex_filename) ;
+ if (idx >= 0)
+ {
+ writeEntryToHeaderImmediately(idx, entry);
+ ret = true;
+ }
+
+ unlockHeaders() ;
}
+ return ret ;
}
//////////////////////////////////////////////////////////////////////////////