lltexturecache.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "lltexturecache.h"
00035 
00036 #include "llapr.h"
00037 #include "lldir.h"
00038 #include "llimage.h"
00039 #include "lllfsthread.h"
00040 #include "llviewercontrol.h"
00041 
00042 #define USE_LFS_READ 0
00043 #define USE_LFS_WRITE 0
00044 
00045 // Note: first 4 bytes store file size, rest is j2c data
00046 const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024;
00047 
00048 class LLTextureCacheWorker : public LLWorkerClass
00049 {
00050         friend class LLTextureCache;
00051 
00052 private:
00053         class ReadResponder : public LLLFSThread::Responder
00054         {
00055         public:
00056                 ReadResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
00057                 ~ReadResponder() {}
00058                 void completed(S32 bytes)
00059                 {
00060                         mCache->lockWorkers();
00061                         LLTextureCacheWorker* reader = mCache->getReader(mHandle);
00062                         if (reader) reader->ioComplete(bytes);
00063                         mCache->unlockWorkers();
00064                 }
00065                 LLTextureCache* mCache;
00066                 LLTextureCacheWorker::handle_t mHandle;
00067         };
00068 
00069         class WriteResponder : public LLLFSThread::Responder
00070         {
00071         public:
00072                 WriteResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
00073                 ~WriteResponder() {}
00074                 void completed(S32 bytes)
00075                 {
00076                         mCache->lockWorkers();
00077                         LLTextureCacheWorker* writer = mCache->getWriter(mHandle);
00078                         if (writer) writer->ioComplete(bytes);
00079                         mCache->unlockWorkers();
00080                 }
00081                 LLTextureCache* mCache;
00082                 LLTextureCacheWorker::handle_t mHandle;
00083         };
00084         
00085 public:
00086         LLTextureCacheWorker(LLTextureCache* cache, U32 priority, const LLUUID& id,
00087                                                  U8* data, S32 datasize, S32 offset,
00088                                                  S32 imagesize, // for writes
00089                                                  LLTextureCache::Responder* responder)
00090                 : LLWorkerClass(cache, "LLTextureCacheWorker"),
00091                   mID(id),
00092                   mCache(cache),
00093                   mPriority(priority),
00094                   mReadData(NULL),
00095                   mWriteData(data),
00096                   mDataSize(datasize),
00097                   mOffset(offset),
00098                   mImageSize(imagesize),
00099                   mImageFormat(IMG_CODEC_J2C),
00100                   mImageLocal(FALSE),
00101                   mResponder(responder),
00102                   mFileHandle(LLLFSThread::nullHandle()),
00103                   mBytesToRead(0),
00104                   mBytesRead(0)
00105         {
00106                 mPriority &= LLWorkerThread::PRIORITY_LOWBITS;
00107         }
00108         ~LLTextureCacheWorker()
00109         {
00110                 llassert_always(!haveWork());
00111                 delete[] mReadData;
00112         }
00113 
00114         // override this interface
00115         virtual bool doRead() = 0;
00116         virtual bool doWrite() = 0;
00117 
00118         virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
00119 
00120         handle_t read() { addWork(0, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
00121         handle_t write() { addWork(1, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
00122         bool complete() { return checkWork(); }
00123         void ioComplete(S32 bytes)
00124         {
00125                 mBytesRead = bytes;
00126                 setPriority(LLWorkerThread::PRIORITY_HIGH | mPriority);
00127         }
00128 
00129 private:
00130         virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
00131         virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
00132         virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
00133 
00134 protected:
00135         LLTextureCache* mCache;
00136         U32 mPriority;
00137         LLUUID  mID;
00138         
00139         U8* mReadData;
00140         U8* mWriteData;
00141         S32 mDataSize;
00142         S32 mOffset;
00143         S32 mImageSize;
00144         EImageCodec mImageFormat;
00145         BOOL mImageLocal;
00146         LLPointer<LLTextureCache::Responder> mResponder;
00147         LLLFSThread::handle_t mFileHandle;
00148         S32 mBytesToRead;
00149         LLAtomicS32 mBytesRead;
00150 };
00151 
00152 class LLTextureCacheLocalFileWorker : public LLTextureCacheWorker
00153 {
00154 public:
00155         LLTextureCacheLocalFileWorker(LLTextureCache* cache, U32 priority, const LLString& filename, const LLUUID& id,
00156                                                  U8* data, S32 datasize, S32 offset,
00157                                                  S32 imagesize, // for writes
00158                                                  LLTextureCache::Responder* responder) 
00159                         : LLTextureCacheWorker(cache, priority, id, data, datasize, offset, imagesize, responder),
00160                         mFileName(filename)
00161 
00162         {
00163         }
00164 
00165         virtual bool doRead();
00166         virtual bool doWrite();
00167         
00168 private:
00169         LLString        mFileName;
00170 };
00171 
00172 bool LLTextureCacheLocalFileWorker::doRead()
00173 {
00174         S32 local_size = ll_apr_file_size(mFileName, mCache->getFileAPRPool());
00175 
00176         if (local_size > 0 && mFileName.size() > 4)
00177         {
00178                 mDataSize = local_size; // Only a complete file is valid
00179 
00180                 LLString extension = mFileName.substr(mFileName.size() - 3, 3);
00181 
00182                 mImageFormat = LLImageBase::getCodecFromExtension(extension);
00183 
00184                 if (mImageFormat == IMG_CODEC_INVALID)
00185                 {
00186                         llwarns << "Unrecognized file extension " << extension << " for local texture " << mFileName << llendl;
00187                         mDataSize = 0; // no data
00188                         return true;
00189                 }
00190         }
00191         else
00192         {
00193                 // file doesn't exist
00194                 mDataSize = 0; // no data
00195                 return true;
00196         }
00197 
00198 #if USE_LFS_READ
00199         if (mFileHandle == LLLFSThread::nullHandle())
00200         {
00201                 mImageLocal = TRUE;
00202                 mImageSize = local_size;
00203                 if (!mDataSize || mDataSize + mOffset > local_size)
00204                 {
00205                         mDataSize = local_size - mOffset;
00206                 }
00207                 if (mDataSize <= 0)
00208                 {
00209                         // no more data to read
00210                         mDataSize = 0;
00211                         return true;
00212                 }
00213                 mReadData = new U8[mDataSize];
00214                 mBytesRead = -1;
00215                 mBytesToRead = mDataSize;
00216                 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00217                 mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
00218                                                                                                 new ReadResponder(mCache, mRequestHandle));
00219                 return false;
00220         }
00221         else
00222         {
00223                 if (mBytesRead >= 0)
00224                 {
00225                         if (mBytesRead != mBytesToRead)
00226                         {
00227                                 llwarns << "Error reading file from local cache: " << local_filename
00228                                                 << " Bytes: " << mDataSize << " Offset: " << mOffset
00229                                                 << " / " << mDataSize << llendl;
00230                                 mDataSize = 0; // failed
00231                                 delete[] mReadData;
00232                                 mReadData = NULL;
00233                         }
00234                         return true;
00235                 }
00236                 else
00237                 {
00238                         return false;
00239                 }
00240         }
00241 #else
00242         if (!mDataSize || mDataSize > local_size)
00243         {
00244                 mDataSize = local_size;
00245         }
00246         mReadData = new U8[mDataSize];
00247         S32 bytes_read = ll_apr_file_read_ex(mFileName, mCache->getFileAPRPool(),
00248                                                                                  mReadData, mOffset, mDataSize);
00249         if (bytes_read != mDataSize)
00250         {
00251                 llwarns << "Error reading file from local cache: " << mFileName
00252                                 << " Bytes: " << mDataSize << " Offset: " << mOffset
00253                                 << " / " << mDataSize << llendl;
00254                 mDataSize = 0;
00255                 delete[] mReadData;
00256                 mReadData = NULL;
00257         }
00258         else
00259         {
00260                 mImageSize = local_size;
00261                 mImageLocal = TRUE;
00262         }
00263         return true;
00264 #endif
00265 }
00266 
00267 bool LLTextureCacheLocalFileWorker::doWrite()
00268 {
00269         // no writes for local files
00270         return false;
00271 }
00272 
00273 class LLTextureCacheRemoteWorker : public LLTextureCacheWorker
00274 {
00275 public:
00276         LLTextureCacheRemoteWorker(LLTextureCache* cache, U32 priority, const LLUUID& id,
00277                                                  U8* data, S32 datasize, S32 offset,
00278                                                  S32 imagesize, // for writes
00279                                                  LLTextureCache::Responder* responder) 
00280                         : LLTextureCacheWorker(cache, priority, id, data, datasize, offset, imagesize, responder),
00281                         mState(INIT)
00282         {
00283         }
00284 
00285         virtual bool doRead();
00286         virtual bool doWrite();
00287 
00288 private:
00289         enum e_state
00290         {
00291                 INIT = 0,
00292                 LOCAL = 1,
00293                 CACHE = 2,
00294                 HEADER = 3,
00295                 BODY = 4
00296         };
00297 
00298         e_state mState;
00299 };
00300 
00301 
00302 //virtual
00303 void LLTextureCacheWorker::startWork(S32 param)
00304 {
00305 }
00306 
00307 bool LLTextureCacheRemoteWorker::doRead()
00308 {
00309         S32 local_size = 0;
00310         std::string local_filename;
00311         
00312         if (mState == INIT)
00313         {
00314                 std::string filename = mCache->getLocalFileName(mID);   
00315                 local_filename = filename + ".j2c";
00316                 local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
00317                 if (local_size == 0)
00318                 {
00319                         local_filename = filename + ".tga";
00320                         local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
00321                         if (local_size > 0)
00322                         {
00323                                 mImageFormat = IMG_CODEC_TGA;
00324                                 mDataSize = local_size; // Only a complete .tga file is valid
00325                         }
00326                 }
00327                 if (local_size > 0)
00328                 {
00329                         mState = LOCAL;
00330                 }
00331                 else
00332                 {
00333                         mState = CACHE;
00334                 }
00335         }
00336 
00337         if (mState == LOCAL)
00338         {
00339 #if USE_LFS_READ
00340                 if (mFileHandle == LLLFSThread::nullHandle())
00341                 {
00342                         mImageLocal = TRUE;
00343                         mImageSize = local_size;
00344                         if (!mDataSize || mDataSize + mOffset > local_size)
00345                         {
00346                                 mDataSize = local_size - mOffset;
00347                         }
00348                         if (mDataSize <= 0)
00349                         {
00350                                 // no more data to read
00351                                 mDataSize = 0;
00352                                 return true;
00353                         }
00354                         mReadData = new U8[mDataSize];
00355                         mBytesRead = -1;
00356                         mBytesToRead = mDataSize;
00357                         setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00358                         mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
00359                                                                                                         new ReadResponder(mCache, mRequestHandle));
00360                         return false;
00361                 }
00362                 else
00363                 {
00364                         if (mBytesRead >= 0)
00365                         {
00366                                 if (mBytesRead != mBytesToRead)
00367                                 {
00368                                         llwarns << "Error reading file from local cache: " << local_filename
00369                                                         << " Bytes: " << mDataSize << " Offset: " << mOffset
00370                                                         << " / " << mDataSize << llendl;
00371                                         mDataSize = 0; // failed
00372                                         delete[] mReadData;
00373                                         mReadData = NULL;
00374                                 }
00375                                 return true;
00376                         }
00377                         else
00378                         {
00379                                 return false;
00380                         }
00381                 }
00382 #else
00383                 if (!mDataSize || mDataSize > local_size)
00384                 {
00385                         mDataSize = local_size;
00386                 }
00387                 mReadData = new U8[mDataSize];
00388                 S32 bytes_read = ll_apr_file_read_ex(local_filename, mCache->getFileAPRPool(),
00389                                                                                          mReadData, mOffset, mDataSize);
00390                 if (bytes_read != mDataSize)
00391                 {
00392                         llwarns << "Error reading file from local cache: " << local_filename
00393                                         << " Bytes: " << mDataSize << " Offset: " << mOffset
00394                                         << " / " << mDataSize << llendl;
00395                         mDataSize = 0;
00396                         delete[] mReadData;
00397                         mReadData = NULL;
00398                 }
00399                 else
00400                 {
00401                         mImageSize = local_size;
00402                         mImageLocal = TRUE;
00403                 }
00404                 return true;
00405 #endif
00406         }
00407 
00408         S32 idx = -1;
00409         
00410         if (mState == CACHE)
00411         {
00412                 llassert_always(mImageSize == 0);
00413                 idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
00414                 if (idx >= 0 && mImageSize > mOffset)
00415                 {
00416                         llassert_always(mImageSize > 0);
00417                         if (!mDataSize || mDataSize > mImageSize)
00418                         {
00419                                 mDataSize = mImageSize;
00420                         }
00421                         mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
00422                 }
00423                 else
00424                 {
00425                         mDataSize = 0; // no data
00426                         return true;
00427                 }
00428         }
00429 
00430         if (mState == HEADER)
00431         {
00432 #if USE_LFS_READ
00433                 if (mFileHandle == LLLFSThread::nullHandle())
00434                 {
00435                         llassert_always(idx >= 0);
00436                         llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00437                         S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00438                         S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00439                         llassert_always(mReadData == NULL);
00440                         mReadData = new U8[size];
00441                         mBytesRead = -1;
00442                         mBytesToRead = size;
00443                         setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00444                         mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
00445                                                                                                         mReadData, offset, mBytesToRead,
00446                                                                                                         new ReadResponder(mCache, mRequestHandle));
00447                         return false;
00448                 }
00449                 else
00450                 {
00451                         if (mBytesRead >= 0)
00452                         {
00453                                 if (mBytesRead != mBytesToRead)
00454                                 {
00455                                         llwarns << "LLTextureCacheWorker: "  << mID
00456                                                         << " incorrect number of bytes read from header: " << mBytesRead
00457                                                         << " != " << mBytesToRead << llendl;
00458                                         mDataSize = -1; // failed
00459                                         return true;
00460                                 }
00461                                 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
00462                                 {
00463                                         return true; // done
00464                                 }
00465                                 else
00466                                 {
00467                                         mFileHandle = LLLFSThread::nullHandle();
00468                                         mState = BODY;
00469                                 }
00470                         }
00471                         else
00472                         {
00473                                 return false;
00474                         }
00475                 }
00476 #else
00477                 llassert_always(idx >= 0);
00478                 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00479                 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00480                 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00481                 mReadData = new U8[size];
00482                 S32 bytes_read = ll_apr_file_read_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
00483                                                                                          mReadData, offset, size);
00484                 if (bytes_read != size)
00485                 {
00486                         llwarns << "LLTextureCacheWorker: "  << mID
00487                                         << " incorrect number of bytes read from header: " << bytes_read
00488                                         << " / " << size << llendl;
00489                         mDataSize = -1; // failed
00490                         return true;
00491                 }
00492                 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
00493                 {
00494                         return true; // done
00495                 }
00496                 else
00497                 {
00498                         mState = BODY;
00499                 }
00500 #endif
00501         }
00502 
00503         if (mState == BODY)
00504         {
00505 #if USE_LFS_READ
00506                 if (mFileHandle == LLLFSThread::nullHandle())
00507                 {
00508                         std::string filename = mCache->getTextureFileName(mID);
00509                         S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
00510                         if (filesize > mOffset)
00511                         {
00512                                 S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
00513                                 mDataSize = llmin(datasize, mDataSize);
00514                                 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00515                                 data_offset = llmax(data_offset, 0);
00516                                 S32 file_size = mDataSize - data_offset;
00517                                 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00518                                 file_offset = llmax(file_offset, 0);
00519 
00520                                 llassert_always(mDataSize > 0);
00521                                 U8* data = new U8[mDataSize];
00522                                 if (data_offset > 0)
00523                                 {
00524                                         llassert_always(mReadData);
00525                                         llassert_always(data_offset <= mDataSize);
00526                                         memcpy(data, mReadData, data_offset);
00527                                         delete[] mReadData;
00528                                         mReadData = NULL;
00529                                 }
00530                                 llassert_always(mReadData == NULL);
00531                                 mReadData = data;
00532 
00533                                 mBytesRead = -1;
00534                                 mBytesToRead = file_size;
00535                                 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00536                                 llassert_always(data_offset + mBytesToRead <= mDataSize);
00537                                 mFileHandle = LLLFSThread::sLocal->read(filename,
00538                                                                                                                 mReadData + data_offset, file_offset, mBytesToRead,
00539                                                                                                                 new ReadResponder(mCache, mRequestHandle));
00540                                 return false;
00541                         }
00542                         else
00543                         {
00544                                 mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
00545                                 return true; // done
00546                         }
00547                 }
00548                 else
00549                 {
00550                         if (mBytesRead >= 0)
00551                         {
00552                                 if (mBytesRead != mBytesToRead)
00553                                 {
00554                                         llwarns << "LLTextureCacheWorker: "  << mID
00555                                                         << " incorrect number of bytes read from body: " << mBytesRead
00556                                                         << " != " << mBytesToRead << llendl;
00557                                         mDataSize = -1; // failed
00558                                 }
00559                                 return true;
00560                         }
00561                         else
00562                         {
00563                                 return false;
00564                         }
00565                 }
00566 #else
00567                 std::string filename = mCache->getTextureFileName(mID);
00568                 S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
00569                 S32 bytes_read = 0;
00570                 if (filesize > mOffset)
00571                 {
00572                         S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
00573                         mDataSize = llmin(datasize, mDataSize);
00574                         S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00575                         data_offset = llmax(data_offset, 0);
00576                         S32 file_size = mDataSize - data_offset;
00577                         S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00578                         file_offset = llmax(file_offset, 0);
00579                         
00580                         U8* data = new U8[mDataSize];
00581                         if (data_offset > 0)
00582                         {
00583                                 llassert_always(mReadData);
00584                                 memcpy(data, mReadData, data_offset);
00585                                 delete[] mReadData;
00586                         }
00587                         mReadData = data;
00588                         bytes_read = ll_apr_file_read_ex(filename, mCache->getFileAPRPool(),
00589                                                                                          mReadData + data_offset,
00590                                                                                          file_offset, file_size);
00591                         if (bytes_read != file_size)
00592                         {
00593                                 llwarns << "LLTextureCacheWorker: "  << mID
00594                                                 << " incorrect number of bytes read from body: " << bytes_read
00595                                                 << " / " << file_size << llendl;
00596                                 mDataSize = -1; // failed
00597                                 return true;
00598                         }
00599                 }
00600                 else
00601                 {
00602                         mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
00603                 }
00604                 
00605                 return true;
00606 #endif
00607         }
00608         
00609         return false;
00610 }
00611 
00612 bool LLTextureCacheRemoteWorker::doWrite()
00613 {
00614         S32 idx = -1;
00615 
00616         // No LOCAL state for write()
00617         
00618         if (mState == INIT)
00619         {
00620                 S32 cur_imagesize = 0;
00621                 S32 offset = mOffset;
00622                 idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
00623                 if (idx >= 0 && cur_imagesize > 0)
00624                 {
00625                         offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header
00626                 }
00627                 idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry
00628                 if (idx >= 0)
00629                 {
00630                         if(cur_imagesize > 0 && mImageSize != cur_imagesize)
00631                         {
00632                                 llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl;
00633                                 offset = 0; // re-write header
00634                         }
00635                         mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
00636                 }
00637                 else
00638                 {
00639                         mDataSize = -1; // failed
00640                         return true;
00641                 }
00642         }
00643         
00644         if (mState == HEADER)
00645         {
00646 #if USE_LFS_WRITE
00647                 if (mFileHandle == LLLFSThread::nullHandle())
00648                 {
00649                         llassert_always(idx >= 0);
00650                         llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00651                         S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00652                         S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00653                         mBytesRead = -1;
00654                         mBytesToRead = size;
00655                         setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00656                         mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
00657                                                                                                          mWriteData, offset, mBytesToRead,
00658                                                                                                          new WriteResponder(mCache, mRequestHandle));
00659                         return false;
00660                 }
00661                 else
00662                 {
00663                         if (mBytesRead >= 0)
00664                         {
00665                                 if (mBytesRead != mBytesToRead)
00666                                 {
00667                                         llwarns << "LLTextureCacheWorker: "  << mID
00668                                                         << " incorrect number of bytes written to header: " << mBytesRead
00669                                                         << " != " << mBytesToRead << llendl;
00670                                         mDataSize = -1; // failed
00671                                         return true;
00672                                 }
00673                                 if (mDataSize <=  mBytesToRead)
00674                                 {
00675                                         return true; // done
00676                                 }
00677                                 else
00678                                 {
00679                                         mFileHandle = LLLFSThread::nullHandle();
00680                                         mState = BODY;
00681                                 }
00682                         }
00683                         else
00684                         {
00685                                 return false;
00686                         }
00687                 }
00688 #else
00689                 llassert_always(idx >= 0);
00690                 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00691                 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00692                 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00693                 S32 bytes_written = ll_apr_file_write_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
00694                                                                                                  mWriteData, offset, size);
00695 
00696                 if (bytes_written <= 0)
00697                 {
00698                         llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
00699                         mDataSize = -1; // failed
00700                         return true;
00701                 }
00702 
00703                 if (mDataSize <= size)
00704                 {
00705                         return true; // done
00706                 }
00707                 else
00708                 {
00709                         mState = BODY;
00710                 }
00711 #endif
00712         }
00713         
00714         if (mState == BODY)
00715         {
00716 #if USE_LFS_WRITE
00717                 if (mFileHandle == LLLFSThread::nullHandle())
00718                 {
00719                         S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00720                         data_offset = llmax(data_offset, 0);
00721                         S32 file_size = mDataSize - data_offset;
00722                         S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00723                         file_offset = llmax(file_offset, 0);
00724                         if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
00725                         {
00726                                 std::string filename = mCache->getTextureFileName(mID);
00727                                 mBytesRead = -1;
00728                                 mBytesToRead = file_size;
00729                                 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00730                                 mFileHandle = LLLFSThread::sLocal->write(filename,
00731                                                                                                                  mWriteData + data_offset, file_offset, mBytesToRead,
00732                                                                                                                  new WriteResponder(mCache, mRequestHandle));
00733                                 return false;
00734                         }
00735                         else
00736                         {
00737                                 mDataSize = 0; // no data written
00738                                 return true; // done
00739                         }
00740                 }
00741                 else
00742                 {
00743                         if (mBytesRead >= 0)
00744                         {
00745                                 if (mBytesRead != mBytesToRead)
00746                                 {
00747                                         llwarns << "LLTextureCacheWorker: "  << mID
00748                                                         << " incorrect number of bytes written to body: " << mBytesRead
00749                                                         << " != " << mBytesToRead << llendl;
00750                                         mDataSize = -1; // failed
00751                                 }
00752                                 return true;
00753                         }
00754                         else
00755                         {
00756                                 return false;
00757                         }
00758                 }
00759 #else
00760                 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00761                 data_offset = llmax(data_offset, 0);
00762                 S32 file_size = mDataSize - data_offset;
00763                 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00764                 file_offset = llmax(file_offset, 0);
00765                 S32 bytes_written = 0;
00766                 if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
00767                 {
00768                         std::string filename = mCache->getTextureFileName(mID);
00769                         bytes_written = ll_apr_file_write_ex(filename, mCache->getFileAPRPool(),
00770                                                                                                  mWriteData + data_offset,
00771                                                                                                  file_offset, file_size);
00772                         if (bytes_written <= 0)
00773                         {
00774                                 mDataSize = -1; // failed
00775                         }
00776                 }
00777                 else
00778                 {
00779                         mDataSize = 0; // no data written
00780                 }
00781 
00782                 return true;
00783 #endif
00784         }
00785         
00786         return false;
00787 }
00788 
00789 //virtual
00790 bool LLTextureCacheWorker::doWork(S32 param)
00791 {
00792         bool res = false;
00793         if (param == 0) // read
00794         {
00795                 res = doRead();
00796         }
00797         else if (param == 1) // write
00798         {
00799                 res = doWrite();
00800         }
00801         else
00802         {
00803                 llassert_always(0);
00804         }
00805         return res;
00806 }
00807 
00808 //virtual (WORKER THREAD)
00809 void LLTextureCacheWorker::finishWork(S32 param, bool completed)
00810 {
00811         if (mResponder.notNull())
00812         {
00813                 bool success = (completed && mDataSize > 0);
00814                 if (param == 0)
00815                 {
00816                         // read
00817                         if (success)
00818                         {
00819                                 mResponder->setData(mReadData, mDataSize, mImageSize, mImageFormat, mImageLocal);
00820                                 mReadData = NULL; // responder owns data
00821                                 mDataSize = 0;
00822                         }
00823                         else
00824                         {
00825                                 delete[] mReadData;
00826                                 mReadData = NULL;
00827                                 
00828                         }
00829                 }
00830                 else
00831                 {
00832                         // write
00833                         mWriteData = NULL; // we never owned data
00834                         mDataSize = 0;
00835                 }
00836                 mCache->addCompleted(mResponder, success);
00837         }
00838 }
00839 
00840 //virtual (MAIN THREAD)
00841 void LLTextureCacheWorker::endWork(S32 param, bool aborted)
00842 {
00843         if (aborted)
00844         {
00845                 // Let the destructor handle any cleanup
00846                 return;
00847         }
00848         switch(param)
00849         {
00850           default:
00851           case 0: // read
00852           case 1: // write
00853           {
00854                   if (mDataSize < 0)
00855                   {
00856                           // failed
00857                           mCache->removeFromCache(mID);
00858                   }
00859                   break;
00860           }
00861         }
00862 }
00863 
00865 
00866 LLTextureCache::LLTextureCache(bool threaded)
00867         : LLWorkerThread("TextureCache", threaded),
00868           mWorkersMutex(getAPRPool()),
00869           mHeaderMutex(getAPRPool()),
00870           mListMutex(getAPRPool()),
00871           mFileAPRPool(NULL),
00872           mReadOnly(FALSE),
00873           mTexturesSizeTotal(0),
00874           mDoPurge(FALSE)
00875 {
00876         apr_pool_create(&mFileAPRPool, NULL);
00877 }
00878 
00879 LLTextureCache::~LLTextureCache()
00880 {
00881         apr_pool_destroy(mFileAPRPool);
00882 }
00883 
00885 
00886 //virtual
00887 S32 LLTextureCache::update(U32 max_time_ms)
00888 {
00889         S32 res;
00890         res = LLWorkerThread::update(max_time_ms);
00891 
00892         mListMutex.lock();
00893         handle_list_t priorty_list = mPrioritizeWriteList; // copy list
00894         mPrioritizeWriteList.clear();
00895         responder_list_t completed_list = mCompletedList; // copy list
00896         mCompletedList.clear();
00897         mListMutex.unlock();
00898         
00899         lockWorkers();
00900         
00901         for (handle_list_t::iterator iter1 = priorty_list.begin();
00902                  iter1 != priorty_list.end(); ++iter1)
00903         {
00904                 handle_t handle = *iter1;
00905                 handle_map_t::iterator iter2 = mWriters.find(handle);
00906                 if(iter2 != mWriters.end())
00907                 {
00908                         LLTextureCacheWorker* worker = iter2->second;
00909                         worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mPriority);
00910                 }
00911         }
00912 
00913         for (responder_list_t::iterator iter1 = completed_list.begin();
00914                  iter1 != completed_list.end(); ++iter1)
00915         {
00916                 Responder *responder = iter1->first;
00917                 bool success = iter1->second;
00918                 responder->completed(success);
00919         }
00920         
00921         unlockWorkers();
00922         
00923         return res;
00924 }
00925 
00927 
00928 std::string LLTextureCache::getLocalFileName(const LLUUID& id)
00929 {
00930         // Does not include extension
00931         std::string idstr = id.asString();
00932         std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", idstr);
00933         return filename;
00934 }
00935 
00936 std::string LLTextureCache::getTextureFileName(const LLUUID& id)
00937 {
00938         std::string idstr = id.asString();
00939         std::string delem = gDirUtilp->getDirDelimiter();
00940         std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
00941         return filename;
00942 }
00943 
00944 bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
00945 {
00946         bool res = false;
00947         bool purge = false;
00948         // Append UUID to end of texture entries
00949         {
00950                 LLMutexLock lock(&mHeaderMutex);
00951                 size_map_t::iterator iter = mTexturesSizeMap.find(id);
00952                 if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
00953                 {
00954                         llassert_always(bodysize > 0);
00955                         Entry* entry = new Entry(id, bodysize, time(NULL));
00956                         ll_apr_file_write_ex(mTexturesDirEntriesFileName, getFileAPRPool(),
00957                                                                  (U8*)entry, -1, 1*sizeof(Entry));
00958                         delete entry;
00959                         if (iter != mTexturesSizeMap.end())
00960                         {
00961                                 mTexturesSizeTotal -= iter->second;
00962                         }
00963                         mTexturesSizeTotal += bodysize;
00964                         mTexturesSizeMap[id] = bodysize;
00965                         if (mTexturesSizeTotal > sCacheMaxTexturesSize)
00966                         {
00967                                 purge = true;
00968                         }
00969                         res = true;
00970                 }
00971         }
00972         if (purge)
00973         {
00974                 mDoPurge = TRUE;
00975         }
00976         return res;
00977 }
00978 
00980 
00981 //static
00982 const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB
00983 F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
00984 U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
00985 S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
00986 const char* entries_filename = "texture.entries";
00987 const char* cache_filename = "texture.cache";
00988 const char* textures_dirname = "textures";
00989 
00990 void LLTextureCache::setDirNames(ELLPath location)
00991 {
00992         std::string delem = gDirUtilp->getDirDelimiter();
00993         mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
00994         mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
00995         mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
00996         mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
00997 }
00998 
00999 void LLTextureCache::purgeCache(ELLPath location)
01000 {
01001         if (!mReadOnly)
01002         {
01003                 setDirNames(location);
01004         
01005                 ll_apr_file_remove(mHeaderEntriesFileName, NULL);
01006                 ll_apr_file_remove(mHeaderDataFileName, NULL);
01007         }
01008         purgeAllTextures(true);
01009 }
01010 
01011 S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
01012 {
01013         mReadOnly = read_only;
01014         
01015         S64 header_size = (max_size * 2) / 10;
01016         S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE;
01017         sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries));
01018         header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE;
01019         max_size -= header_size;
01020         if (sCacheMaxTexturesSize > 0)
01021                 sCacheMaxTexturesSize = llmin(sCacheMaxTexturesSize, max_size);
01022         else
01023                 sCacheMaxTexturesSize = max_size;
01024         max_size -= sCacheMaxTexturesSize;
01025         
01026         LL_INFOS("TextureCache") << "Headers: " << sCacheMaxEntries
01027                         << " Textures size: " << sCacheMaxTexturesSize/(1024*1024) << " MB" << LL_ENDL;
01028 
01029         setDirNames(location);
01030         
01031         if (!mReadOnly)
01032         {
01033                 LLFile::mkdir(mTexturesDirName.c_str());
01034                 const char* subdirs = "0123456789abcdef";
01035                 for (S32 i=0; i<16; i++)
01036                 {
01037                         std::string dirname = mTexturesDirName + gDirUtilp->getDirDelimiter() + subdirs[i];
01038                         LLFile::mkdir(dirname.c_str());
01039                 }
01040         }
01041         readHeaderCache();
01042         purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it
01043 
01044         return max_size; // unused cache space
01045 }
01046 
01047 struct lru_data
01048 {
01049         lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
01050         U32 time;
01051         S32 index;
01052         LLUUID uuid;
01053         struct Compare
01054         {
01055                 // lhs < rhs
01056                 typedef const lru_data* lru_data_ptr;
01057                 bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
01058                 {
01059                         if (a->time > b->time)
01060                                 return true;
01061                         else if (b->time > a->time)
01062                                 return false;
01063                         else
01064                                 return a->index < b->index;
01065                 }
01066         };                              
01067 };
01068 
01069 // Called from either the main thread or the worker thread
01070 void LLTextureCache::readHeaderCache(apr_pool_t* poolp)
01071 {
01072         LLMutexLock lock(&mHeaderMutex);
01073         mHeaderEntriesInfo.mVersion = 0.f;
01074         mHeaderEntriesInfo.mEntries = 0;
01075         if (ll_apr_file_exists(mHeaderEntriesFileName, poolp))
01076         {
01077                 ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
01078                                                         (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
01079         }
01080         if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
01081         {
01082                 if (!mReadOnly)
01083                 {
01084                         // Info with 0 entries
01085                         mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
01086                         ll_apr_file_write_ex(mHeaderEntriesFileName, poolp,
01087                                                                  (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
01088                 }
01089         }
01090         else
01091         {
01092                 S32 num_entries = mHeaderEntriesInfo.mEntries;
01093                 if (num_entries)
01094                 {
01095                         Entry* entries = new Entry[num_entries];
01096                         ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
01097                                                                 (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry));
01098                         typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
01099                         lru_set_t lru;
01100                         for (S32 i=0; i<num_entries; i++)
01101                         {
01102                                 if (entries[i].mSize >= 0) // -1 indicates erased entry, skip
01103                                 {
01104                                         const LLUUID& id = entries[i].mID;
01105                                         lru.insert(new lru_data(entries[i].mTime, i, id));
01106                                         mHeaderIDMap[id] = i;
01107                                 }
01108                         }
01109                         mLRU.clear();
01110                         S32 lru_entries = sCacheMaxEntries / 10;
01111                         for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
01112                         {
01113                                 lru_data* data = *iter;
01114                                 mLRU[data->index] = data->uuid;
01115                                 if (--lru_entries <= 0)
01116                                         break;
01117                         }
01118                         for_each(lru.begin(), lru.end(), DeletePointer());
01119                         delete[] entries;
01120                 }
01121         }
01122 }
01123 
01125 
01126 void LLTextureCache::purgeAllTextures(bool purge_directories)
01127 {
01128         if (!mReadOnly)
01129         {
01130                 const char* subdirs = "0123456789abcdef";
01131                 std::string delem = gDirUtilp->getDirDelimiter();
01132                 std::string mask = delem + "*";
01133                 for (S32 i=0; i<16; i++)
01134                 {
01135                         std::string dirname = mTexturesDirName + delem + subdirs[i];
01136                         gDirUtilp->deleteFilesInDir(dirname.c_str(),mask);
01137                         if (purge_directories)
01138                         {
01139                                 LLFile::rmdir(dirname.c_str());
01140                         }
01141                 }
01142                 ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
01143                 if (purge_directories)
01144                 {
01145                         LLFile::rmdir(mTexturesDirName.c_str());
01146                 }
01147         }
01148         mTexturesSizeMap.clear();
01149 }
01150 
01151 void LLTextureCache::purgeTextures(bool validate)
01152 {
01153         if (mReadOnly)
01154         {
01155                 return;
01156         }
01157 
01158         LLMutexLock lock(&mHeaderMutex);
01159         
01160         S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL);
01161         S32 num_entries = filesize / sizeof(Entry);
01162         if (num_entries * (S32)sizeof(Entry) != filesize)
01163         {
01164                 LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
01165                 purgeAllTextures(false);
01166                 return;
01167         }
01168         if (num_entries == 0)
01169         {
01170                 return; // nothing to do
01171         }
01172         
01173         Entry* entries = new Entry[num_entries];
01174         S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL,
01175                                                                                  (U8*)entries, 0, num_entries*sizeof(Entry));
01176         if (bytes_read != filesize)
01177         {
01178                 LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
01179                 purgeAllTextures(false);
01180                 return;
01181         }
01182         
01183         LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading Entries..." << LL_ENDL;
01184         
01185         std::map<LLUUID, S32> entry_idx_map;
01186         S64 total_size = 0;
01187         for (S32 idx=0; idx<num_entries; idx++)
01188         {
01189                 const LLUUID& id = entries[idx].mID;
01190                 LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL;
01191                 std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
01192                 if (iter != entry_idx_map.end())
01193                 {
01194                         // Newer entry replacing older entry
01195                         S32 pidx = iter->second;
01196                         total_size -= entries[pidx].mSize;
01197                         entries[pidx].mSize = 0; // flag: skip older entry
01198                 }
01199                 entry_idx_map[id] = idx;
01200                 total_size += entries[idx].mSize;
01201         }
01202 
01203         U32 validate_idx = 0;
01204         if (validate)
01205         {
01206                 validate_idx = gSavedSettings.getU32("CacheValidateCounter");
01207                 U32 next_idx = (++validate_idx) % 256;
01208                 gSavedSettings.setU32("CacheValidateCounter", next_idx);
01209                 LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL;
01210         }
01211         
01212         S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
01213         S32 purge_count = 0;
01214         S32 next_idx = 0;
01215         for (S32 idx=0; idx<num_entries; idx++)
01216         {
01217                 if (entries[idx].mSize == 0)
01218                 {
01219                         continue;
01220                 }
01221                 bool purge_entry = false;
01222                 std::string filename = getTextureFileName(entries[idx].mID);
01223                 if (total_size >= min_cache_size)
01224                 {
01225                         purge_entry = true;
01226                 }
01227                 else if (validate)
01228                 {
01229                         // make sure file exists and is the correct size
01230                         S32 uuididx = entries[idx].mID.mData[0];
01231                         if (uuididx == validate_idx)
01232                         {
01233                                 LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL;
01234                                 S32 bodysize = ll_apr_file_size(filename, NULL);
01235                                 if (bodysize != entries[idx].mSize)
01236                                 {
01237                                         LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
01238                                                         << filename << LL_ENDL;
01239                                         purge_entry = true;
01240                                 }
01241                         }
01242                 }
01243                 if (purge_entry)
01244                 {
01245                         purge_count++;
01246                         LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL;
01247                         ll_apr_file_remove(filename, NULL);
01248                         total_size -= entries[idx].mSize;
01249                         entries[idx].mSize = 0;
01250                 }
01251                 else
01252                 {
01253                         if (next_idx != idx)
01254                         {
01255                                 entries[next_idx] = entries[idx];
01256                         }
01257                         ++next_idx;
01258                 }
01259         }
01260         num_entries = next_idx;
01261 
01262         LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL;
01263         
01264         ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
01265         ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL,
01266                                                  (U8*)&entries[0], 0, num_entries*sizeof(Entry));
01267         
01268         mTexturesSizeTotal = 0;
01269         mTexturesSizeMap.clear();
01270         for (S32 idx=0; idx<num_entries; idx++)
01271         {
01272                 mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
01273                 mTexturesSizeTotal += entries[idx].mSize;
01274         }
01275         llassert(mTexturesSizeTotal == total_size);
01276         
01277         delete[] entries;
01278         
01279         LL_INFOS("TextureCache") << "TEXTURE CACHE:"
01280                         << " PURGED: " << purge_count
01281                         << " ENTRIES: " << num_entries
01282                         << " CACHE SIZE: " << total_size / 1024*1024 << " MB"
01283                         << llendl;
01284 }
01285 
01287 
01288 // call lockWorkers() first!
01289 LLTextureCacheWorker* LLTextureCache::getReader(handle_t handle)
01290 {
01291         LLTextureCacheWorker* res = NULL;
01292         handle_map_t::iterator iter = mReaders.find(handle);
01293         if (iter != mReaders.end())
01294         {
01295                 res = iter->second;
01296         }
01297         return res;
01298 }
01299 
01300 LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
01301 {
01302         LLTextureCacheWorker* res = NULL;
01303         handle_map_t::iterator iter = mWriters.find(handle);
01304         if (iter != mWriters.end())
01305         {
01306                 res = iter->second;
01307         }
01308         return res;
01309 }
01310 
01312 
01313 // Called from work thread
01314 S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
01315 {
01316         bool retry = false;
01317         S32 idx = -1;
01318 
01319         {
01320                 LLMutexLock lock(&mHeaderMutex);
01321                 id_map_t::iterator iter = mHeaderIDMap.find(id);
01322                 if (iter != mHeaderIDMap.end())
01323                 {
01324                         idx = iter->second;
01325                 }
01326                 else if (touch && !mReadOnly)
01327                 {
01328                         if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
01329                         {
01330                                 // Add an entry
01331                                 idx = mHeaderEntriesInfo.mEntries++;
01332                                 mHeaderIDMap[id] = idx;
01333                                 // Update Info
01334                                 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
01335                                                                         (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
01336                         }
01337                         else if (!mLRU.empty())
01338                         {
01339                                 idx = mLRU.begin()->first; // will be erased below
01340                                 const LLUUID& oldid = mLRU.begin()->second;
01341                                 mHeaderIDMap.erase(oldid);
01342                                 mTexturesSizeMap.erase(oldid);
01343                                 mHeaderIDMap[id] = idx;
01344                         }
01345                         else
01346                         {
01347                                 idx = -1;
01348                                 retry = true;
01349                         }
01350                 }
01351                 if (idx >= 0)
01352                 {
01353                         if (touch && !mReadOnly)
01354                         {
01355                                 // Update the lru entry
01356                                 mLRU.erase(idx);
01357                                 llassert_always(imagesize && *imagesize > 0);
01358                                 Entry* entry = new Entry(id, *imagesize, time(NULL));
01359                                 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01360                                 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
01361                                                                          (U8*)entry, offset, sizeof(Entry));
01362                                 delete entry;
01363                         }
01364                         else if (imagesize)
01365                         {
01366                                 // Get the image size
01367                                 Entry entry;
01368                                 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01369                                 ll_apr_file_read_ex(mHeaderEntriesFileName, getFileAPRPool(),
01370                                                                         (U8*)&entry, offset, sizeof(Entry));
01371                                 *imagesize = entry.mSize;
01372                         }
01373                 }
01374         }
01375         if (retry)
01376         {
01377                 readHeaderCache(getFileAPRPool()); // updates the lru
01378                 llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
01379                 idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion
01380         }
01381         return idx;
01382 }
01383 
01385 
01386 // Calls from texture pipeline thread (i.e. LLTextureFetch)
01387 
01388 LLTextureCache::handle_t LLTextureCache::readFromCache(const LLString& filename, const LLUUID& id, U32 priority,
01389                                                                                                            S32 offset, S32 size, ReadResponder* responder)
01390 {
01391         // Note: checking to see if an entry exists can cause a stall,
01392         //  so let the thread handle it
01393         LLMutexLock lock(&mWorkersMutex);
01394         LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id,
01395                                                                                                                         NULL, size, offset, 0,
01396                                                                                                                         responder);
01397         handle_t handle = worker->read();
01398         mReaders[handle] = worker;
01399         return handle;
01400 }
01401 
01402 LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 priority,
01403                                                                                                            S32 offset, S32 size, ReadResponder* responder)
01404 {
01405         // Note: checking to see if an entry exists can cause a stall,
01406         //  so let the thread handle it
01407         LLMutexLock lock(&mWorkersMutex);
01408         LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
01409                                                                                                                         NULL, size, offset, 0,
01410                                                                                                                         responder);
01411         handle_t handle = worker->read();
01412         mReaders[handle] = worker;
01413         return handle;
01414 }
01415 
01416 
01417 bool LLTextureCache::readComplete(handle_t handle, bool abort)
01418 {
01419         lockWorkers();
01420         handle_map_t::iterator iter = mReaders.find(handle);
01421         llassert_always(iter != mReaders.end());
01422         LLTextureCacheWorker* worker = iter->second;
01423         bool res = worker->complete();
01424         if (res || abort)
01425         {
01426                 mReaders.erase(handle);
01427                 unlockWorkers();
01428                 worker->scheduleDelete();
01429                 return true;
01430         }
01431         else
01432         {
01433                 unlockWorkers();
01434                 return false;
01435         }
01436 }
01437 
01438 LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority,
01439                                                                                                           U8* data, S32 datasize, S32 imagesize,
01440                                                                                                           WriteResponder* responder)
01441 {
01442         if (mReadOnly)
01443         {
01444                 delete responder;
01445                 return LLWorkerThread::nullHandle();
01446         }
01447         if (mDoPurge)
01448         {
01449                 // NOTE: This may cause an occasional hiccup,
01450                 //  but it really needs to be done on the control thread
01451                 //  (i.e. here)
01452                 purgeTextures(false);
01453                 mDoPurge = FALSE;
01454         }
01455         if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
01456         {
01457                 LLMutexLock lock(&mWorkersMutex);
01458                 llassert_always(imagesize > 0);
01459                 LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id,
01460                                                                                                                                 data, datasize, 0,
01461                                                                                                                                 imagesize, responder);
01462                 handle_t handle = worker->write();
01463                 mWriters[handle] = worker;
01464                 return handle;
01465         }
01466         delete responder;
01467         return LLWorkerThread::nullHandle();
01468 }
01469 
01470 bool LLTextureCache::writeComplete(handle_t handle, bool abort)
01471 {
01472         lockWorkers();
01473         handle_map_t::iterator iter = mWriters.find(handle);
01474         llassert_always(iter != mWriters.end());
01475         LLTextureCacheWorker* worker = iter->second;
01476         if (worker->complete() || abort)
01477         {
01478                 mWriters.erase(handle);
01479                 unlockWorkers();
01480                 worker->scheduleDelete();
01481                 return true;
01482         }
01483         else
01484         {
01485                 unlockWorkers();
01486                 return false;
01487         }
01488 }
01489 
01490 void LLTextureCache::prioritizeWrite(handle_t handle)
01491 {
01492         // Don't prioritize yet, we might be working on this now
01493         //   which could create a deadlock
01494         LLMutexLock lock(&mListMutex);  
01495         mPrioritizeWriteList.push_back(handle);
01496 }
01497 
01498 void LLTextureCache::addCompleted(Responder* responder, bool success)
01499 {
01500         LLMutexLock lock(&mListMutex);
01501         mCompletedList.push_back(std::make_pair(responder,success));
01502 }
01503 
01505 
01506 // Called from MAIN thread (endWork())
01507 
01508 bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
01509 {
01510         if (mReadOnly)
01511         {
01512                 return false;
01513         }
01514         LLMutexLock lock(&mHeaderMutex);
01515         id_map_t::iterator iter = mHeaderIDMap.find(id);
01516         if (iter != mHeaderIDMap.end())
01517         {
01518                 S32 idx = iter->second;
01519                 if (idx >= 0)
01520                 {
01521                         Entry* entry = new Entry(id, -1, time(NULL));
01522                         S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01523                         ll_apr_file_write_ex(mHeaderEntriesFileName, NULL,
01524                                                                  (U8*)entry, offset, sizeof(Entry));
01525                         delete entry;
01526                         mLRU[idx] = id;
01527                         mHeaderIDMap.erase(id);
01528                         mTexturesSizeMap.erase(id);
01529                         return true;
01530                 }
01531         }
01532         return false;
01533 }
01534 
01535 void LLTextureCache::removeFromCache(const LLUUID& id)
01536 {
01537         llwarns << "Removing texture from cache: " << id << llendl;
01538         if (!mReadOnly)
01539         {
01540                 removeHeaderCacheEntry(id);
01541                 ll_apr_file_remove(getTextureFileName(id), NULL);
01542         }
01543 }
01544 
01546 
01547 LLTextureCache::ReadResponder::ReadResponder()
01548         : mImageSize(0),
01549           mImageLocal(FALSE)
01550 {
01551 }
01552 
01553 void LLTextureCache::ReadResponder::setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
01554 {
01555         if (mFormattedImage.notNull())
01556         {
01557                 llassert_always(mFormattedImage->getCodec() == imageformat);
01558                 mFormattedImage->appendData(data, datasize);
01559         }
01560         else
01561         {
01562                 mFormattedImage = LLImageFormatted::createFromType(imageformat);
01563                 mFormattedImage->setData(data,datasize);
01564         }
01565         mImageSize = imagesize;
01566         mImageLocal = imagelocal;
01567 }
01568 

Generated on Fri May 16 08:34:04 2008 for SecondLife by  doxygen 1.5.5