aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/lltexturecache.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:50 -0500
committerJacek Antonelli2008-08-15 23:44:50 -0500
commit89fe5dab825a62a0e3fd8d248cbc91c65eb2a426 (patch)
treebcff14b7888d04a2fec799c59369f6095224bd08 /linden/indra/newview/lltexturecache.cpp
parentSecond Life viewer sources 1.13.3.2 (diff)
downloadmeta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.zip
meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.gz
meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.bz2
meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.xz
Second Life viewer sources 1.14.0.0
Diffstat (limited to 'linden/indra/newview/lltexturecache.cpp')
-rw-r--r--linden/indra/newview/lltexturecache.cpp1409
1 files changed, 1409 insertions, 0 deletions
diff --git a/linden/indra/newview/lltexturecache.cpp b/linden/indra/newview/lltexturecache.cpp
new file mode 100644
index 0000000..084211a
--- /dev/null
+++ b/linden/indra/newview/lltexturecache.cpp
@@ -0,0 +1,1409 @@
1/**
2 * @file texturecache.cpp
3 * @brief Object which handles local texture caching
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "lltexturecache.h"
31
32#include "llapr.h"
33#include "lldir.h"
34#include "llimage.h"
35#include "lllfsthread.h"
36#include "llviewercontrol.h"
37
38#define USE_LFS_READ 0
39#define USE_LFS_WRITE 0
40
41// Note: first 4 bytes store file size, rest is j2c data
42const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024;
43
44class LLTextureCacheWorker : public LLWorkerClass
45{
46 friend class LLTextureCache;
47
48private:
49 enum e_state
50 {
51 INIT = 0,
52 LOCAL = 1,
53 CACHE = 2,
54 HEADER = 3,
55 BODY = 4
56 };
57
58 class ReadResponder : public LLLFSThread::Responder
59 {
60 public:
61 ReadResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
62 ~ReadResponder() {}
63 void completed(S32 bytes)
64 {
65 mCache->lockWorkers();
66 LLTextureCacheWorker* reader = mCache->getReader(mHandle);
67 if (reader) reader->ioComplete(bytes);
68 mCache->unlockWorkers();
69 }
70 LLTextureCache* mCache;
71 LLTextureCacheWorker::handle_t mHandle;
72 };
73
74 class WriteResponder : public LLLFSThread::Responder
75 {
76 public:
77 WriteResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
78 ~WriteResponder() {}
79 void completed(S32 bytes)
80 {
81 mCache->lockWorkers();
82 LLTextureCacheWorker* writer = mCache->getWriter(mHandle);
83 if (writer) writer->ioComplete(bytes);
84 mCache->unlockWorkers();
85 }
86 LLTextureCache* mCache;
87 LLTextureCacheWorker::handle_t mHandle;
88 };
89
90public:
91 LLTextureCacheWorker(LLTextureCache* cache, U32 priority, const LLUUID& id,
92 U8* data, S32 datasize, S32 offset,
93 S32 imagesize, // for writes
94 LLTextureCache::Responder* responder)
95 : LLWorkerClass(cache, "LLTextureCacheWorker"),
96 mCache(cache),
97 mPriority(priority),
98 mID(id),
99 mState(INIT),
100 mReadData(NULL),
101 mWriteData(data),
102 mDataSize(datasize),
103 mOffset(offset),
104 mImageSize(imagesize),
105 mImageFormat(IMG_CODEC_J2C),
106 mImageLocal(FALSE),
107 mResponder(responder),
108 mFileHandle(LLLFSThread::nullHandle()),
109 mBytesToRead(0),
110 mBytesRead(0)
111 {
112 mPriority &= LLWorkerThread::PRIORITY_LOWBITS;
113 }
114 ~LLTextureCacheWorker()
115 {
116 llassert_always(!haveWork());
117 delete[] mReadData;
118 }
119
120 bool doRead();
121 bool doWrite();
122 virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
123
124 handle_t read() { addWork(0, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
125 handle_t write() { addWork(1, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
126 bool complete() { return checkWork(); }
127 void ioComplete(S32 bytes)
128 {
129 mBytesRead = bytes;
130 setPriority(LLWorkerThread::PRIORITY_HIGH | mPriority);
131 }
132
133private:
134 virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
135 virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
136 virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
137
138private:
139 LLTextureCache* mCache;
140 U32 mPriority;
141 LLUUID mID;
142 e_state mState;
143
144 U8* mReadData;
145 U8* mWriteData;
146 S32 mDataSize;
147 S32 mOffset;
148 S32 mImageSize;
149 S32 mImageFormat;
150 BOOL mImageLocal;
151 LLPointer<LLTextureCache::Responder> mResponder;
152 LLLFSThread::handle_t mFileHandle;
153 S32 mBytesToRead;
154 LLAtomicS32 mBytesRead;
155};
156
157//virtual
158void LLTextureCacheWorker::startWork(S32 param)
159{
160}
161
162bool LLTextureCacheWorker::doRead()
163{
164 S32 local_size = 0;
165 std::string local_filename;
166
167 if (mState == INIT)
168 {
169 std::string filename = mCache->getLocalFileName(mID);
170 local_filename = filename + ".j2c";
171 local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
172 if (local_size == 0)
173 {
174 local_filename = filename + ".tga";
175 local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
176 if (local_size > 0)
177 {
178 mImageFormat = IMG_CODEC_TGA;
179 mDataSize = local_size; // Only a complete .tga file is valid
180 }
181 }
182 if (local_size > 0)
183 {
184 mState = LOCAL;
185 }
186 else
187 {
188 mState = CACHE;
189 }
190 }
191
192 if (mState == LOCAL)
193 {
194#if USE_LFS_READ
195 if (mFileHandle == LLLFSThread::nullHandle())
196 {
197 mImageLocal = TRUE;
198 mImageSize = local_size;
199 if (!mDataSize || mDataSize + mOffset > local_size)
200 {
201 mDataSize = local_size - mOffset;
202 }
203 if (mDataSize <= 0)
204 {
205 // no more data to read
206 mDataSize = 0;
207 return true;
208 }
209 mReadData = new U8[mDataSize];
210 mBytesRead = -1;
211 mBytesToRead = mDataSize;
212 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
213 mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
214 new ReadResponder(mCache, mRequestHandle));
215 return false;
216 }
217 else
218 {
219 if (mBytesRead >= 0)
220 {
221 if (mBytesRead != mBytesToRead)
222 {
223 llwarns << "Error reading file from local cache: " << local_filename
224 << " Bytes: " << mDataSize << " Offset: " << mOffset
225 << " / " << mDataSize << llendl;
226 mDataSize = 0; // failed
227 delete[] mReadData;
228 mReadData = NULL;
229 }
230 return true;
231 }
232 else
233 {
234 return false;
235 }
236 }
237#else
238 if (!mDataSize || mDataSize > local_size)
239 {
240 mDataSize = local_size;
241 }
242 mReadData = new U8[mDataSize];
243 S32 bytes_read = ll_apr_file_read_ex(local_filename, mCache->getFileAPRPool(),
244 mReadData, mOffset, mDataSize);
245 if (bytes_read != mDataSize)
246 {
247 llwarns << "Error reading file from local cache: " << local_filename
248 << " Bytes: " << mDataSize << " Offset: " << mOffset
249 << " / " << mDataSize << llendl;
250 mDataSize = 0;
251 delete[] mReadData;
252 mReadData = NULL;
253 }
254 else
255 {
256 mImageSize = local_size;
257 mImageLocal = TRUE;
258 }
259 return true;
260#endif
261 }
262
263 S32 idx = -1;
264
265 if (mState == CACHE)
266 {
267 llassert_always(mImageSize == 0);
268 idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
269 if (idx >= 0 && mImageSize > mOffset)
270 {
271 llassert_always(mImageSize > 0);
272 if (!mDataSize || mDataSize > mImageSize)
273 {
274 mDataSize = mImageSize;
275 }
276 mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
277 }
278 else
279 {
280 mDataSize = 0; // no data
281 return true;
282 }
283 }
284
285 if (mState == HEADER)
286 {
287#if USE_LFS_READ
288 if (mFileHandle == LLLFSThread::nullHandle())
289 {
290 llassert_always(idx >= 0);
291 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
292 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
293 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
294 llassert_always(mReadData == NULL);
295 mReadData = new U8[size];
296 mBytesRead = -1;
297 mBytesToRead = size;
298 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
299 mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
300 mReadData, offset, mBytesToRead,
301 new ReadResponder(mCache, mRequestHandle));
302 return false;
303 }
304 else
305 {
306 if (mBytesRead >= 0)
307 {
308 if (mBytesRead != mBytesToRead)
309 {
310 llwarns << "LLTextureCacheWorker: " << mID
311 << " incorrect number of bytes read from header: " << mBytesRead
312 << " != " << mBytesToRead << llendl;
313 mDataSize = -1; // failed
314 return true;
315 }
316 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
317 {
318 return true; // done
319 }
320 else
321 {
322 mFileHandle = LLLFSThread::nullHandle();
323 mState = BODY;
324 }
325 }
326 else
327 {
328 return false;
329 }
330 }
331#else
332 llassert_always(idx >= 0);
333 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
334 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
335 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
336 mReadData = new U8[size];
337 S32 bytes_read = ll_apr_file_read_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
338 mReadData, offset, size);
339 if (bytes_read != size)
340 {
341 llwarns << "LLTextureCacheWorker: " << mID
342 << " incorrect number of bytes read from header: " << bytes_read
343 << " / " << size << llendl;
344 mDataSize = -1; // failed
345 return true;
346 }
347 if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
348 {
349 return true; // done
350 }
351 else
352 {
353 mState = BODY;
354 }
355#endif
356 }
357
358 if (mState == BODY)
359 {
360#if USE_LFS_READ
361 if (mFileHandle == LLLFSThread::nullHandle())
362 {
363 std::string filename = mCache->getTextureFileName(mID);
364 S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
365 if (filesize > mOffset)
366 {
367 S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
368 mDataSize = llmin(datasize, mDataSize);
369 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
370 data_offset = llmax(data_offset, 0);
371 S32 file_size = mDataSize - data_offset;
372 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
373 file_offset = llmax(file_offset, 0);
374
375 llassert_always(mDataSize > 0);
376 U8* data = new U8[mDataSize];
377 if (data_offset > 0)
378 {
379 llassert_always(mReadData);
380 llassert_always(data_offset <= mDataSize);
381 memcpy(data, mReadData, data_offset);
382 delete[] mReadData;
383 mReadData = NULL;
384 }
385 llassert_always(mReadData == NULL);
386 mReadData = data;
387
388 mBytesRead = -1;
389 mBytesToRead = file_size;
390 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
391 llassert_always(data_offset + mBytesToRead <= mDataSize);
392 mFileHandle = LLLFSThread::sLocal->read(filename,
393 mReadData + data_offset, file_offset, mBytesToRead,
394 new ReadResponder(mCache, mRequestHandle));
395 return false;
396 }
397 else
398 {
399 mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
400 return true; // done
401 }
402 }
403 else
404 {
405 if (mBytesRead >= 0)
406 {
407 if (mBytesRead != mBytesToRead)
408 {
409 llwarns << "LLTextureCacheWorker: " << mID
410 << " incorrect number of bytes read from body: " << mBytesRead
411 << " != " << mBytesToRead << llendl;
412 mDataSize = -1; // failed
413 }
414 return true;
415 }
416 else
417 {
418 return false;
419 }
420 }
421#else
422 std::string filename = mCache->getTextureFileName(mID);
423 S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
424 S32 bytes_read = 0;
425 if (filesize > mOffset)
426 {
427 S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
428 mDataSize = llmin(datasize, mDataSize);
429 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
430 data_offset = llmax(data_offset, 0);
431 S32 file_size = mDataSize - data_offset;
432 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
433 file_offset = llmax(file_offset, 0);
434
435 U8* data = new U8[mDataSize];
436 if (data_offset > 0)
437 {
438 llassert_always(mReadData);
439 memcpy(data, mReadData, data_offset);
440 delete[] mReadData;
441 }
442 mReadData = data;
443 bytes_read = ll_apr_file_read_ex(filename, mCache->getFileAPRPool(),
444 mReadData + data_offset,
445 file_offset, file_size);
446 if (bytes_read != file_size)
447 {
448 llwarns << "LLTextureCacheWorker: " << mID
449 << " incorrect number of bytes read from body: " << bytes_read
450 << " / " << file_size << llendl;
451 mDataSize = -1; // failed
452 return true;
453 }
454 }
455 else
456 {
457 mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
458 }
459
460 return true;
461#endif
462 }
463
464 return false;
465}
466
467bool LLTextureCacheWorker::doWrite()
468{
469 S32 idx = -1;
470
471 if (mState == INIT)
472 {
473 llassert_always(mOffset == 0); // Currently don't support offsets
474 mState = CACHE;
475 }
476
477 // No LOCAL state for write()
478
479 if (mState == CACHE)
480 {
481 S32 cur_imagesize = 0;
482 S32 offset = mOffset;
483 idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
484 if (idx >= 0 && cur_imagesize > 0)
485 {
486 offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header
487 }
488 idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry
489 if (idx >= 0)
490 {
491 llassert_always(cur_imagesize <= 0 || mImageSize == cur_imagesize);
492 mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
493 }
494 else
495 {
496 mDataSize = -1; // failed
497 return true;
498 }
499 }
500
501 if (mState == HEADER)
502 {
503#if USE_LFS_WRITE
504 if (mFileHandle == LLLFSThread::nullHandle())
505 {
506 llassert_always(idx >= 0);
507 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
508 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
509 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
510 mBytesRead = -1;
511 mBytesToRead = size;
512 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
513 mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
514 mWriteData, offset, mBytesToRead,
515 new WriteResponder(mCache, mRequestHandle));
516 return false;
517 }
518 else
519 {
520 if (mBytesRead >= 0)
521 {
522 if (mBytesRead != mBytesToRead)
523 {
524 llwarns << "LLTextureCacheWorker: " << mID
525 << " incorrect number of bytes written to header: " << mBytesRead
526 << " != " << mBytesToRead << llendl;
527 mDataSize = -1; // failed
528 return true;
529 }
530 if (mDataSize <= mBytesToRead)
531 {
532 return true; // done
533 }
534 else
535 {
536 mFileHandle = LLLFSThread::nullHandle();
537 mState = BODY;
538 }
539 }
540 else
541 {
542 return false;
543 }
544 }
545#else
546 llassert_always(idx >= 0);
547 llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
548 S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
549 S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
550 S32 bytes_written = ll_apr_file_write_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
551 mWriteData, offset, size);
552
553 if (bytes_written <= 0)
554 {
555 llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
556 mDataSize = -1; // failed
557 return true;
558 }
559
560 if (mDataSize <= size)
561 {
562 return true; // done
563 }
564 else
565 {
566 mState = BODY;
567 }
568#endif
569 }
570
571 if (mState == BODY)
572 {
573#if USE_LFS_WRITE
574 if (mFileHandle == LLLFSThread::nullHandle())
575 {
576 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
577 data_offset = llmax(data_offset, 0);
578 S32 file_size = mDataSize - data_offset;
579 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
580 file_offset = llmax(file_offset, 0);
581 if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
582 {
583 std::string filename = mCache->getTextureFileName(mID);
584 mBytesRead = -1;
585 mBytesToRead = file_size;
586 setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
587 mFileHandle = LLLFSThread::sLocal->write(filename,
588 mWriteData + data_offset, file_offset, mBytesToRead,
589 new WriteResponder(mCache, mRequestHandle));
590 return false;
591 }
592 else
593 {
594 mDataSize = 0; // no data written
595 return true; // done
596 }
597 }
598 else
599 {
600 if (mBytesRead >= 0)
601 {
602 if (mBytesRead != mBytesToRead)
603 {
604 llwarns << "LLTextureCacheWorker: " << mID
605 << " incorrect number of bytes written to body: " << mBytesRead
606 << " != " << mBytesToRead << llendl;
607 mDataSize = -1; // failed
608 }
609 return true;
610 }
611 else
612 {
613 return false;
614 }
615 }
616#else
617 S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
618 data_offset = llmax(data_offset, 0);
619 S32 file_size = mDataSize - data_offset;
620 S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
621 file_offset = llmax(file_offset, 0);
622 S32 bytes_written = 0;
623 if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
624 {
625 std::string filename = mCache->getTextureFileName(mID);
626 bytes_written = ll_apr_file_write_ex(filename, mCache->getFileAPRPool(),
627 mWriteData + data_offset,
628 file_offset, file_size);
629 if (bytes_written <= 0)
630 {
631 mDataSize = -1; // failed
632 }
633 }
634 else
635 {
636 mDataSize = 0; // no data written
637 }
638
639 return true;
640#endif
641 }
642
643 return false;
644}
645
646//virtual
647bool LLTextureCacheWorker::doWork(S32 param)
648{
649 bool res = false;
650 if (param == 0) // read
651 {
652 res = doRead();
653 }
654 else if (param == 1) // write
655 {
656 res = doWrite();
657 }
658 else
659 {
660 llassert_always(0);
661 }
662 return res;
663}
664
665//virtual (WORKER THREAD)
666void LLTextureCacheWorker::finishWork(S32 param, bool completed)
667{
668 if (mResponder.notNull())
669 {
670 bool success = (completed && mDataSize > 0);
671 if (param == 0)
672 {
673 // read
674 if (success)
675 {
676 mResponder->setData(mReadData, mDataSize, mImageSize, mImageFormat, mImageLocal);
677 mReadData = NULL; // responder owns data
678 mDataSize = 0;
679 }
680 else
681 {
682 delete[] mReadData;
683 mReadData = NULL;
684
685 }
686 }
687 else
688 {
689 // write
690 mWriteData = NULL; // we never owned data
691 mDataSize = 0;
692 }
693 mCache->addCompleted(mResponder, success);
694 }
695}
696
697//virtual (MAIN THREAD)
698void LLTextureCacheWorker::endWork(S32 param, bool aborted)
699{
700 if (aborted)
701 {
702 // Let the destructor handle any cleanup
703 return;
704 }
705 switch(param)
706 {
707 default:
708 case 0: // read
709 case 1: // write
710 {
711 if (mDataSize < 0)
712 {
713 // failed
714 mCache->removeFromCache(mID);
715 }
716 break;
717 }
718 }
719}
720
721//////////////////////////////////////////////////////////////////////////////
722
723LLTextureCache::LLTextureCache(bool threaded)
724 : LLWorkerThread("TextureCache", threaded),
725 mWorkersMutex(getAPRPool()),
726 mHeaderMutex(getAPRPool()),
727 mListMutex(getAPRPool()),
728 mFileAPRPool(NULL),
729 mReadOnly(FALSE),
730 mTexturesSizeTotal(0),
731 mDoPurge(FALSE)
732{
733 apr_pool_create(&mFileAPRPool, NULL);
734}
735
736LLTextureCache::~LLTextureCache()
737{
738 apr_pool_destroy(mFileAPRPool);
739}
740
741//////////////////////////////////////////////////////////////////////////////
742
743//virtual
744S32 LLTextureCache::update(U32 max_time_ms)
745{
746 S32 res;
747 res = LLWorkerThread::update(max_time_ms);
748
749 mListMutex.lock();
750 handle_list_t priorty_list = mPrioritizeWriteList; // copy list
751 mPrioritizeWriteList.clear();
752 responder_list_t completed_list = mCompletedList; // copy list
753 mCompletedList.clear();
754 mListMutex.unlock();
755
756 lockWorkers();
757
758 for (handle_list_t::iterator iter1 = priorty_list.begin();
759 iter1 != priorty_list.end(); ++iter1)
760 {
761 handle_t handle = *iter1;
762 handle_map_t::iterator iter2 = mWriters.find(handle);
763 if(iter2 != mWriters.end())
764 {
765 LLTextureCacheWorker* worker = iter2->second;
766 worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mPriority);
767 }
768 }
769
770 for (responder_list_t::iterator iter1 = completed_list.begin();
771 iter1 != completed_list.end(); ++iter1)
772 {
773 Responder *responder = iter1->first;
774 bool success = iter1->second;
775 responder->completed(success);
776 }
777
778 unlockWorkers();
779
780 return res;
781}
782
783//////////////////////////////////////////////////////////////////////////////
784
785std::string LLTextureCache::getLocalFileName(const LLUUID& id)
786{
787 // Does not include extension
788 std::string idstr = id.asString();
789 std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", idstr);
790 return filename;
791}
792
793std::string LLTextureCache::getTextureFileName(const LLUUID& id)
794{
795 std::string idstr = id.asString();
796 std::string delem = gDirUtilp->getDirDelimiter();
797 std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
798 return filename;
799}
800
801bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
802{
803 bool res = false;
804 bool purge = false;
805 // Append UUID to end of texture entries
806 {
807 LLMutexLock lock(&mHeaderMutex);
808 size_map_t::iterator iter = mTexturesSizeMap.find(id);
809 if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
810 {
811 llassert_always(bodysize > 0);
812 Entry* entry = new Entry(id, bodysize, time(NULL));
813 ll_apr_file_write_ex(mTexturesDirEntriesFileName, getFileAPRPool(),
814 (U8*)entry, -1, 1*sizeof(Entry));
815 delete entry;
816 if (iter != mTexturesSizeMap.end())
817 {
818 mTexturesSizeTotal -= iter->second;
819 }
820 mTexturesSizeTotal += bodysize;
821 mTexturesSizeMap[id] = bodysize;
822 if (mTexturesSizeTotal > sCacheMaxTexturesSize)
823 {
824 purge = true;
825 }
826 res = true;
827 }
828 }
829 if (purge)
830 {
831 mDoPurge = TRUE;
832 }
833 return res;
834}
835
836//////////////////////////////////////////////////////////////////////////////
837
838//static
839const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB
840F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
841U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
842S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
843const char* entries_filename = "texture.entries";
844const char* cache_filename = "texture.cache";
845const char* textures_dirname = "textures";
846
847void LLTextureCache::setDirNames(ELLPath location)
848{
849 std::string delem = gDirUtilp->getDirDelimiter();
850 mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
851 mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
852 mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
853 mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
854}
855
856void LLTextureCache::purgeCache(ELLPath location)
857{
858 if (!mReadOnly)
859 {
860 setDirNames(location);
861
862 ll_apr_file_remove(mHeaderEntriesFileName, NULL);
863 ll_apr_file_remove(mHeaderDataFileName, NULL);
864 }
865 purgeAllTextures(true);
866}
867
868S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
869{
870 mReadOnly = read_only;
871
872 S64 header_size = (max_size * 2) / 10;
873 S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE;
874 sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries));
875 header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE;
876 max_size -= header_size;
877 if (sCacheMaxTexturesSize > 0)
878 sCacheMaxTexturesSize = llmin(sCacheMaxTexturesSize, max_size);
879 else
880 sCacheMaxTexturesSize = max_size;
881 max_size -= sCacheMaxTexturesSize;
882
883 llinfos << "TEXTURE CACHE: Headers: " << sCacheMaxEntries
884 << " Textures size: " << sCacheMaxTexturesSize/(1024*1024) << " MB" << llendl;
885
886 setDirNames(location);
887
888 if (!mReadOnly)
889 {
890 LLFile::mkdir(mTexturesDirName.c_str());
891 const char* subdirs = "0123456789abcdef";
892 for (S32 i=0; i<16; i++)
893 {
894 std::string dirname = mTexturesDirName + gDirUtilp->getDirDelimiter() + subdirs[i];
895 LLFile::mkdir(dirname.c_str());
896 }
897 }
898 readHeaderCache();
899 purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it
900
901 return max_size; // unused cache space
902}
903
904struct lru_data
905{
906 lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
907 U32 time;
908 S32 index;
909 LLUUID uuid;
910 struct Compare
911 {
912 // lhs < rhs
913 typedef const lru_data* lru_data_ptr;
914 bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
915 {
916 if (!(a->time < b->time))
917 return true;
918 else if (!(b->time < a->time))
919 return false;
920 else
921 return a->index < b->index;
922 }
923 };
924};
925
926// Called from either the main thread or the worker thread
927void LLTextureCache::readHeaderCache(apr_pool_t* poolp)
928{
929 LLMutexLock lock(&mHeaderMutex);
930 mHeaderEntriesInfo.mVersion = 0.f;
931 mHeaderEntriesInfo.mEntries = 0;
932 if (ll_apr_file_exists(mHeaderEntriesFileName, poolp))
933 {
934 ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
935 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
936 }
937 if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
938 {
939 if (!mReadOnly)
940 {
941 // Info with 0 entries
942 mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
943 ll_apr_file_write_ex(mHeaderEntriesFileName, poolp,
944 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
945 }
946 }
947 else
948 {
949 S32 num_entries = mHeaderEntriesInfo.mEntries;
950 if (num_entries)
951 {
952 Entry* entries = new Entry[num_entries];
953 ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
954 (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry));
955 typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
956 lru_set_t lru;
957 for (S32 i=0; i<num_entries; i++)
958 {
959 if (entries[i].mSize >= 0) // -1 indicates erased entry, skip
960 {
961 const LLUUID& id = entries[i].mID;
962 lru.insert(new lru_data(entries[i].mTime, i, id));
963 mHeaderIDMap[id] = i;
964 }
965 }
966 mLRU.clear();
967 S32 lru_entries = sCacheMaxEntries / 10;
968 for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
969 {
970 lru_data* data = *iter;
971 mLRU[data->index] = data->uuid;
972 if (--lru_entries <= 0)
973 break;
974 }
975 for_each(lru.begin(), lru.end(), DeletePointer());
976 delete[] entries;
977 }
978 }
979}
980
981//////////////////////////////////////////////////////////////////////////////
982
983void LLTextureCache::purgeAllTextures(bool purge_directories)
984{
985 if (!mReadOnly)
986 {
987 const char* subdirs = "0123456789abcdef";
988 std::string delem = gDirUtilp->getDirDelimiter();
989 std::string mask = delem + "*";
990 for (S32 i=0; i<16; i++)
991 {
992 std::string dirname = mTexturesDirName + delem + subdirs[i];
993 gDirUtilp->deleteFilesInDir(dirname.c_str(),mask);
994 if (purge_directories)
995 {
996 LLFile::rmdir(dirname.c_str());
997 }
998 }
999 ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
1000 if (purge_directories)
1001 {
1002 LLFile::rmdir(mTexturesDirName.c_str());
1003 }
1004 }
1005 mTexturesSizeMap.clear();
1006}
1007
1008void LLTextureCache::purgeTextures(bool validate)
1009{
1010 if (mReadOnly)
1011 {
1012 return;
1013 }
1014
1015 LLMutexLock lock(&mHeaderMutex);
1016
1017 S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL);
1018 S32 num_entries = filesize / sizeof(Entry);
1019 if (num_entries * (S32)sizeof(Entry) != filesize)
1020 {
1021 llwarns << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << llendl;
1022 purgeAllTextures(false);
1023 return;
1024 }
1025 if (num_entries == 0)
1026 {
1027 return; // nothing to do
1028 }
1029
1030 Entry* entries = new Entry[num_entries];
1031 S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL,
1032 (U8*)entries, 0, num_entries*sizeof(Entry));
1033 if (bytes_read != filesize)
1034 {
1035 llwarns << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << llendl;
1036 purgeAllTextures(false);
1037 return;
1038 }
1039
1040 llinfos << "TEXTURE CACHE: Reading Entries..." << llendl;
1041
1042 std::map<LLUUID, S32> entry_idx_map;
1043 S64 total_size = 0;
1044 for (S32 idx=0; idx<num_entries; idx++)
1045 {
1046 const LLUUID& id = entries[idx].mID;
1047// llinfos << "Entry: " << id << " Size: " << entries[i].mSize << " Time: " << entries[i].mTime << llendl;
1048 std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
1049 if (iter != entry_idx_map.end())
1050 {
1051 // Newer entry replacing older entry
1052 S32 pidx = iter->second;
1053 total_size -= entries[pidx].mSize;
1054 entries[pidx].mSize = 0; // flag: skip older entry
1055 }
1056 entry_idx_map[id] = idx;
1057 total_size += entries[idx].mSize;
1058 }
1059
1060 U32 validate_idx = 0;
1061 if (validate)
1062 {
1063 validate_idx = gSavedSettings.getU32("CacheValidateCounter");
1064 U32 next_idx = (++validate_idx) % 256;
1065 gSavedSettings.setU32("CacheValidateCounter", next_idx);
1066 llinfos << "TEXTURE CACHE: Validating: " << validate_idx << llendl;
1067 }
1068
1069 S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
1070 S32 purge_count = 0;
1071 S32 next_idx = 0;
1072 for (S32 idx=0; idx<num_entries; idx++)
1073 {
1074 if (entries[idx].mSize == 0)
1075 {
1076 continue;
1077 }
1078 bool purge_entry = false;
1079 std::string filename = getTextureFileName(entries[idx].mID);
1080 if (total_size >= min_cache_size)
1081 {
1082 purge_entry = true;
1083 }
1084 else if (validate)
1085 {
1086 // make sure file exists and is the correct size
1087 S32 uuididx = entries[idx].mID.mData[0];
1088 if (uuididx == validate_idx)
1089 {
1090// llinfos << "Validating: " << filename << "Size: " << entries[idx].mSize << llendl;
1091 S32 bodysize = ll_apr_file_size(filename, NULL);
1092 if (bodysize != entries[idx].mSize)
1093 {
1094 llwarns << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
1095 << filename << llendl;
1096 purge_entry = true;
1097 }
1098 }
1099 }
1100 if (purge_entry)
1101 {
1102 purge_count++;
1103// llinfos << "PURGING: " << filename << llendl;
1104 ll_apr_file_remove(filename, NULL);
1105 total_size -= entries[idx].mSize;
1106 entries[idx].mSize = 0;
1107 }
1108 else
1109 {
1110 if (next_idx != idx)
1111 {
1112 entries[next_idx] = entries[idx];
1113 }
1114 ++next_idx;
1115 }
1116 }
1117 num_entries = next_idx;
1118
1119 llinfos << "TEXTURE CACHE: Writing Entries: " << num_entries << llendl;
1120
1121 ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
1122 ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL,
1123 (U8*)&entries[0], 0, num_entries*sizeof(Entry));
1124
1125 mTexturesSizeTotal = 0;
1126 mTexturesSizeMap.clear();
1127 for (S32 idx=0; idx<num_entries; idx++)
1128 {
1129 mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
1130 mTexturesSizeTotal += entries[idx].mSize;
1131 }
1132 llassert(mTexturesSizeTotal == total_size);
1133
1134 delete[] entries;
1135
1136 llinfos << "TEXTURE CACHE:"
1137 << " PURGED: " << purge_count
1138 << " ENTRIES: " << num_entries
1139 << " CACHE SIZE: " << total_size / 1024*1024 << " MB"
1140 << llendl;
1141}
1142
1143//////////////////////////////////////////////////////////////////////////////
1144
1145// call lockWorkers() first!
1146LLTextureCacheWorker* LLTextureCache::getReader(handle_t handle)
1147{
1148 LLTextureCacheWorker* res = NULL;
1149 handle_map_t::iterator iter = mReaders.find(handle);
1150 if (iter != mReaders.end())
1151 {
1152 res = iter->second;
1153 }
1154 return res;
1155}
1156
1157LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
1158{
1159 LLTextureCacheWorker* res = NULL;
1160 handle_map_t::iterator iter = mWriters.find(handle);
1161 if (iter != mWriters.end())
1162 {
1163 res = iter->second;
1164 }
1165 return res;
1166}
1167
1168//////////////////////////////////////////////////////////////////////////////
1169
1170// Called from work thread
1171S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
1172{
1173 bool retry = false;
1174 S32 idx = -1;
1175
1176 {
1177 LLMutexLock lock(&mHeaderMutex);
1178 id_map_t::iterator iter = mHeaderIDMap.find(id);
1179 if (iter != mHeaderIDMap.end())
1180 {
1181 idx = iter->second;
1182 }
1183 else if (touch && !mReadOnly)
1184 {
1185 if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
1186 {
1187 // Add an entry
1188 idx = mHeaderEntriesInfo.mEntries++;
1189 mHeaderIDMap[id] = idx;
1190 // Update Info
1191 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
1192 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
1193 }
1194 else if (!mLRU.empty())
1195 {
1196 idx = mLRU.begin()->first; // will be erased below
1197 const LLUUID& oldid = mLRU.begin()->second;
1198 mHeaderIDMap.erase(oldid);
1199 mTexturesSizeMap.erase(oldid);
1200 mHeaderIDMap[id] = idx;
1201 }
1202 else
1203 {
1204 idx = -1;
1205 retry = true;
1206 }
1207 }
1208 if (idx >= 0)
1209 {
1210 if (touch && !mReadOnly)
1211 {
1212 // Update the lru entry
1213 mLRU.erase(idx);
1214 llassert_always(imagesize && *imagesize > 0);
1215 Entry* entry = new Entry(id, *imagesize, time(NULL));
1216 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
1217 ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
1218 (U8*)entry, offset, sizeof(Entry));
1219 delete entry;
1220 }
1221 else if (imagesize)
1222 {
1223 // Get the image size
1224 Entry entry;
1225 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
1226 ll_apr_file_read_ex(mHeaderEntriesFileName, getFileAPRPool(),
1227 (U8*)&entry, offset, sizeof(Entry));
1228 *imagesize = entry.mSize;
1229 }
1230 }
1231 }
1232 if (retry)
1233 {
1234 readHeaderCache(getFileAPRPool()); // updates the lru
1235 llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
1236 idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion
1237 }
1238 return idx;
1239}
1240
1241//////////////////////////////////////////////////////////////////////////////
1242
1243// Calls from texture pipeline thread (i.e. LLTextureFetch)
1244
1245LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 priority,
1246 S32 offset, S32 size, ReadResponder* responder)
1247{
1248 // Note: checking to see if an entry exists can cause a stall,
1249 // so let the thread handle it
1250 LLMutexLock lock(&mWorkersMutex);
1251 LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
1252 NULL, size, offset, 0,
1253 responder);
1254 handle_t handle = worker->read();
1255 mReaders[handle] = worker;
1256 return handle;
1257}
1258
1259bool LLTextureCache::readComplete(handle_t handle, bool abort)
1260{
1261 lockWorkers();
1262 handle_map_t::iterator iter = mReaders.find(handle);
1263 llassert_always(iter != mReaders.end());
1264 LLTextureCacheWorker* worker = iter->second;
1265 bool res = worker->complete();
1266 if (res || abort)
1267 {
1268 mReaders.erase(handle);
1269 unlockWorkers();
1270 worker->scheduleDelete();
1271 return true;
1272 }
1273 else
1274 {
1275 unlockWorkers();
1276 return false;
1277 }
1278}
1279
1280LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority,
1281 U8* data, S32 datasize, S32 imagesize,
1282 WriteResponder* responder)
1283{
1284 if (mReadOnly)
1285 {
1286 return LLWorkerThread::nullHandle();
1287 }
1288 if (mDoPurge)
1289 {
1290 // NOTE: This may cause an occasional hiccup,
1291 // but it really needs to be done on the control thread
1292 // (i.e. here)
1293 purgeTextures(false);
1294 mDoPurge = FALSE;
1295 }
1296 if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
1297 {
1298 LLMutexLock lock(&mWorkersMutex);
1299 llassert_always(imagesize > 0);
1300 LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
1301 data, datasize, 0,
1302 imagesize, responder);
1303 handle_t handle = worker->write();
1304 mWriters[handle] = worker;
1305 return handle;
1306 }
1307 return LLWorkerThread::nullHandle();
1308}
1309
1310bool LLTextureCache::writeComplete(handle_t handle, bool abort)
1311{
1312 lockWorkers();
1313 handle_map_t::iterator iter = mWriters.find(handle);
1314 llassert_always(iter != mWriters.end());
1315 LLTextureCacheWorker* worker = iter->second;
1316 if (worker->complete() || abort)
1317 {
1318 mWriters.erase(handle);
1319 unlockWorkers();
1320 worker->scheduleDelete();
1321 return true;
1322 }
1323 else
1324 {
1325 unlockWorkers();
1326 return false;
1327 }
1328}
1329
1330void LLTextureCache::prioritizeWrite(handle_t handle)
1331{
1332 // Don't prioritize yet, we might be working on this now
1333 // which could create a deadlock
1334 LLMutexLock lock(&mListMutex);
1335 mPrioritizeWriteList.push_back(handle);
1336}
1337
1338void LLTextureCache::addCompleted(Responder* responder, bool success)
1339{
1340 LLMutexLock lock(&mListMutex);
1341 mCompletedList.push_back(std::make_pair(responder,success));
1342}
1343
1344//////////////////////////////////////////////////////////////////////////////
1345
1346// Called from MAIN thread (endWork())
1347
1348bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
1349{
1350 if (mReadOnly)
1351 {
1352 return false;
1353 }
1354 LLMutexLock lock(&mHeaderMutex);
1355 id_map_t::iterator iter = mHeaderIDMap.find(id);
1356 if (iter != mHeaderIDMap.end())
1357 {
1358 S32 idx = iter->second;
1359 if (idx >= 0)
1360 {
1361 Entry* entry = new Entry(id, -1, time(NULL));
1362 S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
1363 ll_apr_file_write_ex(mHeaderEntriesFileName, NULL,
1364 (U8*)entry, offset, sizeof(Entry));
1365 delete entry;
1366 mLRU[idx] = id;
1367 mHeaderIDMap.erase(id);
1368 mTexturesSizeMap.erase(id);
1369 return true;
1370 }
1371 }
1372 return false;
1373}
1374
1375void LLTextureCache::removeFromCache(const LLUUID& id)
1376{
1377 llwarns << "Removing texture from cache: " << id << llendl;
1378 if (!mReadOnly)
1379 {
1380 removeHeaderCacheEntry(id);
1381 ll_apr_file_remove(getTextureFileName(id), NULL);
1382 }
1383}
1384
1385//////////////////////////////////////////////////////////////////////////////
1386
1387LLTextureCache::ReadResponder::ReadResponder()
1388 : mImageSize(0),
1389 mImageLocal(FALSE)
1390{
1391}
1392
1393void LLTextureCache::ReadResponder::setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
1394{
1395 if (mFormattedImage.notNull())
1396 {
1397 llassert_always(mFormattedImage->getCodec() == imageformat);
1398 mFormattedImage->appendData(data, datasize);
1399 }
1400 else
1401 {
1402 mFormattedImage = LLImageFormatted::createFromType(imageformat);
1403 mFormattedImage->setData(data,datasize);
1404 }
1405 mImageSize = imagesize;
1406 mImageLocal = imagelocal;
1407}
1408
1409//////////////////////////////////////////////////////////////////////////////