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
00046 const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE;
00047
00048 class LLTextureCacheWorker : public LLWorkerClass
00049 {
00050 friend class LLTextureCache;
00051
00052 private:
00053 enum e_state
00054 {
00055 INIT = 0,
00056 LOCAL = 1,
00057 CACHE = 2,
00058 HEADER = 3,
00059 BODY = 4
00060 };
00061
00062 class ReadResponder : public LLLFSThread::Responder
00063 {
00064 public:
00065 ReadResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
00066 ~ReadResponder() {}
00067 void completed(S32 bytes)
00068 {
00069 mCache->lockWorkers();
00070 LLTextureCacheWorker* reader = mCache->getReader(mHandle);
00071 if (reader) reader->ioComplete(bytes);
00072 mCache->unlockWorkers();
00073 }
00074 LLTextureCache* mCache;
00075 LLTextureCacheWorker::handle_t mHandle;
00076 };
00077
00078 class WriteResponder : public LLLFSThread::Responder
00079 {
00080 public:
00081 WriteResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
00082 ~WriteResponder() {}
00083 void completed(S32 bytes)
00084 {
00085 mCache->lockWorkers();
00086 LLTextureCacheWorker* writer = mCache->getWriter(mHandle);
00087 if (writer) writer->ioComplete(bytes);
00088 mCache->unlockWorkers();
00089 }
00090 LLTextureCache* mCache;
00091 LLTextureCacheWorker::handle_t mHandle;
00092 };
00093
00094 public:
00095 LLTextureCacheWorker(LLTextureCache* cache, U32 priority, const LLUUID& id,
00096 U8* data, S32 datasize, S32 offset,
00097 S32 imagesize,
00098 LLTextureCache::Responder* responder)
00099 : LLWorkerClass(cache, "LLTextureCacheWorker"),
00100 mCache(cache),
00101 mPriority(priority),
00102 mID(id),
00103 mState(INIT),
00104 mReadData(NULL),
00105 mWriteData(data),
00106 mDataSize(datasize),
00107 mOffset(offset),
00108 mImageSize(imagesize),
00109 mImageFormat(IMG_CODEC_J2C),
00110 mImageLocal(FALSE),
00111 mResponder(responder),
00112 mFileHandle(LLLFSThread::nullHandle()),
00113 mBytesToRead(0),
00114 mBytesRead(0)
00115 {
00116 mPriority &= LLWorkerThread::PRIORITY_LOWBITS;
00117 }
00118 ~LLTextureCacheWorker()
00119 {
00120 llassert_always(!haveWork());
00121 delete[] mReadData;
00122 }
00123
00124 bool doRead();
00125 bool doWrite();
00126 virtual bool doWork(S32 param);
00127
00128 handle_t read() { addWork(0, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
00129 handle_t write() { addWork(1, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
00130 bool complete() { return checkWork(); }
00131 void ioComplete(S32 bytes)
00132 {
00133 mBytesRead = bytes;
00134 setPriority(LLWorkerThread::PRIORITY_HIGH | mPriority);
00135 }
00136
00137 private:
00138 virtual void startWork(S32 param);
00139 virtual void finishWork(S32 param, bool completed);
00140 virtual void endWork(S32 param, bool aborted);
00141
00142 private:
00143 LLTextureCache* mCache;
00144 U32 mPriority;
00145 LLUUID mID;
00146 e_state mState;
00147
00148 U8* mReadData;
00149 U8* mWriteData;
00150 S32 mDataSize;
00151 S32 mOffset;
00152 S32 mImageSize;
00153 S32 mImageFormat;
00154 BOOL mImageLocal;
00155 LLPointer<LLTextureCache::Responder> mResponder;
00156 LLLFSThread::handle_t mFileHandle;
00157 S32 mBytesToRead;
00158 LLAtomicS32 mBytesRead;
00159 };
00160
00161
00162 void LLTextureCacheWorker::startWork(S32 param)
00163 {
00164 }
00165
00166 bool LLTextureCacheWorker::doRead()
00167 {
00168 S32 local_size = 0;
00169 std::string local_filename;
00170
00171 if (mState == INIT)
00172 {
00173 std::string filename = mCache->getLocalFileName(mID);
00174 local_filename = filename + ".j2c";
00175 local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
00176 if (local_size == 0)
00177 {
00178 local_filename = filename + ".tga";
00179 local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
00180 if (local_size > 0)
00181 {
00182 mImageFormat = IMG_CODEC_TGA;
00183 mDataSize = local_size;
00184 }
00185 }
00186 if (local_size > 0)
00187 {
00188 mState = LOCAL;
00189 }
00190 else
00191 {
00192 mState = CACHE;
00193 }
00194 }
00195
00196 if (mState == LOCAL)
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
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;
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(local_filename, mCache->getFileAPRPool(),
00248 mReadData, mOffset, mDataSize);
00249 if (bytes_read != mDataSize)
00250 {
00251 llwarns << "Error reading file from local cache: " << local_filename
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 S32 idx = -1;
00268
00269 if (mState == CACHE)
00270 {
00271 llassert_always(mImageSize == 0);
00272 idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
00273 if (idx >= 0 && mImageSize > mOffset)
00274 {
00275 llassert_always(mImageSize > 0);
00276 if (!mDataSize || mDataSize > mImageSize)
00277 {
00278 mDataSize = mImageSize;
00279 }
00280 mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
00281 }
00282 else
00283 {
00284 mDataSize = 0;
00285 return true;
00286 }
00287 }
00288
00289 if (mState == HEADER)
00290 {
00291 #if USE_LFS_READ
00292 if (mFileHandle == LLLFSThread::nullHandle())
00293 {
00294 llassert_always(idx >= 0);
00295 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00296 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00297 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00298 llassert_always(mReadData == NULL);
00299 mReadData = new U8[size];
00300 mBytesRead = -1;
00301 mBytesToRead = size;
00302 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00303 mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
00304 mReadData, offset, mBytesToRead,
00305 new ReadResponder(mCache, mRequestHandle));
00306 return false;
00307 }
00308 else
00309 {
00310 if (mBytesRead >= 0)
00311 {
00312 if (mBytesRead != mBytesToRead)
00313 {
00314 llwarns << "LLTextureCacheWorker: " << mID
00315 << " incorrect number of bytes read from header: " << mBytesRead
00316 << " != " << mBytesToRead << llendl;
00317 mDataSize = -1;
00318 return true;
00319 }
00320 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
00321 {
00322 return true;
00323 }
00324 else
00325 {
00326 mFileHandle = LLLFSThread::nullHandle();
00327 mState = BODY;
00328 }
00329 }
00330 else
00331 {
00332 return false;
00333 }
00334 }
00335 #else
00336 llassert_always(idx >= 0);
00337 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00338 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00339 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00340 mReadData = new U8[size];
00341 S32 bytes_read = ll_apr_file_read_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
00342 mReadData, offset, size);
00343 if (bytes_read != size)
00344 {
00345 llwarns << "LLTextureCacheWorker: " << mID
00346 << " incorrect number of bytes read from header: " << bytes_read
00347 << " / " << size << llendl;
00348 mDataSize = -1;
00349 return true;
00350 }
00351 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
00352 {
00353 return true;
00354 }
00355 else
00356 {
00357 mState = BODY;
00358 }
00359 #endif
00360 }
00361
00362 if (mState == BODY)
00363 {
00364 #if USE_LFS_READ
00365 if (mFileHandle == LLLFSThread::nullHandle())
00366 {
00367 std::string filename = mCache->getTextureFileName(mID);
00368 S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
00369 if (filesize > mOffset)
00370 {
00371 S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
00372 mDataSize = llmin(datasize, mDataSize);
00373 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00374 data_offset = llmax(data_offset, 0);
00375 S32 file_size = mDataSize - data_offset;
00376 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00377 file_offset = llmax(file_offset, 0);
00378
00379 llassert_always(mDataSize > 0);
00380 U8* data = new U8[mDataSize];
00381 if (data_offset > 0)
00382 {
00383 llassert_always(mReadData);
00384 llassert_always(data_offset <= mDataSize);
00385 memcpy(data, mReadData, data_offset);
00386 delete[] mReadData;
00387 mReadData = NULL;
00388 }
00389 llassert_always(mReadData == NULL);
00390 mReadData = data;
00391
00392 mBytesRead = -1;
00393 mBytesToRead = file_size;
00394 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00395 llassert_always(data_offset + mBytesToRead <= mDataSize);
00396 mFileHandle = LLLFSThread::sLocal->read(filename,
00397 mReadData + data_offset, file_offset, mBytesToRead,
00398 new ReadResponder(mCache, mRequestHandle));
00399 return false;
00400 }
00401 else
00402 {
00403 mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
00404 return true;
00405 }
00406 }
00407 else
00408 {
00409 if (mBytesRead >= 0)
00410 {
00411 if (mBytesRead != mBytesToRead)
00412 {
00413 llwarns << "LLTextureCacheWorker: " << mID
00414 << " incorrect number of bytes read from body: " << mBytesRead
00415 << " != " << mBytesToRead << llendl;
00416 mDataSize = -1;
00417 }
00418 return true;
00419 }
00420 else
00421 {
00422 return false;
00423 }
00424 }
00425 #else
00426 std::string filename = mCache->getTextureFileName(mID);
00427 S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
00428 S32 bytes_read = 0;
00429 if (filesize > mOffset)
00430 {
00431 S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
00432 mDataSize = llmin(datasize, mDataSize);
00433 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00434 data_offset = llmax(data_offset, 0);
00435 S32 file_size = mDataSize - data_offset;
00436 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00437 file_offset = llmax(file_offset, 0);
00438
00439 U8* data = new U8[mDataSize];
00440 if (data_offset > 0)
00441 {
00442 llassert_always(mReadData);
00443 memcpy(data, mReadData, data_offset);
00444 delete[] mReadData;
00445 }
00446 mReadData = data;
00447 bytes_read = ll_apr_file_read_ex(filename, mCache->getFileAPRPool(),
00448 mReadData + data_offset,
00449 file_offset, file_size);
00450 if (bytes_read != file_size)
00451 {
00452 llwarns << "LLTextureCacheWorker: " << mID
00453 << " incorrect number of bytes read from body: " << bytes_read
00454 << " / " << file_size << llendl;
00455 mDataSize = -1;
00456 return true;
00457 }
00458 }
00459 else
00460 {
00461 mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
00462 }
00463
00464 return true;
00465 #endif
00466 }
00467
00468 return false;
00469 }
00470
00471 bool LLTextureCacheWorker::doWrite()
00472 {
00473 S32 idx = -1;
00474
00475 if (mState == INIT)
00476 {
00477 llassert_always(mOffset == 0);
00478 mState = CACHE;
00479 }
00480
00481
00482
00483 if (mState == CACHE)
00484 {
00485 S32 cur_imagesize = 0;
00486 S32 offset = mOffset;
00487 idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
00488 if (idx >= 0 && cur_imagesize > 0)
00489 {
00490 offset = TEXTURE_CACHE_ENTRY_SIZE;
00491 }
00492 idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize);
00493 if (idx >= 0)
00494 {
00495 llassert_always(cur_imagesize <= 0 || mImageSize == cur_imagesize);
00496 mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
00497 }
00498 else
00499 {
00500 mDataSize = -1;
00501 return true;
00502 }
00503 }
00504
00505 if (mState == HEADER)
00506 {
00507 #if USE_LFS_WRITE
00508 if (mFileHandle == LLLFSThread::nullHandle())
00509 {
00510 llassert_always(idx >= 0);
00511 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00512 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00513 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00514 mBytesRead = -1;
00515 mBytesToRead = size;
00516 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00517 mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
00518 mWriteData, offset, mBytesToRead,
00519 new WriteResponder(mCache, mRequestHandle));
00520 return false;
00521 }
00522 else
00523 {
00524 if (mBytesRead >= 0)
00525 {
00526 if (mBytesRead != mBytesToRead)
00527 {
00528 llwarns << "LLTextureCacheWorker: " << mID
00529 << " incorrect number of bytes written to header: " << mBytesRead
00530 << " != " << mBytesToRead << llendl;
00531 mDataSize = -1;
00532 return true;
00533 }
00534 if (mDataSize <= mBytesToRead)
00535 {
00536 return true;
00537 }
00538 else
00539 {
00540 mFileHandle = LLLFSThread::nullHandle();
00541 mState = BODY;
00542 }
00543 }
00544 else
00545 {
00546 return false;
00547 }
00548 }
00549 #else
00550 llassert_always(idx >= 0);
00551 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
00552 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
00553 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00554 S32 bytes_written = ll_apr_file_write_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
00555 mWriteData, offset, size);
00556
00557 if (bytes_written <= 0)
00558 {
00559 llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
00560 mDataSize = -1;
00561 return true;
00562 }
00563
00564 if (mDataSize <= size)
00565 {
00566 return true;
00567 }
00568 else
00569 {
00570 mState = BODY;
00571 }
00572 #endif
00573 }
00574
00575 if (mState == BODY)
00576 {
00577 #if USE_LFS_WRITE
00578 if (mFileHandle == LLLFSThread::nullHandle())
00579 {
00580 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00581 data_offset = llmax(data_offset, 0);
00582 S32 file_size = mDataSize - data_offset;
00583 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00584 file_offset = llmax(file_offset, 0);
00585 if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
00586 {
00587 std::string filename = mCache->getTextureFileName(mID);
00588 mBytesRead = -1;
00589 mBytesToRead = file_size;
00590 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
00591 mFileHandle = LLLFSThread::sLocal->write(filename,
00592 mWriteData + data_offset, file_offset, mBytesToRead,
00593 new WriteResponder(mCache, mRequestHandle));
00594 return false;
00595 }
00596 else
00597 {
00598 mDataSize = 0;
00599 return true;
00600 }
00601 }
00602 else
00603 {
00604 if (mBytesRead >= 0)
00605 {
00606 if (mBytesRead != mBytesToRead)
00607 {
00608 llwarns << "LLTextureCacheWorker: " << mID
00609 << " incorrect number of bytes written to body: " << mBytesRead
00610 << " != " << mBytesToRead << llendl;
00611 mDataSize = -1;
00612 }
00613 return true;
00614 }
00615 else
00616 {
00617 return false;
00618 }
00619 }
00620 #else
00621 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
00622 data_offset = llmax(data_offset, 0);
00623 S32 file_size = mDataSize - data_offset;
00624 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
00625 file_offset = llmax(file_offset, 0);
00626 S32 bytes_written = 0;
00627 if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
00628 {
00629 std::string filename = mCache->getTextureFileName(mID);
00630 bytes_written = ll_apr_file_write_ex(filename, mCache->getFileAPRPool(),
00631 mWriteData + data_offset,
00632 file_offset, file_size);
00633 if (bytes_written <= 0)
00634 {
00635 mDataSize = -1;
00636 }
00637 }
00638 else
00639 {
00640 mDataSize = 0;
00641 }
00642
00643 return true;
00644 #endif
00645 }
00646
00647 return false;
00648 }
00649
00650
00651 bool LLTextureCacheWorker::doWork(S32 param)
00652 {
00653 bool res = false;
00654 if (param == 0)
00655 {
00656 res = doRead();
00657 }
00658 else if (param == 1)
00659 {
00660 res = doWrite();
00661 }
00662 else
00663 {
00664 llassert_always(0);
00665 }
00666 return res;
00667 }
00668
00669
00670 void LLTextureCacheWorker::finishWork(S32 param, bool completed)
00671 {
00672 if (mResponder.notNull())
00673 {
00674 bool success = (completed && mDataSize > 0);
00675 if (param == 0)
00676 {
00677
00678 if (success)
00679 {
00680 mResponder->setData(mReadData, mDataSize, mImageSize, mImageFormat, mImageLocal);
00681 mReadData = NULL;
00682 mDataSize = 0;
00683 }
00684 else
00685 {
00686 delete[] mReadData;
00687 mReadData = NULL;
00688
00689 }
00690 }
00691 else
00692 {
00693
00694 mWriteData = NULL;
00695 mDataSize = 0;
00696 }
00697 mCache->addCompleted(mResponder, success);
00698 }
00699 }
00700
00701
00702 void LLTextureCacheWorker::endWork(S32 param, bool aborted)
00703 {
00704 if (aborted)
00705 {
00706
00707 return;
00708 }
00709 switch(param)
00710 {
00711 default:
00712 case 0:
00713 case 1:
00714 {
00715 if (mDataSize < 0)
00716 {
00717
00718 mCache->removeFromCache(mID);
00719 }
00720 break;
00721 }
00722 }
00723 }
00724
00726
00727 LLTextureCache::LLTextureCache(bool threaded)
00728 : LLWorkerThread("TextureCache", threaded),
00729 mWorkersMutex(getAPRPool()),
00730 mHeaderMutex(getAPRPool()),
00731 mListMutex(getAPRPool()),
00732 mFileAPRPool(NULL),
00733 mReadOnly(FALSE),
00734 mTexturesSizeTotal(0),
00735 mDoPurge(FALSE)
00736 {
00737 apr_pool_create(&mFileAPRPool, NULL);
00738 }
00739
00740 LLTextureCache::~LLTextureCache()
00741 {
00742 apr_pool_destroy(mFileAPRPool);
00743 }
00744
00746
00747
00748 S32 LLTextureCache::update(U32 max_time_ms)
00749 {
00750 S32 res;
00751 res = LLWorkerThread::update(max_time_ms);
00752
00753 mListMutex.lock();
00754 handle_list_t priorty_list = mPrioritizeWriteList;
00755 mPrioritizeWriteList.clear();
00756 responder_list_t completed_list = mCompletedList;
00757 mCompletedList.clear();
00758 mListMutex.unlock();
00759
00760 lockWorkers();
00761
00762 for (handle_list_t::iterator iter1 = priorty_list.begin();
00763 iter1 != priorty_list.end(); ++iter1)
00764 {
00765 handle_t handle = *iter1;
00766 handle_map_t::iterator iter2 = mWriters.find(handle);
00767 if(iter2 != mWriters.end())
00768 {
00769 LLTextureCacheWorker* worker = iter2->second;
00770 worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mPriority);
00771 }
00772 }
00773
00774 for (responder_list_t::iterator iter1 = completed_list.begin();
00775 iter1 != completed_list.end(); ++iter1)
00776 {
00777 Responder *responder = iter1->first;
00778 bool success = iter1->second;
00779 responder->completed(success);
00780 }
00781
00782 unlockWorkers();
00783
00784 return res;
00785 }
00786
00788
00789 std::string LLTextureCache::getLocalFileName(const LLUUID& id)
00790 {
00791
00792 std::string idstr = id.asString();
00793 std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", idstr);
00794 return filename;
00795 }
00796
00797 std::string LLTextureCache::getTextureFileName(const LLUUID& id)
00798 {
00799 std::string idstr = id.asString();
00800 std::string delem = gDirUtilp->getDirDelimiter();
00801 std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
00802 return filename;
00803 }
00804
00805 bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
00806 {
00807 bool res = false;
00808 bool purge = false;
00809
00810 {
00811 LLMutexLock lock(&mHeaderMutex);
00812 size_map_t::iterator iter = mTexturesSizeMap.find(id);
00813 if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
00814 {
00815 llassert_always(bodysize > 0);
00816 Entry* entry = new Entry(id, bodysize, time(NULL));
00817 ll_apr_file_write_ex(mTexturesDirEntriesFileName, getFileAPRPool(),
00818 (U8*)entry, -1, 1*sizeof(Entry));
00819 delete entry;
00820 if (iter != mTexturesSizeMap.end())
00821 {
00822 mTexturesSizeTotal -= iter->second;
00823 }
00824 mTexturesSizeTotal += bodysize;
00825 mTexturesSizeMap[id] = bodysize;
00826 if (mTexturesSizeTotal > sCacheMaxTexturesSize)
00827 {
00828 purge = true;
00829 }
00830 res = true;
00831 }
00832 }
00833 if (purge)
00834 {
00835 mDoPurge = TRUE;
00836 }
00837 return res;
00838 }
00839
00841
00842
00843 const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024;
00844 F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
00845 U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
00846 S64 LLTextureCache::sCacheMaxTexturesSize = 0;
00847 const char* entries_filename = "texture.entries";
00848 const char* cache_filename = "texture.cache";
00849 const char* textures_dirname = "textures";
00850
00851 void LLTextureCache::setDirNames(ELLPath location)
00852 {
00853 std::string delem = gDirUtilp->getDirDelimiter();
00854 mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
00855 mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
00856 mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
00857 mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
00858 }
00859
00860 void LLTextureCache::purgeCache(ELLPath location)
00861 {
00862 if (!mReadOnly)
00863 {
00864 setDirNames(location);
00865
00866 ll_apr_file_remove(mHeaderEntriesFileName, NULL);
00867 ll_apr_file_remove(mHeaderDataFileName, NULL);
00868 }
00869 purgeAllTextures(true);
00870 }
00871
00872 S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
00873 {
00874 mReadOnly = read_only;
00875
00876 S64 header_size = (max_size * 2) / 10;
00877 S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE;
00878 sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries));
00879 header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE;
00880 max_size -= header_size;
00881 if (sCacheMaxTexturesSize > 0)
00882 sCacheMaxTexturesSize = llmin(sCacheMaxTexturesSize, max_size);
00883 else
00884 sCacheMaxTexturesSize = max_size;
00885 max_size -= sCacheMaxTexturesSize;
00886
00887 llinfos << "TEXTURE CACHE: Headers: " << sCacheMaxEntries
00888 << " Textures size: " << sCacheMaxTexturesSize/(1024*1024) << " MB" << llendl;
00889
00890 setDirNames(location);
00891
00892 if (!mReadOnly)
00893 {
00894 LLFile::mkdir(mTexturesDirName.c_str());
00895 const char* subdirs = "0123456789abcdef";
00896 for (S32 i=0; i<16; i++)
00897 {
00898 std::string dirname = mTexturesDirName + gDirUtilp->getDirDelimiter() + subdirs[i];
00899 LLFile::mkdir(dirname.c_str());
00900 }
00901 }
00902 readHeaderCache();
00903 purgeTextures(true);
00904
00905 return max_size;
00906 }
00907
00908 struct lru_data
00909 {
00910 lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
00911 U32 time;
00912 S32 index;
00913 LLUUID uuid;
00914 struct Compare
00915 {
00916
00917 typedef const lru_data* lru_data_ptr;
00918 bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
00919 {
00920 if (!(a->time < b->time))
00921 return true;
00922 else if (!(b->time < a->time))
00923 return false;
00924 else
00925 return a->index < b->index;
00926 }
00927 };
00928 };
00929
00930
00931 void LLTextureCache::readHeaderCache(apr_pool_t* poolp)
00932 {
00933 LLMutexLock lock(&mHeaderMutex);
00934 mHeaderEntriesInfo.mVersion = 0.f;
00935 mHeaderEntriesInfo.mEntries = 0;
00936 if (ll_apr_file_exists(mHeaderEntriesFileName, poolp))
00937 {
00938 ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
00939 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
00940 }
00941 if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
00942 {
00943 if (!mReadOnly)
00944 {
00945
00946 mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
00947 ll_apr_file_write_ex(mHeaderEntriesFileName, poolp,
00948 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
00949 }
00950 }
00951 else
00952 {
00953 S32 num_entries = mHeaderEntriesInfo.mEntries;
00954 if (num_entries)
00955 {
00956 Entry* entries = new Entry[num_entries];
00957 ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
00958 (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry));
00959 typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
00960 lru_set_t lru;
00961 for (S32 i=0; i<num_entries; i++)
00962 {
00963 if (entries[i].mSize >= 0)
00964 {
00965 const LLUUID& id = entries[i].mID;
00966 lru.insert(new lru_data(entries[i].mTime, i, id));
00967 mHeaderIDMap[id] = i;
00968 }
00969 }
00970 mLRU.clear();
00971 S32 lru_entries = sCacheMaxEntries / 10;
00972 for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
00973 {
00974 lru_data* data = *iter;
00975 mLRU[data->index] = data->uuid;
00976 if (--lru_entries <= 0)
00977 break;
00978 }
00979 for_each(lru.begin(), lru.end(), DeletePointer());
00980 delete[] entries;
00981 }
00982 }
00983 }
00984
00986
00987 void LLTextureCache::purgeAllTextures(bool purge_directories)
00988 {
00989 if (!mReadOnly)
00990 {
00991 const char* subdirs = "0123456789abcdef";
00992 std::string delem = gDirUtilp->getDirDelimiter();
00993 std::string mask = delem + "*";
00994 for (S32 i=0; i<16; i++)
00995 {
00996 std::string dirname = mTexturesDirName + delem + subdirs[i];
00997 gDirUtilp->deleteFilesInDir(dirname.c_str(),mask);
00998 if (purge_directories)
00999 {
01000 LLFile::rmdir(dirname.c_str());
01001 }
01002 }
01003 ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
01004 if (purge_directories)
01005 {
01006 LLFile::rmdir(mTexturesDirName.c_str());
01007 }
01008 }
01009 mTexturesSizeMap.clear();
01010 }
01011
01012 void LLTextureCache::purgeTextures(bool validate)
01013 {
01014 if (mReadOnly)
01015 {
01016 return;
01017 }
01018
01019 LLMutexLock lock(&mHeaderMutex);
01020
01021 S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL);
01022 S32 num_entries = filesize / sizeof(Entry);
01023 if (num_entries * (S32)sizeof(Entry) != filesize)
01024 {
01025 llwarns << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << llendl;
01026 purgeAllTextures(false);
01027 return;
01028 }
01029 if (num_entries == 0)
01030 {
01031 return;
01032 }
01033
01034 Entry* entries = new Entry[num_entries];
01035 S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL,
01036 (U8*)entries, 0, num_entries*sizeof(Entry));
01037 if (bytes_read != filesize)
01038 {
01039 llwarns << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << llendl;
01040 purgeAllTextures(false);
01041 return;
01042 }
01043
01044 llinfos << "TEXTURE CACHE: Reading Entries..." << llendl;
01045
01046 std::map<LLUUID, S32> entry_idx_map;
01047 S64 total_size = 0;
01048 for (S32 idx=0; idx<num_entries; idx++)
01049 {
01050 const LLUUID& id = entries[idx].mID;
01051
01052 std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
01053 if (iter != entry_idx_map.end())
01054 {
01055
01056 S32 pidx = iter->second;
01057 total_size -= entries[pidx].mSize;
01058 entries[pidx].mSize = 0;
01059 }
01060 entry_idx_map[id] = idx;
01061 total_size += entries[idx].mSize;
01062 }
01063
01064 U32 validate_idx = 0;
01065 if (validate)
01066 {
01067 validate_idx = gSavedSettings.getU32("CacheValidateCounter");
01068 U32 next_idx = (++validate_idx) % 256;
01069 gSavedSettings.setU32("CacheValidateCounter", next_idx);
01070 llinfos << "TEXTURE CACHE: Validating: " << validate_idx << llendl;
01071 }
01072
01073 S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
01074 S32 purge_count = 0;
01075 S32 next_idx = 0;
01076 for (S32 idx=0; idx<num_entries; idx++)
01077 {
01078 if (entries[idx].mSize == 0)
01079 {
01080 continue;
01081 }
01082 bool purge_entry = false;
01083 std::string filename = getTextureFileName(entries[idx].mID);
01084 if (total_size >= min_cache_size)
01085 {
01086 purge_entry = true;
01087 }
01088 else if (validate)
01089 {
01090
01091 S32 uuididx = entries[idx].mID.mData[0];
01092 if (uuididx == validate_idx)
01093 {
01094
01095 S32 bodysize = ll_apr_file_size(filename, NULL);
01096 if (bodysize != entries[idx].mSize)
01097 {
01098 llwarns << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
01099 << filename << llendl;
01100 purge_entry = true;
01101 }
01102 }
01103 }
01104 if (purge_entry)
01105 {
01106 purge_count++;
01107
01108 ll_apr_file_remove(filename, NULL);
01109 total_size -= entries[idx].mSize;
01110 entries[idx].mSize = 0;
01111 }
01112 else
01113 {
01114 if (next_idx != idx)
01115 {
01116 entries[next_idx] = entries[idx];
01117 }
01118 ++next_idx;
01119 }
01120 }
01121 num_entries = next_idx;
01122
01123 llinfos << "TEXTURE CACHE: Writing Entries: " << num_entries << llendl;
01124
01125 ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
01126 ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL,
01127 (U8*)&entries[0], 0, num_entries*sizeof(Entry));
01128
01129 mTexturesSizeTotal = 0;
01130 mTexturesSizeMap.clear();
01131 for (S32 idx=0; idx<num_entries; idx++)
01132 {
01133 mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
01134 mTexturesSizeTotal += entries[idx].mSize;
01135 }
01136 llassert(mTexturesSizeTotal == total_size);
01137
01138 delete[] entries;
01139
01140 llinfos << "TEXTURE CACHE:"
01141 << " PURGED: " << purge_count
01142 << " ENTRIES: " << num_entries
01143 << " CACHE SIZE: " << total_size / 1024*1024 << " MB"
01144 << llendl;
01145 }
01146
01148
01149
01150 LLTextureCacheWorker* LLTextureCache::getReader(handle_t handle)
01151 {
01152 LLTextureCacheWorker* res = NULL;
01153 handle_map_t::iterator iter = mReaders.find(handle);
01154 if (iter != mReaders.end())
01155 {
01156 res = iter->second;
01157 }
01158 return res;
01159 }
01160
01161 LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
01162 {
01163 LLTextureCacheWorker* res = NULL;
01164 handle_map_t::iterator iter = mWriters.find(handle);
01165 if (iter != mWriters.end())
01166 {
01167 res = iter->second;
01168 }
01169 return res;
01170 }
01171
01173
01174
01175 S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
01176 {
01177 bool retry = false;
01178 S32 idx = -1;
01179
01180 {
01181 LLMutexLock lock(&mHeaderMutex);
01182 id_map_t::iterator iter = mHeaderIDMap.find(id);
01183 if (iter != mHeaderIDMap.end())
01184 {
01185 idx = iter->second;
01186 }
01187 else if (touch && !mReadOnly)
01188 {
01189 if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
01190 {
01191
01192 idx = mHeaderEntriesInfo.mEntries++;
01193 mHeaderIDMap[id] = idx;
01194
01195 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
01196 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
01197 }
01198 else if (!mLRU.empty())
01199 {
01200 idx = mLRU.begin()->first;
01201 const LLUUID& oldid = mLRU.begin()->second;
01202 mHeaderIDMap.erase(oldid);
01203 mTexturesSizeMap.erase(oldid);
01204 mHeaderIDMap[id] = idx;
01205 }
01206 else
01207 {
01208 idx = -1;
01209 retry = true;
01210 }
01211 }
01212 if (idx >= 0)
01213 {
01214 if (touch && !mReadOnly)
01215 {
01216
01217 mLRU.erase(idx);
01218 llassert_always(imagesize && *imagesize > 0);
01219 Entry* entry = new Entry(id, *imagesize, time(NULL));
01220 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01221 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
01222 (U8*)entry, offset, sizeof(Entry));
01223 delete entry;
01224 }
01225 else if (imagesize)
01226 {
01227
01228 Entry entry;
01229 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01230 ll_apr_file_read_ex(mHeaderEntriesFileName, getFileAPRPool(),
01231 (U8*)&entry, offset, sizeof(Entry));
01232 *imagesize = entry.mSize;
01233 }
01234 }
01235 }
01236 if (retry)
01237 {
01238 readHeaderCache(getFileAPRPool());
01239 llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
01240 idx = getHeaderCacheEntry(id, touch, imagesize);
01241 }
01242 return idx;
01243 }
01244
01246
01247
01248
01249 LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 priority,
01250 S32 offset, S32 size, ReadResponder* responder)
01251 {
01252
01253
01254 LLMutexLock lock(&mWorkersMutex);
01255 LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
01256 NULL, size, offset, 0,
01257 responder);
01258 handle_t handle = worker->read();
01259 mReaders[handle] = worker;
01260 return handle;
01261 }
01262
01263 bool LLTextureCache::readComplete(handle_t handle, bool abort)
01264 {
01265 lockWorkers();
01266 handle_map_t::iterator iter = mReaders.find(handle);
01267 llassert_always(iter != mReaders.end());
01268 LLTextureCacheWorker* worker = iter->second;
01269 bool res = worker->complete();
01270 if (res || abort)
01271 {
01272 mReaders.erase(handle);
01273 unlockWorkers();
01274 worker->scheduleDelete();
01275 return true;
01276 }
01277 else
01278 {
01279 unlockWorkers();
01280 return false;
01281 }
01282 }
01283
01284 LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority,
01285 U8* data, S32 datasize, S32 imagesize,
01286 WriteResponder* responder)
01287 {
01288 if (mReadOnly)
01289 {
01290 delete responder;
01291 return LLWorkerThread::nullHandle();
01292 }
01293 if (mDoPurge)
01294 {
01295
01296
01297
01298 purgeTextures(false);
01299 mDoPurge = FALSE;
01300 }
01301 if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
01302 {
01303 LLMutexLock lock(&mWorkersMutex);
01304 llassert_always(imagesize > 0);
01305 LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
01306 data, datasize, 0,
01307 imagesize, responder);
01308 handle_t handle = worker->write();
01309 mWriters[handle] = worker;
01310 return handle;
01311 }
01312 delete responder;
01313 return LLWorkerThread::nullHandle();
01314 }
01315
01316 bool LLTextureCache::writeComplete(handle_t handle, bool abort)
01317 {
01318 lockWorkers();
01319 handle_map_t::iterator iter = mWriters.find(handle);
01320 llassert_always(iter != mWriters.end());
01321 LLTextureCacheWorker* worker = iter->second;
01322 if (worker->complete() || abort)
01323 {
01324 mWriters.erase(handle);
01325 unlockWorkers();
01326 worker->scheduleDelete();
01327 return true;
01328 }
01329 else
01330 {
01331 unlockWorkers();
01332 return false;
01333 }
01334 }
01335
01336 void LLTextureCache::prioritizeWrite(handle_t handle)
01337 {
01338
01339
01340 LLMutexLock lock(&mListMutex);
01341 mPrioritizeWriteList.push_back(handle);
01342 }
01343
01344 void LLTextureCache::addCompleted(Responder* responder, bool success)
01345 {
01346 LLMutexLock lock(&mListMutex);
01347 mCompletedList.push_back(std::make_pair(responder,success));
01348 }
01349
01351
01352
01353
01354 bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
01355 {
01356 if (mReadOnly)
01357 {
01358 return false;
01359 }
01360 LLMutexLock lock(&mHeaderMutex);
01361 id_map_t::iterator iter = mHeaderIDMap.find(id);
01362 if (iter != mHeaderIDMap.end())
01363 {
01364 S32 idx = iter->second;
01365 if (idx >= 0)
01366 {
01367 Entry* entry = new Entry(id, -1, time(NULL));
01368 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
01369 ll_apr_file_write_ex(mHeaderEntriesFileName, NULL,
01370 (U8*)entry, offset, sizeof(Entry));
01371 delete entry;
01372 mLRU[idx] = id;
01373 mHeaderIDMap.erase(id);
01374 mTexturesSizeMap.erase(id);
01375 return true;
01376 }
01377 }
01378 return false;
01379 }
01380
01381 void LLTextureCache::removeFromCache(const LLUUID& id)
01382 {
01383 llwarns << "Removing texture from cache: " << id << llendl;
01384 if (!mReadOnly)
01385 {
01386 removeHeaderCacheEntry(id);
01387 ll_apr_file_remove(getTextureFileName(id), NULL);
01388 }
01389 }
01390
01392
01393 LLTextureCache::ReadResponder::ReadResponder()
01394 : mImageSize(0),
01395 mImageLocal(FALSE)
01396 {
01397 }
01398
01399 void LLTextureCache::ReadResponder::setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
01400 {
01401 if (mFormattedImage.notNull())
01402 {
01403 llassert_always(mFormattedImage->getCodec() == imageformat);
01404 mFormattedImage->appendData(data, datasize);
01405 }
01406 else
01407 {
01408 mFormattedImage = LLImageFormatted::createFromType(imageformat);
01409 mFormattedImage->setData(data,datasize);
01410 }
01411 mImageSize = imagesize;
01412 mImageLocal = imagelocal;
01413 }
01414