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.cpp1286
1 files changed, 680 insertions, 606 deletions
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index 1b249d75d1..69a2d1d7a6 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -43,11 +43,17 @@
// Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout
#include "llappviewer.h"
-#define USE_LFS_READ 0
-#define USE_LFS_WRITE 0
-
-// Note: first 4 bytes store file size, rest is j2c data
-const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024;
+// Cache organization:
+// cache/texture.entries
+// Unordered array of Entry structs
+// cache/texture.cache
+// First TEXTURE_CACHE_ENTRY_SIZE bytes of each texture in texture.entries in same order
+// cache/textures/[0-F]/UUID.texture
+// Actual texture body files
+
+const S32 TEXTURE_CACHE_ENTRY_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)
class LLTextureCacheWorker : public LLWorkerClass
{
@@ -309,94 +315,75 @@ void LLTextureCacheWorker::startWork(S32 param)
{
}
+// This is where a texture is read from the cache system (header and body)
+// Current assumption are:
+// - the whole data are in a raw form, will be stored at mReadData
+// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache)
+// - the code supports offset reading but this is actually never exercised in the viewer
bool LLTextureCacheRemoteWorker::doRead()
{
+ bool done = false;
+ S32 idx = -1;
+
S32 local_size = 0;
std::string local_filename;
+ // First state / stage : find out if the file is local
if (mState == INIT)
{
std::string filename = mCache->getLocalFileName(mID);
- local_filename = filename + ".j2c";
- local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
- if (local_size == 0)
+ // Is it a JPEG2000 file?
{
- local_filename = filename + ".tga";
+ local_filename = filename + ".j2c";
local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
if (local_size > 0)
{
- mImageFormat = IMG_CODEC_TGA;
- mDataSize = local_size; // Only a complete .tga file is valid
+ mImageFormat = IMG_CODEC_J2C;
}
}
- if (local_size > 0)
- {
- mState = LOCAL;
- }
- else
- {
- mState = CACHE;
- }
- }
-
- if (mState == LOCAL)
- {
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
+ // If not, is it a jpeg file?
+ if (local_size == 0)
{
- mImageLocal = TRUE;
- mImageSize = local_size;
- if (!mDataSize || mDataSize + mOffset > local_size)
+ local_filename = filename + ".jpg";
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
+ if (local_size > 0)
{
- mDataSize = local_size - mOffset;
+ mImageFormat = IMG_CODEC_JPEG;
+ mDataSize = local_size; // Only a complete .jpg file is valid
}
- if (mDataSize <= 0)
- {
- // no more data to read
- mDataSize = 0;
- return true;
- }
- mReadData = new U8[mDataSize];
- mBytesRead = -1;
- mBytesToRead = mDataSize;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
- new ReadResponder(mCache, mRequestHandle));
- return false;
}
- else
+ // Hmm... What about a targa file? (used for UI texture mostly)
+ if (local_size == 0)
{
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "Error reading file from local cache: " << local_filename
-// << " Bytes: " << mDataSize << " Offset: " << mOffset
-// << " / " << mDataSize << llendl;
- mDataSize = 0; // failed
- delete[] mReadData;
- mReadData = NULL;
- }
- return true;
- }
- else
+ local_filename = filename + ".tga";
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
+ if (local_size > 0)
{
- return false;
+ mImageFormat = IMG_CODEC_TGA;
+ mDataSize = local_size; // Only a complete .tga file is valid
}
}
-#else
+ // Determine the next stage: if we found a file, then LOCAL else CACHE
+ mState = (local_size > 0 ? LOCAL : CACHE);
+ }
+
+ // Second state / stage : if the file is local, load it and leave
+ if (!done && (mState == LOCAL))
+ {
+ llassert(local_size != 0); // we're assuming there is a non empty local file here...
if (!mDataSize || mDataSize > local_size)
{
mDataSize = local_size;
}
+ // Allocate read buffer
mReadData = new U8[mDataSize];
S32 bytes_read = LLAPRFile::readEx(local_filename,
mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool());
if (bytes_read != mDataSize)
{
-// llwarns << "Error reading file from local cache: " << local_filename
-// << " Bytes: " << mDataSize << " Offset: " << mOffset
-// << " / " << mDataSize << llendl;
+ llwarns << "Error reading file from local cache: " << local_filename
+ << " Bytes: " << mDataSize << " Offset: " << mOffset
+ << " / " << mDataSize << llendl;
mDataSize = 0;
delete[] mReadData;
mReadData = NULL;
@@ -406,405 +393,275 @@ bool LLTextureCacheRemoteWorker::doRead()
mImageSize = local_size;
mImageLocal = TRUE;
}
- return true;
-#endif
+ // We're done...
+ done = true;
}
- S32 idx = -1;
-
- if (mState == CACHE)
+ // Second state / stage : identify the cache or not...
+ if (!done && (mState == CACHE))
{
- llassert_always(mImageSize == 0);
- idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
- if (idx >= 0 && mImageSize > mOffset)
+ idx = mCache->getHeaderCacheEntry(mID, mImageSize);
+ if (idx < 0)
{
- llassert_always(mImageSize > 0);
- if (!mDataSize || mDataSize > mImageSize)
- {
- mDataSize = mImageSize;
- }
- mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+ // The texture is *not* cached. We're done here...
+ mDataSize = 0; // no data
+ done = true;
}
else
{
- mDataSize = 0; // no data
- return true;
+ // 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;
}
}
- if (mState == HEADER)
+ // Third state / stage : read data from the header cache (texture.entries) file
+ if (!done && (mState == HEADER))
{
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
- {
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- llassert_always(mReadData == NULL);
- mReadData = new U8[size];
- mBytesRead = -1;
- mBytesToRead = size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
- mReadData, offset, mBytesToRead,
- new ReadResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from header: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- return true;
- }
- if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
- {
- return true; // done
- }
- else
- {
- mFileHandle = LLLFSThread::nullHandle();
- mState = BODY;
- }
- }
- else
- {
- return false;
- }
- }
-#else
- llassert_always(idx >= 0);
+ llassert_always(idx >= 0); // we need an entry here or reading the header makes no sense
llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+ // Compute the size we need to read (in bytes)
S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+ size = llmin(size, mDataSize);
+ // Allocate the read buffer
mReadData = new U8[size];
S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName,
mReadData, offset, size, mCache->getLocalAPRFilePool());
if (bytes_read != size)
{
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from header: " << bytes_read
-// << " / " << size << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes read from header: " << bytes_read
+ << " / " << size << llendl;
+ delete[] mReadData;
+ mReadData = NULL;
mDataSize = -1; // failed
- return true;
+ done = true;
}
- if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
+ // If we already read all we expected, we're actually done
+ if (mDataSize <= bytes_read)
{
- return true; // done
+ done = true;
}
else
{
mState = BODY;
}
-#endif
}
- if (mState == BODY)
+ // Fourth state / stage : read the rest of the data from the UUID based cached file
+ if (!done && (mState == BODY))
{
-#if USE_LFS_READ
- if (mFileHandle == LLLFSThread::nullHandle())
- {
- std::string filename = mCache->getTextureFileName(mID);
- S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool());
- if (filesize > mOffset)
- {
- S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
- mDataSize = llmin(datasize, mDataSize);
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
-
- llassert_always(mDataSize > 0);
- U8* data = new U8[mDataSize];
- if (data_offset > 0)
- {
- llassert_always(mReadData);
- llassert_always(data_offset <= mDataSize);
- memcpy(data, mReadData, data_offset);
- delete[] mReadData;
- mReadData = NULL;
- }
- llassert_always(mReadData == NULL);
- mReadData = data;
-
- mBytesRead = -1;
- mBytesToRead = file_size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- llassert_always(data_offset + mBytesToRead <= mDataSize);
- mFileHandle = LLLFSThread::sLocal->read(filename,
- mReadData + data_offset, file_offset, mBytesToRead,
- new ReadResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
- return true; // done
- }
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from body: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- }
- return true;
- }
- else
- {
- return false;
- }
- }
-#else
std::string filename = mCache->getTextureFileName(mID);
S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool());
- S32 bytes_read = 0;
- if (filesize > mOffset)
+
+ if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset)
{
- S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
- mDataSize = llmin(datasize, mDataSize);
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
+ S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset;
+ mDataSize = llmin(max_datasize, mDataSize);
+
+ S32 data_offset, file_size, file_offset;
+ // Reserve the whole data buffer first
U8* data = new U8[mDataSize];
- if (data_offset > 0)
+
+ // Set the data file pointers taking the read offset into account. 2 cases:
+ if (mOffset < TEXTURE_CACHE_ENTRY_SIZE)
{
+ // Offset within the header record. That means we read something from the header cache.
+ // Note: most common case is (mOffset = 0), so this is the "normal" code path.
+ data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; // i.e. TEXTURE_CACHE_ENTRY_SIZE if mOffset nul (common case)
+ file_offset = 0;
+ file_size = mDataSize - data_offset;
+ // Copy the raw data we've been holding from the header cache into the new sized buffer
llassert_always(mReadData);
memcpy(data, mReadData, data_offset);
delete[] mReadData;
+ mReadData = NULL;
+ }
+ else
+ {
+ // Offset bigger than the header record. That means we haven't read anything yet.
+ data_offset = 0;
+ file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+ file_size = mDataSize;
+ // No data from header cache to copy in that case, we skipped it all
}
+
+ // Now use that buffer as the object read buffer
+ llassert_always(mReadData == NULL);
mReadData = data;
- bytes_read = LLAPRFile::readEx(filename,
+
+ // Read the data at last
+ S32 bytes_read = LLAPRFile::readEx(filename,
mReadData + data_offset,
file_offset, file_size,
mCache->getLocalAPRFilePool());
if (bytes_read != file_size)
{
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes read from body: " << bytes_read
-// << " / " << file_size << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes read from body: " << bytes_read
+ << " / " << file_size << llendl;
+ delete[] mReadData;
+ mReadData = NULL;
mDataSize = -1; // failed
- return true;
+ done = true;
}
}
else
{
- mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
- }
-
- return true;
-#endif
+ // No body, we're done.
+ mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0);
+ lldebugs << "No body file for: " << filename << llendl;
+ }
+ // Nothing else to do at that point...
+ done = true;
}
-
- return false;
+
+ // Clean up and exit
+ return done;
}
+// This is where *everything* about a texture is written down in the cache system (entry map, header and body)
+// Current assumption are:
+// - the whole data are in a raw form, starting at mWriteData
+// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache)
+// - the code *does not* support offset writing so there are no difference between buffer addresses and start of data
bool LLTextureCacheRemoteWorker::doWrite()
{
+ bool done = false;
S32 idx = -1;
- // No LOCAL state for write()
-
+ // 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...
+ mState = CACHE;
+ }
+
+ // No LOCAL state for write(): because it doesn't make much sense to cache a local file...
+
+ // Second state / stage : set an entry in the headers entry (texture.entries) file
+ if (!done && (mState == CACHE))
+ {
+ bool alreadyCached = false;
S32 cur_imagesize = 0;
- S32 offset = mOffset;
- idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
- if (idx >= 0 && cur_imagesize > 0)
+ // Checks if this image is already in the entry list
+ idx = mCache->getHeaderCacheEntry(mID, cur_imagesize);
+ if (idx >= 0 && (cur_imagesize >= 0))
{
- offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header
+ alreadyCached = true; // already there and non empty
}
- idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry
- if (idx >= 0)
+ idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry
+ if (idx < 0)
{
- if(cur_imagesize > 0 && mImageSize != cur_imagesize)
- {
-// llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl;
- offset = 0; // re-write header
- }
- mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " Unable to create header entry for writing!" << llendl;
+ mDataSize = -1; // failed
+ done = true;
}
else
{
- mDataSize = -1; // failed
- return true;
+ 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
+ done = true;
+ }
+ else
+ {
+ // If the texture has already been cached, we don't resave the header and go directly to the body part
+ mState = alreadyCached ? BODY : HEADER;
+ }
}
}
-
- if (mState == HEADER)
+
+ // Third stage / state : write the header record in the header file (texture.cache)
+ if (!done && (mState == HEADER))
{
-#if USE_LFS_WRITE
- if (mFileHandle == LLLFSThread::nullHandle())
+ llassert_always(idx >= 0); // we need an entry here or storing the header makes no sense
+ S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE; // skip to the correct spot in the header file
+ S32 size = TEXTURE_CACHE_ENTRY_SIZE; // record size is fixed for the header
+ S32 bytes_written;
+
+ if (mDataSize < TEXTURE_CACHE_ENTRY_SIZE)
{
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- mBytesRead = -1;
- mBytesToRead = size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
- mWriteData, offset, mBytesToRead,
- new WriteResponder(mCache, mRequestHandle));
- return false;
+ // We need to write a full record in the header cache so, if the amount of data is smaller
+ // than a record, we need to transfer the data to a buffer padded with 0 and write that
+ U8* padBuffer = new U8[TEXTURE_CACHE_ENTRY_SIZE];
+ memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros
+ memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool());
+ delete [] padBuffer;
}
else
{
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes written to header: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- return true;
- }
- if (mDataSize <= mBytesToRead)
- {
- return true; // done
- }
- else
- {
- mFileHandle = LLLFSThread::nullHandle();
- mState = BODY;
- }
- }
- else
- {
- return false;
- }
+ // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool());
}
-#else
- llassert_always(idx >= 0);
- llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
- S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
- S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- S32 bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool());
if (bytes_written <= 0)
{
-// llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " Unable to write header entry!" << llendl;
mDataSize = -1; // failed
- return true;
+ done = true;
}
- if (mDataSize <= size)
+ // If we wrote everything (may be more with padding) in the header cache,
+ // we're done so we don't have a body to store
+ if (mDataSize <= bytes_written)
{
- return true; // done
+ done = true;
}
else
{
mState = BODY;
}
-#endif
}
- if (mState == BODY)
+ // Fourth stage / state : write the body file, i.e. the rest of the texture in a "UUID" file name
+ if (!done && (mState == BODY))
{
-#if USE_LFS_WRITE
- if (mFileHandle == LLLFSThread::nullHandle())
+ 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))
{
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
- if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
- {
- std::string filename = mCache->getTextureFileName(mID);
- mBytesRead = -1;
- mBytesToRead = file_size;
- setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
- mFileHandle = LLLFSThread::sLocal->write(filename,
- mWriteData + data_offset, file_offset, mBytesToRead,
- new WriteResponder(mCache, mRequestHandle));
- return false;
- }
- else
- {
- mDataSize = 0; // no data written
- return true; // done
- }
- }
- else
- {
- if (mBytesRead >= 0)
- {
- if (mBytesRead != mBytesToRead)
- {
-// llwarns << "LLTextureCacheWorker: " << mID
-// << " incorrect number of bytes written to body: " << mBytesRead
-// << " != " << mBytesToRead << llendl;
- mDataSize = -1; // failed
- }
- return true;
- }
- else
- {
- return false;
- }
- }
-#else
- S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
- data_offset = llmax(data_offset, 0);
- S32 file_size = mDataSize - data_offset;
- S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
- file_offset = llmax(file_offset, 0);
- S32 bytes_written = 0;
- if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
- {
- std::string filename = mCache->getTextureFileName(mID);
-
- bytes_written = LLAPRFile::writeEx(filename,
- mWriteData + data_offset,
- file_offset, file_size,
- mCache->getLocalAPRFilePool());
+ // build the cache file name from the UUID
+ std::string filename = mCache->getTextureFileName(mID);
+// llinfos << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << llendl;
+ S32 bytes_written = LLAPRFile::writeEx( filename,
+ mWriteData + TEXTURE_CACHE_ENTRY_SIZE,
+ 0, file_size,
+ mCache->getLocalAPRFilePool());
if (bytes_written <= 0)
{
+ llwarns << "LLTextureCacheWorker: " << mID
+ << " incorrect number of bytes written to body: " << bytes_written
+ << " / " << file_size << llendl;
mDataSize = -1; // failed
+ done = true;
}
}
else
{
mDataSize = 0; // no data written
}
-
- return true;
-#endif
+ // Nothing else to do at that point...
+ done = true;
}
-
- return false;
+
+ // Clean up and exit
+ return done;
}
//virtual
bool LLTextureCacheWorker::doWork(S32 param)
{
-// *TODO reenable disabled apr_pool usage disabled due to maint-render-9 merge breakage -brad
- //allocate a new local apr_pool
-// LLAPRPool pool ;
-
- //save the current mFileAPRPool to avoid breaking anything.
-// apr_pool_t* old_pool = mCache->getFileAPRPool() ;
- //make mFileAPRPool to point to the local one
-// mCache->setFileAPRPool(pool.getAPRPool()) ;
-
bool res = false;
if (param == 0) // read
{
@@ -818,10 +675,6 @@ bool LLTextureCacheWorker::doWork(S32 param)
{
llassert_always(0);
}
-
- //set mFileAPRPool back, the local one will be released automatically.
-// mCache->setFileAPRPool(old_pool) ;
-
return res;
}
@@ -887,6 +740,7 @@ LLTextureCache::LLTextureCache(bool threaded)
mWorkersMutex(NULL),
mHeaderMutex(NULL),
mListMutex(NULL),
+ mHeaderAPRFile(NULL),
mReadOnly(FALSE),
mTexturesSizeTotal(0),
mDoPurge(FALSE)
@@ -926,6 +780,9 @@ S32 LLTextureCache::update(U32 max_time_ms)
}
}
+ unlockWorkers();
+
+ // call 'completed' with workers list unlocked (may call readComplete() or writeComplete()
for (responder_list_t::iterator iter1 = completed_list.begin();
iter1 != completed_list.end(); ++iter1)
{
@@ -934,8 +791,6 @@ S32 LLTextureCache::update(U32 max_time_ms)
responder->completed(success);
}
- unlockWorkers();
-
return res;
}
@@ -954,33 +809,48 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id)
{
std::string idstr = id.asString();
std::string delem = gDirUtilp->getDirDelimiter();
- std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
+ std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr + ".texture";
return filename;
}
-bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
+bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize)
{
bool res = false;
bool purge = false;
- // Append UUID to end of texture entries
{
LLMutexLock lock(&mHeaderMutex);
- size_map_t::iterator iter = mTexturesSizeMap.find(id);
- if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
+ size_map_t::iterator iter1 = mTexturesSizeMap.find(id);
+ if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize)
{
llassert_always(bodysize > 0);
- Entry* entry = new Entry(id, bodysize, time(NULL));
- LLAPRFile::writeEx(mTexturesDirEntriesFileName,
- (U8*)entry, -1, 1*sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- if (iter != mTexturesSizeMap.end())
+ S32 oldbodysize = 0;
+ if (iter1 != mTexturesSizeMap.end())
+ {
+ oldbodysize = iter1->second;
+ }
+
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
+ if (idx < 0)
+ {
+ // TODO: change to llwarns
+ llerrs << "Failed to open entry: " << id << llendl;
+ removeFromCache(id);
+ return false;
+ }
+ else if (oldbodysize != entry.mBodySize)
{
- mTexturesSizeTotal -= iter->second;
+ // 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;
- mTexturesSizeMap[id] = bodysize;
+
if (mTexturesSizeTotal > sCacheMaxTexturesSize)
{
purge = true;
@@ -995,11 +865,59 @@ bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
return res;
}
+//debug
+BOOL LLTextureCache::isInCache(const LLUUID& id)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ id_map_t::const_iterator iter = mHeaderIDMap.find(id);
+
+ return (iter != mHeaderIDMap.end()) ;
+}
+
+//debug
+BOOL LLTextureCache::isInLocal(const LLUUID& id)
+{
+ S32 local_size = 0;
+ std::string local_filename;
+
+ std::string filename = getLocalFileName(id);
+ // Is it a JPEG2000 file?
+ {
+ local_filename = filename + ".j2c";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ // If not, is it a jpeg file?
+ {
+ local_filename = filename + ".jpg";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ // Hmm... What about a targa file? (used for UI texture mostly)
+ {
+ local_filename = filename + ".tga";
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
+ if (local_size > 0)
+ {
+ return TRUE ;
+ }
+ }
+
+ return FALSE ;
+}
//////////////////////////////////////////////////////////////////////////////
//static
const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB
-F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
+F32 LLTextureCache::sHeaderCacheVersion = 1.3f;
U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
const char* entries_filename = "texture.entries";
@@ -1012,7 +930,6 @@ void LLTextureCache::setDirNames(ELLPath location)
mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
- mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
}
void LLTextureCache::purgeCache(ELLPath location)
@@ -1020,7 +937,7 @@ void LLTextureCache::purgeCache(ELLPath location)
if (!mReadOnly)
{
setDirNames(location);
-
+ llassert_always(mHeaderAPRFile == NULL);
LLAPRFile::remove(mHeaderEntriesFileName, getLocalAPRFilePool());
LLAPRFile::remove(mHeaderDataFileName, getLocalAPRFilePool());
}
@@ -1063,83 +980,317 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
return max_size; // unused cache space
}
-struct lru_data
+//----------------------------------------------------------------------------
+// mHeaderMutex must be locked for the following functions!
+
+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);
+ return mHeaderAPRFile;
+}
+
+void LLTextureCache::closeHeaderEntriesFile()
+{
+ llassert_always(mHeaderAPRFile != NULL);
+ delete mHeaderAPRFile;
+ mHeaderAPRFile = NULL;
+}
+
+void LLTextureCache::readEntriesHeader()
+{
+ // mHeaderEntriesInfo initializes to default values so safe not to read it
+ llassert_always(mHeaderAPRFile == NULL);
+ if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool()))
+ {
+ LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ getLocalAPRFilePool());
+ }
+}
+
+void LLTextureCache::writeEntriesHeader()
{
- lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
- U32 time;
- S32 index;
- LLUUID uuid;
- struct Compare
- {
- // lhs < rhs
- typedef const lru_data* lru_data_ptr;
- bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
+ llassert_always(mHeaderAPRFile == NULL);
+ if (!mReadOnly)
+ {
+ LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ getLocalAPRFilePool());
+ }
+}
+
+static S32 mHeaderEntriesMaxWriteIdx = 0;
+
+S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create)
+{
+ S32 idx = -1;
+
+ id_map_t::iterator iter1 = mHeaderIDMap.find(id);
+ if (iter1 != mHeaderIDMap.end())
+ {
+ idx = iter1->second;
+ }
+
+ if (idx < 0)
+ {
+ if (create && !mReadOnly)
{
- if(a->time == b->time)
- return (a->index < b->index);
+ if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
+ {
+ // Add an entry to the end of the list
+ idx = mHeaderEntriesInfo.mEntries++;
+
+ }
+ else if (!mFreeList.empty())
+ {
+ idx = *(mFreeList.begin());
+ mFreeList.erase(mFreeList.begin());
+ }
else
- return (a->time >= b->time);
+ {
+ // Look for a still valid entry in the LRU
+ for (std::set<LLUUID>::iterator iter2 = mLRU.begin(); iter2 != mLRU.end();)
+ {
+ std::set<LLUUID>::iterator curiter2 = iter2++;
+ LLUUID oldid = *curiter2;
+ // Erase entry from LRU regardless
+ mLRU.erase(curiter2);
+ // Look up entry and use it if it is valid
+ id_map_t::iterator iter3 = mHeaderIDMap.find(oldid);
+ if (iter3 != mHeaderIDMap.end() && iter3->second >= 0)
+ {
+ idx = iter3->second;
+ mHeaderIDMap.erase(oldid);
+ mTexturesSizeMap.erase(oldid);
+ break;
+ }
+ }
+ // if (idx < 0) at this point, we will rebuild the LRU
+ // and retry if called from setHeaderCacheEntry(),
+ // otherwise this shouldn't happen and will trigger an error
+ }
+ 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();
+ }
}
- };
-};
+ }
+ else
+ {
+ // 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();
+ }
+ return idx;
+}
+
+void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry)
+{
+ 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();
+ }
+ }
+}
+
+U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
+{
+ U32 num_entries = mHeaderEntriesInfo.mEntries;
+
+ mHeaderIDMap.clear();
+ mTexturesSizeMap.clear();
+ mFreeList.clear();
+ mTexturesSizeTotal = 0;
+
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
+ for (U32 idx=0; idx<num_entries; idx++)
+ {
+ Entry entry;
+ S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry));
+ if (bytes_read < sizeof(Entry))
+ {
+ llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl;
+ closeHeaderEntriesFile();
+ purgeAllTextures(false);
+ return 0;
+ }
+ entries.push_back(entry);
+// llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl;
+ if (entry.mImageSize < 0)
+ {
+ mFreeList.insert(idx);
+ }
+ 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);
+ }
+ }
+ closeHeaderEntriesFile();
+ return num_entries;
+}
+
+void LLTextureCache::writeEntriesAndClose(const std::vector<Entry>& entries)
+{
+ S32 num_entries = entries.size();
+ llassert_always(num_entries == mHeaderEntriesInfo.mEntries);
+
+ if (!mReadOnly)
+ {
+ LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
+ 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));
+ }
+ mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1);
+ closeHeaderEntriesFile();
+ }
+}
+
+//----------------------------------------------------------------------------
// Called from either the main thread or the worker thread
void LLTextureCache::readHeaderCache()
{
LLMutexLock lock(&mHeaderMutex);
- mHeaderEntriesInfo.mVersion = 0.f;
- mHeaderEntriesInfo.mEntries = 0;
- if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool()))
- {
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
- }
+
+ mLRU.clear(); // always clear the LRU
+
+ readEntriesHeader();
+
if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
{
if (!mReadOnly)
{
- // Info with 0 entries
- mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
-
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
+ purgeAllTextures(false);
}
}
else
{
- S32 num_entries = mHeaderEntriesInfo.mEntries;
+ std::vector<Entry> entries;
+ U32 num_entries = openAndReadEntries(entries);
if (num_entries)
{
- Entry* entries = new Entry[num_entries];
+ U32 empty_entries = 0;
+ typedef std::pair<U32, LLUUID> lru_data_t;
+ std::set<lru_data_t> lru;
+ std::vector<LLUUID> purge_list;
+ for (U32 i=0; i<num_entries; i++)
+ {
+ Entry& entry = entries[i];
+ const LLUUID& id = entry.mID;
+ if (entry.mImageSize < 0)
+ {
+ // This will be in the Free List, don't put it in the LRY
+ ++empty_entries;
+ }
+ else
+ {
+ lru.insert(std::make_pair(entry.mTime, id));
+ if (entry.mBodySize > 0)
+ {
+ if (entry.mBodySize > entry.mImageSize)
+ {
+ // Shouldn't happen, failsafe only
+ llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl;
+ purge_list.push_back(id);
+ }
+ }
+ }
+ }
+ if (num_entries > sCacheMaxEntries)
+ {
+ // Special case: cache size was reduced, need to remove entries
+ // Note: After we prune entries, we will call this again and create the LRU
+ U32 entries_to_purge = (num_entries-empty_entries) - sCacheMaxEntries;
+ if (entries_to_purge > 0)
+ {
+ for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ {
+ purge_list.push_back(iter->second);
+ if (--entries_to_purge <= 0)
+ break;
+ }
+ }
+ }
+ else
{
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry),
- getLocalAPRFilePool());
+ S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE);
+ for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ {
+ mLRU.insert(iter->second);
+// llinfos << "LRU: " << iter->first << " : " << iter->second << llendl;
+ if (--lru_entries <= 0)
+ break;
+ }
}
- typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
- lru_set_t lru;
- for (S32 i=0; i<num_entries; i++)
+
+ if (purge_list.size() > 0)
{
- if (entries[i].mSize >= 0) // -1 indicates erased entry, skip
+ for (std::vector<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter)
{
- const LLUUID& id = entries[i].mID;
- lru.insert(new lru_data(entries[i].mTime, i, id));
- mHeaderIDMap[id] = i;
+ removeFromCache(*iter);
}
+ // If we removed any entries, we need to rebuild the entries list,
+ // write the header, and call this again
+ std::vector<Entry> new_entries;
+ for (U32 i=0; i<num_entries; i++)
+ {
+ const Entry& entry = entries[i];
+ if (entry.mImageSize >=0)
+ {
+ new_entries.push_back(entry);
+ }
+ }
+ llassert_always(new_entries.size() <= sCacheMaxEntries);
+ mHeaderEntriesInfo.mEntries = new_entries.size();
+ writeEntriesAndClose(new_entries);
+ readHeaderCache(); // repeat with new entries file
}
- mLRU.clear();
- S32 lru_entries = sCacheMaxEntries / 10;
- for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+ else
{
- lru_data* data = *iter;
- mLRU[data->index] = data->uuid;
- if (--lru_entries <= 0)
- break;
+ writeEntriesAndClose(entries);
}
- for_each(lru.begin(), lru.end(), DeletePointer());
- delete[] entries;
}
}
}
@@ -1162,13 +1313,21 @@ void LLTextureCache::purgeAllTextures(bool purge_directories)
LLFile::rmdir(dirname);
}
}
- LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool());
if (purge_directories)
{
LLFile::rmdir(mTexturesDirName);
}
}
+ mHeaderIDMap.clear();
mTexturesSizeMap.clear();
+ mTexturesSizeTotal = 0;
+ mFreeList.clear();
+ mTexturesSizeTotal = 0;
+
+ // Info with 0 entries
+ mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
+ mHeaderEntriesInfo.mEntries = 0;
+ writeEntriesHeader();
}
void LLTextureCache::purgeTextures(bool validate)
@@ -1182,51 +1341,37 @@ void LLTextureCache::purgeTextures(bool validate)
LLAppViewer::instance()->pauseMainloopTimeout();
LLMutexLock lock(&mHeaderMutex);
-
- S32 filesize = LLAPRFile::size(mTexturesDirEntriesFileName, getLocalAPRFilePool());
- S32 num_entries = filesize / sizeof(Entry);
- if (num_entries * (S32)sizeof(Entry) != filesize)
- {
- LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
- purgeAllTextures(false);
- return;
- }
- if (num_entries == 0)
+
+ llinfos << "TEXTURE CACHE: Purging." << llendl;
+
+ // Read the entries list
+ std::vector<Entry> entries;
+ U32 num_entries = openAndReadEntries(entries);
+ if (!num_entries)
{
- return; // nothing to do
+ writeEntriesAndClose(entries);
+ return; // nothing to purge
}
- Entry* entries = new Entry[num_entries];
- S32 bytes_read = LLAPRFile::readEx(mTexturesDirEntriesFileName,
- (U8*)entries, 0, num_entries*sizeof(Entry),
- getLocalAPRFilePool());
- if (bytes_read != filesize)
- {
- LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
- purgeAllTextures(false);
- return;
- }
-
- LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading Entries..." << LL_ENDL;
-
- std::map<LLUUID, S32> entry_idx_map;
- S64 total_size = 0;
- for (S32 idx=0; idx<num_entries; idx++)
- {
- const LLUUID& id = entries[idx].mID;
- LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL;
- std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
- if (iter != entry_idx_map.end())
+ // Use mTexturesSizeMap to collect UUIDs of textures with bodies
+ typedef std::set<std::pair<U32,S32> > time_idx_set_t;
+ std::set<std::pair<U32,S32> > time_idx_set;
+ for (size_map_t::iterator iter1 = mTexturesSizeMap.begin();
+ iter1 != mTexturesSizeMap.end(); ++iter1)
+ {
+ if (iter1->second > 0)
{
- // Newer entry replacing older entry
- S32 pidx = iter->second;
- total_size -= entries[pidx].mSize;
- entries[pidx].mSize = 0; // flag: skip older entry
+ id_map_t::iterator iter2 = mHeaderIDMap.find(iter1->first);
+ if (iter2 != mHeaderIDMap.end())
+ {
+ S32 idx = iter2->second;
+ 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;
+ }
}
- entry_idx_map[id] = idx;
- total_size += entries[idx].mSize;
}
-
+
+ // Validate 1/256th of the files on startup
U32 validate_idx = 0;
if (validate)
{
@@ -1235,19 +1380,17 @@ void LLTextureCache::purgeTextures(bool validate)
gSavedSettings.setU32("CacheValidateCounter", next_idx);
LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL;
}
-
- S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
+
+ S64 cache_size = mTexturesSizeTotal;
+ S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100;
S32 purge_count = 0;
- S32 next_idx = 0;
- for (S32 idx=0; idx<num_entries; idx++)
+ for (time_idx_set_t::iterator iter = time_idx_set.begin();
+ iter != time_idx_set.end(); ++iter)
{
- if (entries[idx].mSize == 0)
- {
- continue;
- }
+ S32 idx = iter->second;
bool purge_entry = false;
std::string filename = getTextureFileName(entries[idx].mID);
- if (total_size >= min_cache_size)
+ if (cache_size >= purged_cache_size)
{
purge_entry = true;
}
@@ -1257,60 +1400,44 @@ void LLTextureCache::purgeTextures(bool validate)
U32 uuididx = entries[idx].mID.mData[0];
if (uuididx == validate_idx)
{
- LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL;
+ LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL;
S32 bodysize = LLAPRFile::size(filename, getLocalAPRFilePool());
- if (bodysize != entries[idx].mSize)
+ if (bodysize != entries[idx].mBodySize)
{
- LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
+ LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize
<< filename << LL_ENDL;
purge_entry = true;
}
}
}
+ else
+ {
+ break;
+ }
+
if (purge_entry)
{
purge_count++;
LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL;
LLAPRFile::remove(filename, getLocalAPRFilePool());
- total_size -= entries[idx].mSize;
- entries[idx].mSize = 0;
- }
- else
- {
- if (next_idx != idx)
- {
- entries[next_idx] = entries[idx];
- }
- ++next_idx;
+ cache_size -= entries[idx].mBodySize;
+ mTexturesSizeTotal -= entries[idx].mBodySize;
+ entries[idx].mBodySize = 0;
+ mTexturesSizeMap.erase(entries[idx].mID);
}
}
- num_entries = next_idx;
LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL;
-
- LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool());
- LLAPRFile::writeEx(mTexturesDirEntriesFileName,
- (U8*)&entries[0], 0, num_entries*sizeof(Entry),
- getLocalAPRFilePool());
-
- mTexturesSizeTotal = 0;
- mTexturesSizeMap.clear();
- for (S32 idx=0; idx<num_entries; idx++)
- {
- mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
- mTexturesSizeTotal += entries[idx].mSize;
- }
- llassert(mTexturesSizeTotal == total_size);
-
- delete[] entries;
+ writeEntriesAndClose(entries);
+
// *FIX:Mani - watchdog back on.
LLAppViewer::instance()->resumeMainloopTimeout();
LL_INFOS("TextureCache") << "TEXTURE CACHE:"
<< " PURGED: " << purge_count
<< " ENTRIES: " << num_entries
- << " CACHE SIZE: " << total_size / 1024*1024 << " MB"
+ << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB"
<< llendl;
}
@@ -1340,78 +1467,39 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
}
//////////////////////////////////////////////////////////////////////////////
-
// Called from work thread
-S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
-{
- bool retry = false;
- S32 idx = -1;
+// Reads imagesize from the header, updates timestamp
+S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
+ if (idx >= 0)
{
- LLMutexLock lock(&mHeaderMutex);
- id_map_t::iterator iter = mHeaderIDMap.find(id);
- if (iter != mHeaderIDMap.end())
- {
- idx = iter->second;
- }
- else if (touch && !mReadOnly)
- {
- if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
- {
- // Add an entry
- idx = mHeaderEntriesInfo.mEntries++;
- mHeaderIDMap[id] = idx;
- // Update Info
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
- getLocalAPRFilePool());
- }
- else if (!mLRU.empty())
- {
- idx = mLRU.begin()->first; // will be erased below
- const LLUUID& oldid = mLRU.begin()->second;
- mHeaderIDMap.erase(oldid);
- mTexturesSizeMap.erase(oldid);
- mHeaderIDMap[id] = idx;
- }
- else
- {
- idx = -1;
- retry = true;
- }
- }
- if (idx >= 0)
- {
- if (touch && !mReadOnly)
- {
- // Update the lru entry
- mLRU.erase(idx);
- llassert_always(imagesize && *imagesize > 0);
- Entry* entry = new Entry(id, *imagesize, time(NULL));
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- }
- else if (imagesize)
- {
- // Get the image size
- Entry entry;
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+ imagesize = entry.mImageSize;
+ writeEntryAndClose(idx, entry); // updates time
+ }
+ return idx;
+}
- LLAPRFile::readEx(mHeaderEntriesFileName,
- (U8*)&entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- *imagesize = entry.mSize;
- }
- }
+// Writes imagesize to the header, updates timestamp
+S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize)
+{
+ LLMutexLock lock(&mHeaderMutex);
+ llassert_always(imagesize >= 0);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, true);
+ if (idx >= 0)
+ {
+ entry.mImageSize = imagesize;
+ writeEntryAndClose(idx, entry);
}
- if (retry)
+ else // retry
{
- readHeaderCache(); // updates the lru
+ readHeaderCache(); // We couldn't write an entry, so refresh the LRU
llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
- idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion
+ idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion
}
return idx;
}
@@ -1427,8 +1515,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const std::string& filena
// so let the thread handle it
LLMutexLock lock(&mWorkersMutex);
LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id,
- NULL, size, offset, 0,
- responder);
+ NULL, size, offset, 0,
+ responder);
handle_t handle = worker->read();
mReaders[handle] = worker;
return handle;
@@ -1441,8 +1529,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri
// so let the thread handle it
LLMutexLock lock(&mWorkersMutex);
LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
- NULL, size, offset, 0,
- responder);
+ NULL, size, offset,
+ 0, responder);
handle_t handle = worker->read();
mReaders[handle] = worker;
return handle;
@@ -1453,7 +1541,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort)
{
lockWorkers();
handle_map_t::iterator iter = mReaders.find(handle);
- llassert_always(iter != mReaders.end());
+ llassert_always(iter != mReaders.end() || abort);
LLTextureCacheWorker* worker = iter->second;
bool res = worker->complete();
if (res || abort)
@@ -1487,19 +1575,13 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio
purgeTextures(false);
mDoPurge = FALSE;
}
- if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
- {
- LLMutexLock lock(&mWorkersMutex);
- llassert_always(imagesize > 0);
- LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
- data, datasize, 0,
- imagesize, responder);
- handle_t handle = worker->write();
- mWriters[handle] = worker;
- return handle;
- }
- delete responder;
- return LLWorkerThread::nullHandle();
+ LLMutexLock lock(&mWorkersMutex);
+ LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
+ data, datasize, 0,
+ imagesize, responder);
+ handle_t handle = worker->write();
+ mWriters[handle] = worker;
+ return handle;
}
bool LLTextureCache::writeComplete(handle_t handle, bool abort)
@@ -1542,25 +1624,17 @@ void LLTextureCache::addCompleted(Responder* responder, bool success)
bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
{
- if (mReadOnly)
- {
- return false;
- }
- LLMutexLock lock(&mHeaderMutex);
- id_map_t::iterator iter = mHeaderIDMap.find(id);
- if (iter != mHeaderIDMap.end())
+ if (!mReadOnly)
{
- S32 idx = iter->second;
+ LLMutexLock lock(&mHeaderMutex);
+ Entry entry;
+ S32 idx = openAndReadEntry(id, entry, false);
if (idx >= 0)
{
- Entry* entry = new Entry(id, -1, time(NULL));
- S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
-
- LLAPRFile::writeEx(mHeaderEntriesFileName,
- (U8*)entry, offset, sizeof(Entry),
- getLocalAPRFilePool());
- delete entry;
- mLRU[idx] = id;
+ entry.mImageSize = -1;
+ entry.mBodySize = 0;
+ writeEntryAndClose(idx, entry);
+ mFreeList.insert(idx);
mHeaderIDMap.erase(id);
mTexturesSizeMap.erase(id);
return true;