diff options
Diffstat (limited to 'indra/newview/lltexturecache.cpp')
-rw-r--r-- | indra/newview/lltexturecache.cpp | 1286 |
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; |