diff options
Diffstat (limited to 'linden/indra/newview/lltexturecache.cpp')
-rw-r--r-- | linden/indra/newview/lltexturecache.cpp | 1268 |
1 files changed, 626 insertions, 642 deletions
diff --git a/linden/indra/newview/lltexturecache.cpp b/linden/indra/newview/lltexturecache.cpp index 9c3bed2..e0bef61 100644 --- a/linden/indra/newview/lltexturecache.cpp +++ b/linden/indra/newview/lltexturecache.cpp | |||
@@ -43,11 +43,18 @@ | |||
43 | // Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout | 43 | // Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout |
44 | #include "llappviewer.h" | 44 | #include "llappviewer.h" |
45 | 45 | ||
46 | #define USE_LFS_READ 0 | 46 | // Cache organization: |
47 | #define USE_LFS_WRITE 0 | 47 | // cache/texture.entries |
48 | 48 | // Unordered array of Entry structs | |
49 | // Note: first 4 bytes store file size, rest is j2c data | 49 | // cache/texture.cache |
50 | const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024; | 50 | // First TEXTURE_CACHE_ENTRY_SIZE bytes of each texture in texture.entries in same order |
51 | // Entry size same as header packet, so we're not 0-padding unless whole image is contained in header. | ||
52 | // cache/textures/[0-F]/UUID.texture | ||
53 | // Actual texture body files | ||
54 | |||
55 | const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; | ||
56 | const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit | ||
57 | const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) | ||
51 | 58 | ||
52 | class LLTextureCacheWorker : public LLWorkerClass | 59 | class LLTextureCacheWorker : public LLWorkerClass |
53 | { | 60 | { |
@@ -309,93 +316,74 @@ void LLTextureCacheWorker::startWork(S32 param) | |||
309 | { | 316 | { |
310 | } | 317 | } |
311 | 318 | ||
319 | // This is where a texture is read from the cache system (header and body) | ||
320 | // Current assumption are: | ||
321 | // - the whole data are in a raw form, will be stored at mReadData | ||
322 | // - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) | ||
323 | // - the code supports offset reading but this is actually never exercised in the viewer | ||
312 | bool LLTextureCacheRemoteWorker::doRead() | 324 | bool LLTextureCacheRemoteWorker::doRead() |
313 | { | 325 | { |
326 | bool done = false; | ||
327 | S32 idx = -1; | ||
328 | |||
314 | S32 local_size = 0; | 329 | S32 local_size = 0; |
315 | std::string local_filename; | 330 | std::string local_filename; |
316 | 331 | ||
332 | // First state / stage : find out if the file is local | ||
317 | if (mState == INIT) | 333 | if (mState == INIT) |
318 | { | 334 | { |
319 | std::string filename = mCache->getLocalFileName(mID); | 335 | std::string filename = mCache->getLocalFileName(mID); |
320 | local_filename = filename + ".j2c"; | 336 | // Is it a JPEG2000 file? |
321 | local_size = LLAPRFile::size(local_filename); | ||
322 | if (local_size == 0) | ||
323 | { | 337 | { |
324 | local_filename = filename + ".tga"; | 338 | local_filename = filename + ".j2c"; |
325 | local_size = LLAPRFile::size(local_filename); | 339 | local_size = LLAPRFile::size(local_filename); |
326 | if (local_size > 0) | 340 | if (local_size > 0) |
327 | { | 341 | { |
328 | mImageFormat = IMG_CODEC_TGA; | 342 | mImageFormat = IMG_CODEC_J2C; |
329 | mDataSize = local_size; // Only a complete .tga file is valid | ||
330 | } | 343 | } |
331 | } | 344 | } |
332 | if (local_size > 0) | 345 | // If not, is it a jpeg file? |
333 | { | 346 | if (local_size == 0) |
334 | mState = LOCAL; | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | mState = CACHE; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | if (mState == LOCAL) | ||
343 | { | ||
344 | #if USE_LFS_READ | ||
345 | if (mFileHandle == LLLFSThread::nullHandle()) | ||
346 | { | 347 | { |
347 | mImageLocal = TRUE; | 348 | local_filename = filename + ".jpg"; |
348 | mImageSize = local_size; | 349 | local_size = LLAPRFile::size(local_filename); |
349 | if (!mDataSize || mDataSize + mOffset > local_size) | 350 | if (local_size > 0) |
350 | { | ||
351 | mDataSize = local_size - mOffset; | ||
352 | } | ||
353 | if (mDataSize <= 0) | ||
354 | { | 351 | { |
355 | // no more data to read | 352 | mImageFormat = IMG_CODEC_JPEG; |
356 | mDataSize = 0; | 353 | mDataSize = local_size; // Only a complete .jpg file is valid |
357 | return true; | ||
358 | } | 354 | } |
359 | mReadData = new U8[mDataSize]; | ||
360 | mBytesRead = -1; | ||
361 | mBytesToRead = mDataSize; | ||
362 | setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); | ||
363 | mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize, | ||
364 | new ReadResponder(mCache, mRequestHandle)); | ||
365 | return false; | ||
366 | } | 355 | } |
367 | else | 356 | // Hmm... What about a targa file? (used for UI texture mostly) |
357 | if (local_size == 0) | ||
368 | { | 358 | { |
369 | if (mBytesRead >= 0) | 359 | local_filename = filename + ".tga"; |
370 | { | 360 | local_size = LLAPRFile::size(local_filename); |
371 | if (mBytesRead != mBytesToRead) | 361 | if (local_size > 0) |
372 | { | ||
373 | // llwarns << "Error reading file from local cache: " << local_filename | ||
374 | // << " Bytes: " << mDataSize << " Offset: " << mOffset | ||
375 | // << " / " << mDataSize << llendl; | ||
376 | mDataSize = 0; // failed | ||
377 | delete[] mReadData; | ||
378 | mReadData = NULL; | ||
379 | } | ||
380 | return true; | ||
381 | } | ||
382 | else | ||
383 | { | 362 | { |
384 | return false; | 363 | mImageFormat = IMG_CODEC_TGA; |
364 | mDataSize = local_size; // Only a complete .tga file is valid | ||
385 | } | 365 | } |
386 | } | 366 | } |
387 | #else | 367 | // Determine the next stage: if we found a file, then LOCAL else CACHE |
368 | mState = (local_size > 0 ? LOCAL : CACHE); | ||
369 | } | ||
370 | |||
371 | // Second state / stage : if the file is local, load it and leave | ||
372 | if (!done && (mState == LOCAL)) | ||
373 | { | ||
374 | llassert(local_size != 0); // we're assuming there is a non empty local file here... | ||
388 | if (!mDataSize || mDataSize > local_size) | 375 | if (!mDataSize || mDataSize > local_size) |
389 | { | 376 | { |
390 | mDataSize = local_size; | 377 | mDataSize = local_size; |
391 | } | 378 | } |
379 | // Allocate read buffer | ||
392 | mReadData = new U8[mDataSize]; | 380 | mReadData = new U8[mDataSize]; |
393 | S32 bytes_read = LLAPRFile::readEx(local_filename, mReadData, mOffset, mDataSize); | 381 | S32 bytes_read = LLAPRFile::readEx(local_filename, mReadData, mOffset, mDataSize); |
394 | if (bytes_read != mDataSize) | 382 | if (bytes_read != mDataSize) |
395 | { | 383 | { |
396 | // llwarns << "Error reading file from local cache: " << local_filename | 384 | llwarns << "Error reading file from local cache: " << local_filename |
397 | // << " Bytes: " << mDataSize << " Offset: " << mOffset | 385 | << " Bytes: " << mDataSize << " Offset: " << mOffset |
398 | // << " / " << mDataSize << llendl; | 386 | << " / " << mDataSize << llendl; |
399 | mDataSize = 0; | 387 | mDataSize = 0; |
400 | delete[] mReadData; | 388 | delete[] mReadData; |
401 | mReadData = NULL; | 389 | mReadData = NULL; |
@@ -405,403 +393,273 @@ bool LLTextureCacheRemoteWorker::doRead() | |||
405 | mImageSize = local_size; | 393 | mImageSize = local_size; |
406 | mImageLocal = TRUE; | 394 | mImageLocal = TRUE; |
407 | } | 395 | } |
408 | return true; | 396 | // We're done... |
409 | #endif | 397 | done = true; |
410 | } | 398 | } |
411 | 399 | ||
412 | S32 idx = -1; | 400 | // Second state / stage : identify the cache or not... |
413 | 401 | if (!done && (mState == CACHE)) | |
414 | if (mState == CACHE) | ||
415 | { | 402 | { |
416 | llassert_always(mImageSize == 0); | 403 | idx = mCache->getHeaderCacheEntry(mID, mImageSize); |
417 | idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize); | 404 | if (idx < 0) |
418 | if (idx >= 0 && mImageSize > mOffset) | ||
419 | { | 405 | { |
420 | llassert_always(mImageSize > 0); | 406 | // The texture is *not* cached. We're done here... |
421 | if (!mDataSize || mDataSize > mImageSize) | 407 | mDataSize = 0; // no data |
422 | { | 408 | done = true; |
423 | mDataSize = mImageSize; | ||
424 | } | ||
425 | mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; | ||
426 | } | 409 | } |
427 | else | 410 | else |
428 | { | 411 | { |
429 | mDataSize = 0; // no data | 412 | // If the read offset is bigger than the header cache, we read directly from the body |
430 | return true; | 413 | // Note that currently, we *never* read with offset from the cache, so the result is *always* HEADER |
414 | mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; | ||
431 | } | 415 | } |
432 | } | 416 | } |
433 | 417 | ||
434 | if (mState == HEADER) | 418 | // Third state / stage : read data from the header cache (texture.entries) file |
419 | if (!done && (mState == HEADER)) | ||
435 | { | 420 | { |
436 | #if USE_LFS_READ | 421 | llassert_always(idx >= 0); // we need an entry here or reading the header makes no sense |
437 | if (mFileHandle == LLLFSThread::nullHandle()) | ||
438 | { | ||
439 | llassert_always(idx >= 0); | ||
440 | llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); | ||
441 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; | ||
442 | S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | ||
443 | llassert_always(mReadData == NULL); | ||
444 | mReadData = new U8[size]; | ||
445 | mBytesRead = -1; | ||
446 | mBytesToRead = size; | ||
447 | setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); | ||
448 | mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName, | ||
449 | mReadData, offset, mBytesToRead, | ||
450 | new ReadResponder(mCache, mRequestHandle)); | ||
451 | return false; | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | if (mBytesRead >= 0) | ||
456 | { | ||
457 | if (mBytesRead != mBytesToRead) | ||
458 | { | ||
459 | // llwarns << "LLTextureCacheWorker: " << mID | ||
460 | // << " incorrect number of bytes read from header: " << mBytesRead | ||
461 | // << " != " << mBytesToRead << llendl; | ||
462 | mDataSize = -1; // failed | ||
463 | return true; | ||
464 | } | ||
465 | if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) | ||
466 | { | ||
467 | return true; // done | ||
468 | } | ||
469 | else | ||
470 | { | ||
471 | mFileHandle = LLLFSThread::nullHandle(); | ||
472 | mState = BODY; | ||
473 | } | ||
474 | } | ||
475 | else | ||
476 | { | ||
477 | return false; | ||
478 | } | ||
479 | } | ||
480 | #else | ||
481 | llassert_always(idx >= 0); | ||
482 | llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); | 422 | llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); |
483 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; | 423 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; |
424 | // Compute the size we need to read (in bytes) | ||
484 | S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | 425 | S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; |
426 | size = llmin(size, mDataSize); | ||
427 | // Allocate the read buffer | ||
485 | mReadData = new U8[size]; | 428 | mReadData = new U8[size]; |
486 | S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, | 429 | S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, |
487 | mReadData, offset, size); | 430 | mReadData, offset, size); |
488 | if (bytes_read != size) | 431 | if (bytes_read != size) |
489 | { | 432 | { |
490 | // llwarns << "LLTextureCacheWorker: " << mID | 433 | llwarns << "LLTextureCacheWorker: " << mID |
491 | // << " incorrect number of bytes read from header: " << bytes_read | 434 | << " incorrect number of bytes read from header: " << bytes_read |
492 | // << " / " << size << llendl; | 435 | << " / " << size << llendl; |
436 | delete[] mReadData; | ||
437 | mReadData = NULL; | ||
493 | mDataSize = -1; // failed | 438 | mDataSize = -1; // failed |
494 | return true; | 439 | done = true; |
495 | } | 440 | } |
496 | if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) | 441 | // If we already read all we expected, we're actually done |
442 | if (mDataSize <= bytes_read) | ||
497 | { | 443 | { |
498 | return true; // done | 444 | done = true; |
499 | } | 445 | } |
500 | else | 446 | else |
501 | { | 447 | { |
502 | mState = BODY; | 448 | mState = BODY; |
503 | } | 449 | } |
504 | #endif | ||
505 | } | 450 | } |
506 | 451 | ||
507 | if (mState == BODY) | 452 | // Fourth state / stage : read the rest of the data from the UUID based cached file |
453 | if (!done && (mState == BODY)) | ||
508 | { | 454 | { |
509 | #if USE_LFS_READ | ||
510 | if (mFileHandle == LLLFSThread::nullHandle()) | ||
511 | { | ||
512 | std::string filename = mCache->getTextureFileName(mID); | ||
513 | S32 filesize = LLAPRFile::size(filename); | ||
514 | if (filesize > mOffset) | ||
515 | { | ||
516 | S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; | ||
517 | mDataSize = llmin(datasize, mDataSize); | ||
518 | S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | ||
519 | data_offset = llmax(data_offset, 0); | ||
520 | S32 file_size = mDataSize - data_offset; | ||
521 | S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; | ||
522 | file_offset = llmax(file_offset, 0); | ||
523 | |||
524 | llassert_always(mDataSize > 0); | ||
525 | U8* data = new U8[mDataSize]; | ||
526 | if (data_offset > 0) | ||
527 | { | ||
528 | llassert_always(mReadData); | ||
529 | llassert_always(data_offset <= mDataSize); | ||
530 | memcpy(data, mReadData, data_offset); | ||
531 | delete[] mReadData; | ||
532 | mReadData = NULL; | ||
533 | } | ||
534 | llassert_always(mReadData == NULL); | ||
535 | mReadData = data; | ||
536 | |||
537 | mBytesRead = -1; | ||
538 | mBytesToRead = file_size; | ||
539 | setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); | ||
540 | llassert_always(data_offset + mBytesToRead <= mDataSize); | ||
541 | mFileHandle = LLLFSThread::sLocal->read(filename, | ||
542 | mReadData + data_offset, file_offset, mBytesToRead, | ||
543 | new ReadResponder(mCache, mRequestHandle)); | ||
544 | return false; | ||
545 | } | ||
546 | else | ||
547 | { | ||
548 | mDataSize = TEXTURE_CACHE_ENTRY_SIZE; | ||
549 | return true; // done | ||
550 | } | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | if (mBytesRead >= 0) | ||
555 | { | ||
556 | if (mBytesRead != mBytesToRead) | ||
557 | { | ||
558 | // llwarns << "LLTextureCacheWorker: " << mID | ||
559 | // << " incorrect number of bytes read from body: " << mBytesRead | ||
560 | // << " != " << mBytesToRead << llendl; | ||
561 | mDataSize = -1; // failed | ||
562 | } | ||
563 | return true; | ||
564 | } | ||
565 | else | ||
566 | { | ||
567 | return false; | ||
568 | } | ||
569 | } | ||
570 | #else | ||
571 | std::string filename = mCache->getTextureFileName(mID); | 455 | std::string filename = mCache->getTextureFileName(mID); |
572 | S32 filesize = LLAPRFile::size(filename); | 456 | S32 filesize = LLAPRFile::size(filename); |
573 | S32 bytes_read = 0; | 457 | |
574 | if (filesize > mOffset) | 458 | if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) |
575 | { | 459 | { |
576 | S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; | 460 | S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset; |
577 | mDataSize = llmin(datasize, mDataSize); | 461 | mDataSize = llmin(max_datasize, mDataSize); |
578 | S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | 462 | |
579 | data_offset = llmax(data_offset, 0); | 463 | S32 data_offset, file_size, file_offset; |
580 | S32 file_size = mDataSize - data_offset; | ||
581 | S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; | ||
582 | file_offset = llmax(file_offset, 0); | ||
583 | 464 | ||
465 | // Reserve the whole data buffer first | ||
584 | U8* data = new U8[mDataSize]; | 466 | U8* data = new U8[mDataSize]; |
585 | if (data_offset > 0) | 467 | |
468 | // Set the data file pointers taking the read offset into account. 2 cases: | ||
469 | if (mOffset < TEXTURE_CACHE_ENTRY_SIZE) | ||
586 | { | 470 | { |
471 | // Offset within the header record. That means we read something from the header cache. | ||
472 | // Note: most common case is (mOffset = 0), so this is the "normal" code path. | ||
473 | data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; // i.e. TEXTURE_CACHE_ENTRY_SIZE if mOffset nul (common case) | ||
474 | file_offset = 0; | ||
475 | file_size = mDataSize - data_offset; | ||
476 | // Copy the raw data we've been holding from the header cache into the new sized buffer | ||
587 | llassert_always(mReadData); | 477 | llassert_always(mReadData); |
588 | memcpy(data, mReadData, data_offset); | 478 | memcpy(data, mReadData, data_offset); |
589 | delete[] mReadData; | 479 | delete[] mReadData; |
480 | mReadData = NULL; | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | // Offset bigger than the header record. That means we haven't read anything yet. | ||
485 | data_offset = 0; | ||
486 | file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; | ||
487 | file_size = mDataSize; | ||
488 | // No data from header cache to copy in that case, we skipped it all | ||
590 | } | 489 | } |
490 | |||
491 | // Now use that buffer as the object read buffer | ||
492 | llassert_always(mReadData == NULL); | ||
591 | mReadData = data; | 493 | mReadData = data; |
592 | bytes_read = LLAPRFile::readEx(filename, | 494 | |
495 | // Read the data at last | ||
496 | S32 bytes_read = LLAPRFile::readEx(filename, | ||
593 | mReadData + data_offset, | 497 | mReadData + data_offset, |
594 | file_offset, file_size); | 498 | file_offset, file_size); |
595 | if (bytes_read != file_size) | 499 | if (bytes_read != file_size) |
596 | { | 500 | { |
597 | // llwarns << "LLTextureCacheWorker: " << mID | 501 | llwarns << "LLTextureCacheWorker: " << mID |
598 | // << " incorrect number of bytes read from body: " << bytes_read | 502 | << " incorrect number of bytes read from body: " << bytes_read |
599 | // << " / " << file_size << llendl; | 503 | << " / " << file_size << llendl; |
504 | delete[] mReadData; | ||
505 | mReadData = NULL; | ||
600 | mDataSize = -1; // failed | 506 | mDataSize = -1; // failed |
601 | return true; | 507 | done = true; |
602 | } | 508 | } |
603 | } | 509 | } |
604 | else | 510 | else |
605 | { | 511 | { |
606 | mDataSize = TEXTURE_CACHE_ENTRY_SIZE; | 512 | // No body, we're done. |
607 | } | 513 | mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0); |
608 | 514 | lldebugs << "No body file for: " << filename << llendl; | |
609 | return true; | 515 | } |
610 | #endif | 516 | // Nothing else to do at that point... |
517 | done = true; | ||
611 | } | 518 | } |
612 | 519 | ||
613 | return false; | 520 | // Clean up and exit |
521 | return done; | ||
614 | } | 522 | } |
615 | 523 | ||
524 | // This is where *everything* about a texture is written down in the cache system (entry map, header and body) | ||
525 | // Current assumption are: | ||
526 | // - the whole data are in a raw form, starting at mWriteData | ||
527 | // - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) | ||
528 | // - the code *does not* support offset writing so there are no difference between buffer addresses and start of data | ||
616 | bool LLTextureCacheRemoteWorker::doWrite() | 529 | bool LLTextureCacheRemoteWorker::doWrite() |
617 | { | 530 | { |
531 | bool done = false; | ||
618 | S32 idx = -1; | 532 | S32 idx = -1; |
619 | 533 | ||
620 | // No LOCAL state for write() | 534 | // First state / stage : check that what we're trying to cache is in an OK shape |
621 | |||
622 | if (mState == INIT) | 535 | if (mState == INIT) |
623 | { | 536 | { |
537 | llassert_always(mOffset == 0); // We currently do not support write offsets | ||
538 | llassert_always(mDataSize > 0); // Things will go badly wrong if mDataSize is nul or negative... | ||
539 | mState = CACHE; | ||
540 | } | ||
541 | |||
542 | // No LOCAL state for write(): because it doesn't make much sense to cache a local file... | ||
543 | |||
544 | // Second state / stage : set an entry in the headers entry (texture.entries) file | ||
545 | if (!done && (mState == CACHE)) | ||
546 | { | ||
547 | bool alreadyCached = false; | ||
624 | S32 cur_imagesize = 0; | 548 | S32 cur_imagesize = 0; |
625 | S32 offset = mOffset; | 549 | // Checks if this image is already in the entry list |
626 | idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize); | 550 | idx = mCache->getHeaderCacheEntry(mID, cur_imagesize); |
627 | if (idx >= 0 && cur_imagesize > 0) | 551 | if (idx >= 0 && (cur_imagesize >= 0)) |
628 | { | 552 | { |
629 | offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header | 553 | alreadyCached = true; // already there and non empty |
630 | } | 554 | } |
631 | idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry | 555 | idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry |
632 | if (idx >= 0) | 556 | if (idx < 0) |
633 | { | 557 | { |
634 | if(cur_imagesize > 0 && mImageSize != cur_imagesize) | 558 | llwarns << "LLTextureCacheWorker: " << mID |
635 | { | 559 | << " Unable to create header entry for writing!" << llendl; |
636 | // llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl; | 560 | mDataSize = -1; // failed |
637 | offset = 0; // re-write header | 561 | done = true; |
638 | } | ||
639 | mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; | ||
640 | } | 562 | } |
641 | else | 563 | else |
642 | { | 564 | { |
643 | mDataSize = -1; // failed | 565 | if (cur_imagesize > 0 && (mImageSize != cur_imagesize)) |
644 | return true; | 566 | { |
567 | alreadyCached = false; // re-write the header if the size changed in all cases | ||
568 | } | ||
569 | if (alreadyCached && (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)) | ||
570 | { | ||
571 | // Small texture already cached case: we're done with writing | ||
572 | done = true; | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | // If the texture has already been cached, we don't resave the header and go directly to the body part | ||
577 | mState = alreadyCached ? BODY : HEADER; | ||
578 | } | ||
645 | } | 579 | } |
646 | } | 580 | } |
647 | 581 | ||
648 | if (mState == HEADER) | 582 | // Third stage / state : write the header record in the header file (texture.cache) |
583 | if (!done && (mState == HEADER)) | ||
649 | { | 584 | { |
650 | #if USE_LFS_WRITE | 585 | llassert_always(idx >= 0); // we need an entry here or storing the header makes no sense |
651 | if (mFileHandle == LLLFSThread::nullHandle()) | 586 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE; // skip to the correct spot in the header file |
587 | S32 size = TEXTURE_CACHE_ENTRY_SIZE; // record size is fixed for the header | ||
588 | S32 bytes_written; | ||
589 | |||
590 | if (mDataSize < TEXTURE_CACHE_ENTRY_SIZE) | ||
652 | { | 591 | { |
653 | llassert_always(idx >= 0); | 592 | // We need to write a full record in the header cache so, if the amount of data is smaller |
654 | llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); | 593 | // than a record, we need to transfer the data to a buffer padded with 0 and write that |
655 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; | 594 | U8* padBuffer = new U8[TEXTURE_CACHE_ENTRY_SIZE]; |
656 | S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | 595 | memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros |
657 | mBytesRead = -1; | 596 | memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer |
658 | mBytesToRead = size; | 597 | bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size); |
659 | setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); | 598 | delete [] padBuffer; |
660 | mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName, | ||
661 | mWriteData, offset, mBytesToRead, | ||
662 | new WriteResponder(mCache, mRequestHandle)); | ||
663 | return false; | ||
664 | } | 599 | } |
665 | else | 600 | else |
666 | { | 601 | { |
667 | if (mBytesRead >= 0) | 602 | // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file |
668 | { | 603 | bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size); |
669 | if (mBytesRead != mBytesToRead) | ||
670 | { | ||
671 | // llwarns << "LLTextureCacheWorker: " << mID | ||
672 | // << " incorrect number of bytes written to header: " << mBytesRead | ||
673 | // << " != " << mBytesToRead << llendl; | ||
674 | mDataSize = -1; // failed | ||
675 | return true; | ||
676 | } | ||
677 | if (mDataSize <= mBytesToRead) | ||
678 | { | ||
679 | return true; // done | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | mFileHandle = LLLFSThread::nullHandle(); | ||
684 | mState = BODY; | ||
685 | } | ||
686 | } | ||
687 | else | ||
688 | { | ||
689 | return false; | ||
690 | } | ||
691 | } | 604 | } |
692 | #else | ||
693 | llassert_always(idx >= 0); | ||
694 | llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); | ||
695 | S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; | ||
696 | S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | ||
697 | S32 bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size); | ||
698 | 605 | ||
699 | if (bytes_written <= 0) | 606 | if (bytes_written <= 0) |
700 | { | 607 | { |
701 | // llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl; | 608 | llwarns << "LLTextureCacheWorker: " << mID |
609 | << " Unable to write header entry!" << llendl; | ||
702 | mDataSize = -1; // failed | 610 | mDataSize = -1; // failed |
703 | return true; | 611 | done = true; |
704 | } | 612 | } |
705 | 613 | ||
706 | if (mDataSize <= size) | 614 | // If we wrote everything (may be more with padding) in the header cache, |
615 | // we're done so we don't have a body to store | ||
616 | if (mDataSize <= bytes_written) | ||
707 | { | 617 | { |
708 | return true; // done | 618 | done = true; |
709 | } | 619 | } |
710 | else | 620 | else |
711 | { | 621 | { |
712 | mState = BODY; | 622 | mState = BODY; |
713 | } | 623 | } |
714 | #endif | ||
715 | } | 624 | } |
716 | 625 | ||
717 | if (mState == BODY) | 626 | // Fourth stage / state : write the body file, i.e. the rest of the texture in a "UUID" file name |
627 | if (!done && (mState == BODY)) | ||
718 | { | 628 | { |
719 | #if USE_LFS_WRITE | 629 | llassert(mDataSize > TEXTURE_CACHE_ENTRY_SIZE); // wouldn't make sense to be here otherwise... |
720 | if (mFileHandle == LLLFSThread::nullHandle()) | 630 | S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE; |
631 | if ((file_size > 0) && mCache->updateTextureEntryList(mID, file_size)) | ||
721 | { | 632 | { |
722 | S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | 633 | // build the cache file name from the UUID |
723 | data_offset = llmax(data_offset, 0); | 634 | std::string filename = mCache->getTextureFileName(mID); |
724 | S32 file_size = mDataSize - data_offset; | 635 | // llinfos << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << llendl; |
725 | S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; | 636 | S32 bytes_written = LLAPRFile::writeEx( filename, |
726 | file_offset = llmax(file_offset, 0); | 637 | mWriteData + TEXTURE_CACHE_ENTRY_SIZE, |
727 | if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) | 638 | 0, file_size); |
728 | { | ||
729 | std::string filename = mCache->getTextureFileName(mID); | ||
730 | mBytesRead = -1; | ||
731 | mBytesToRead = file_size; | ||
732 | setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); | ||
733 | mFileHandle = LLLFSThread::sLocal->write(filename, | ||
734 | mWriteData + data_offset, file_offset, mBytesToRead, | ||
735 | new WriteResponder(mCache, mRequestHandle)); | ||
736 | return false; | ||
737 | } | ||
738 | else | ||
739 | { | ||
740 | mDataSize = 0; // no data written | ||
741 | return true; // done | ||
742 | } | ||
743 | } | ||
744 | else | ||
745 | { | ||
746 | if (mBytesRead >= 0) | ||
747 | { | ||
748 | if (mBytesRead != mBytesToRead) | ||
749 | { | ||
750 | // llwarns << "LLTextureCacheWorker: " << mID | ||
751 | // << " incorrect number of bytes written to body: " << mBytesRead | ||
752 | // << " != " << mBytesToRead << llendl; | ||
753 | mDataSize = -1; // failed | ||
754 | } | ||
755 | return true; | ||
756 | } | ||
757 | else | ||
758 | { | ||
759 | return false; | ||
760 | } | ||
761 | } | ||
762 | #else | ||
763 | S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; | ||
764 | data_offset = llmax(data_offset, 0); | ||
765 | S32 file_size = mDataSize - data_offset; | ||
766 | S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; | ||
767 | file_offset = llmax(file_offset, 0); | ||
768 | S32 bytes_written = 0; | ||
769 | if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) | ||
770 | { | ||
771 | std::string filename = mCache->getTextureFileName(mID); | ||
772 | |||
773 | bytes_written = LLAPRFile::writeEx(filename, | ||
774 | mWriteData + data_offset, | ||
775 | file_offset, file_size); | ||
776 | if (bytes_written <= 0) | 639 | if (bytes_written <= 0) |
777 | { | 640 | { |
641 | llwarns << "LLTextureCacheWorker: " << mID | ||
642 | << " incorrect number of bytes written to body: " << bytes_written | ||
643 | << " / " << file_size << llendl; | ||
778 | mDataSize = -1; // failed | 644 | mDataSize = -1; // failed |
645 | done = true; | ||
779 | } | 646 | } |
780 | } | 647 | } |
781 | else | 648 | else |
782 | { | 649 | { |
783 | mDataSize = 0; // no data written | 650 | mDataSize = 0; // no data written |
784 | } | 651 | } |
785 | 652 | // Nothing else to do at that point... | |
786 | return true; | 653 | done = true; |
787 | #endif | ||
788 | } | 654 | } |
789 | 655 | ||
790 | return false; | 656 | // Clean up and exit |
657 | return done; | ||
791 | } | 658 | } |
792 | 659 | ||
793 | //virtual | 660 | //virtual |
794 | bool LLTextureCacheWorker::doWork(S32 param) | 661 | bool LLTextureCacheWorker::doWork(S32 param) |
795 | { | 662 | { |
796 | // *TODO reenable disabled apr_pool usage disabled due to maint-render-9 merge breakage -brad | ||
797 | //allocate a new local apr_pool | ||
798 | // LLAPRPool pool ; | ||
799 | |||
800 | //save the current mFileAPRPool to avoid breaking anything. | ||
801 | // apr_pool_t* old_pool = mCache->getFileAPRPool() ; | ||
802 | //make mFileAPRPool to point to the local one | ||
803 | // mCache->setFileAPRPool(pool.getAPRPool()) ; | ||
804 | |||
805 | bool res = false; | 663 | bool res = false; |
806 | if (param == 0) // read | 664 | if (param == 0) // read |
807 | { | 665 | { |
@@ -815,10 +673,6 @@ bool LLTextureCacheWorker::doWork(S32 param) | |||
815 | { | 673 | { |
816 | llassert_always(0); | 674 | llassert_always(0); |
817 | } | 675 | } |
818 | |||
819 | //set mFileAPRPool back, the local one will be released automatically. | ||
820 | // mCache->setFileAPRPool(old_pool) ; | ||
821 | |||
822 | return res; | 676 | return res; |
823 | } | 677 | } |
824 | 678 | ||
@@ -884,6 +738,7 @@ LLTextureCache::LLTextureCache(bool threaded) | |||
884 | mWorkersMutex(NULL), | 738 | mWorkersMutex(NULL), |
885 | mHeaderMutex(NULL), | 739 | mHeaderMutex(NULL), |
886 | mListMutex(NULL), | 740 | mListMutex(NULL), |
741 | mHeaderAPRFile(NULL), | ||
887 | mReadOnly(FALSE), | 742 | mReadOnly(FALSE), |
888 | mTexturesSizeTotal(0), | 743 | mTexturesSizeTotal(0), |
889 | mDoPurge(FALSE) | 744 | mDoPurge(FALSE) |
@@ -892,9 +747,6 @@ LLTextureCache::LLTextureCache(bool threaded) | |||
892 | 747 | ||
893 | LLTextureCache::~LLTextureCache() | 748 | LLTextureCache::~LLTextureCache() |
894 | { | 749 | { |
895 | purgeTextureFilesTimeSliced(TRUE); // force-flush all pending file deletes | ||
896 | |||
897 | // apr_pool_destroy(mFileAPRPool); | ||
898 | } | 750 | } |
899 | 751 | ||
900 | ////////////////////////////////////////////////////////////////////////////// | 752 | ////////////////////////////////////////////////////////////////////////////// |
@@ -926,6 +778,9 @@ S32 LLTextureCache::update(U32 max_time_ms) | |||
926 | } | 778 | } |
927 | } | 779 | } |
928 | 780 | ||
781 | unlockWorkers(); | ||
782 | |||
783 | // call 'completed' with workers list unlocked (may call readComplete() or writeComplete() | ||
929 | for (responder_list_t::iterator iter1 = completed_list.begin(); | 784 | for (responder_list_t::iterator iter1 = completed_list.begin(); |
930 | iter1 != completed_list.end(); ++iter1) | 785 | iter1 != completed_list.end(); ++iter1) |
931 | { | 786 | { |
@@ -934,8 +789,6 @@ S32 LLTextureCache::update(U32 max_time_ms) | |||
934 | responder->completed(success); | 789 | responder->completed(success); |
935 | } | 790 | } |
936 | 791 | ||
937 | unlockWorkers(); | ||
938 | |||
939 | return res; | 792 | return res; |
940 | } | 793 | } |
941 | 794 | ||
@@ -954,32 +807,48 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id) | |||
954 | { | 807 | { |
955 | std::string idstr = id.asString(); | 808 | std::string idstr = id.asString(); |
956 | std::string delem = gDirUtilp->getDirDelimiter(); | 809 | std::string delem = gDirUtilp->getDirDelimiter(); |
957 | std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr; | 810 | std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr + ".texture"; |
958 | return filename; | 811 | return filename; |
959 | } | 812 | } |
960 | 813 | ||
961 | bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) | 814 | bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize) |
962 | { | 815 | { |
963 | bool res = false; | 816 | bool res = false; |
964 | bool purge = false; | 817 | bool purge = false; |
965 | // Append UUID to end of texture entries | ||
966 | { | 818 | { |
967 | LLMutexLock lock(&mHeaderMutex); | 819 | LLMutexLock lock(&mHeaderMutex); |
968 | size_map_t::iterator iter = mTexturesSizeMap.find(id); | 820 | size_map_t::iterator iter1 = mTexturesSizeMap.find(id); |
969 | if (iter == mTexturesSizeMap.end() || iter->second < bodysize) | 821 | if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize) |
970 | { | 822 | { |
971 | llassert_always(bodysize > 0); | 823 | llassert_always(bodysize > 0); |
972 | Entry* entry = new Entry(id, bodysize, time(NULL)); | ||
973 | 824 | ||
974 | LLAPRFile::writeEx(mTexturesDirEntriesFileName, | 825 | S32 oldbodysize = 0; |
975 | (U8*)entry, -1, 1*sizeof(Entry)); | 826 | if (iter1 != mTexturesSizeMap.end()) |
976 | delete entry; | ||
977 | if (iter != mTexturesSizeMap.end()) | ||
978 | { | 827 | { |
979 | mTexturesSizeTotal -= iter->second; | 828 | oldbodysize = iter1->second; |
980 | } | 829 | } |
830 | |||
831 | Entry entry; | ||
832 | S32 idx = openAndReadEntry(id, entry, false); | ||
833 | if (idx < 0) | ||
834 | { | ||
835 | // TODO: change to llwarns | ||
836 | llerrs << "Failed to open entry: " << id << llendl; | ||
837 | removeFromCache(id); | ||
838 | return false; | ||
839 | } | ||
840 | else if (oldbodysize != entry.mBodySize) | ||
841 | { | ||
842 | // TODO: change to llwarns | ||
843 | llerrs << "Entry mismatch in mTextureSizeMap / mHeaderIDMap" | ||
844 | << " idx=" << idx << " oldsize=" << oldbodysize << " entrysize=" << entry.mBodySize << llendl; | ||
845 | } | ||
846 | entry.mBodySize = bodysize; | ||
847 | writeEntryAndClose(idx, entry); | ||
848 | |||
849 | mTexturesSizeTotal -= oldbodysize; | ||
981 | mTexturesSizeTotal += bodysize; | 850 | mTexturesSizeTotal += bodysize; |
982 | mTexturesSizeMap[id] = bodysize; | 851 | |
983 | if (mTexturesSizeTotal > sCacheMaxTexturesSize) | 852 | if (mTexturesSizeTotal > sCacheMaxTexturesSize) |
984 | { | 853 | { |
985 | purge = true; | 854 | purge = true; |
@@ -998,7 +867,7 @@ bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) | |||
998 | 867 | ||
999 | //static | 868 | //static |
1000 | const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB | 869 | const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB |
1001 | F32 LLTextureCache::sHeaderCacheVersion = 1.0f; | 870 | F32 LLTextureCache::sHeaderCacheVersion = 1.2f; |
1002 | U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; | 871 | U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; |
1003 | S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit | 872 | S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit |
1004 | const char* entries_filename = "texture.entries"; | 873 | const char* entries_filename = "texture.entries"; |
@@ -1011,7 +880,6 @@ void LLTextureCache::setDirNames(ELLPath location) | |||
1011 | mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename); | 880 | mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename); |
1012 | mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename); | 881 | mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename); |
1013 | mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); | 882 | mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); |
1014 | mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename; | ||
1015 | } | 883 | } |
1016 | 884 | ||
1017 | void LLTextureCache::purgeCache(ELLPath location) | 885 | void LLTextureCache::purgeCache(ELLPath location) |
@@ -1019,7 +887,7 @@ void LLTextureCache::purgeCache(ELLPath location) | |||
1019 | if (!mReadOnly) | 887 | if (!mReadOnly) |
1020 | { | 888 | { |
1021 | setDirNames(location); | 889 | setDirNames(location); |
1022 | 890 | llassert_always(mHeaderAPRFile == NULL); | |
1023 | LLAPRFile::remove(mHeaderEntriesFileName); | 891 | LLAPRFile::remove(mHeaderEntriesFileName); |
1024 | LLAPRFile::remove(mHeaderDataFileName); | 892 | LLAPRFile::remove(mHeaderDataFileName); |
1025 | } | 893 | } |
@@ -1062,80 +930,315 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only) | |||
1062 | return max_size; // unused cache space | 930 | return max_size; // unused cache space |
1063 | } | 931 | } |
1064 | 932 | ||
1065 | struct lru_data | 933 | //---------------------------------------------------------------------------- |
934 | // mHeaderMutex must be locked for the following functions! | ||
935 | |||
936 | LLAPRFile* LLTextureCache::openHeaderEntriesFile(bool readonly, S32 offset) | ||
937 | { | ||
938 | llassert_always(mHeaderAPRFile == NULL); | ||
939 | apr_int32_t flags = readonly ? APR_READ|APR_BINARY : APR_READ|APR_WRITE|APR_BINARY; | ||
940 | mHeaderAPRFile = new LLAPRFile(mHeaderEntriesFileName, flags, LLAPRFile::local); | ||
941 | mHeaderAPRFile->seek(APR_SET, offset); | ||
942 | return mHeaderAPRFile; | ||
943 | } | ||
944 | |||
945 | void LLTextureCache::closeHeaderEntriesFile() | ||
946 | { | ||
947 | llassert_always(mHeaderAPRFile != NULL); | ||
948 | delete mHeaderAPRFile; | ||
949 | mHeaderAPRFile = NULL; | ||
950 | } | ||
951 | |||
952 | void LLTextureCache::readEntriesHeader() | ||
953 | { | ||
954 | // mHeaderEntriesInfo initializes to default values so safe not to read it | ||
955 | llassert_always(mHeaderAPRFile == NULL); | ||
956 | if (LLAPRFile::isExist(mHeaderEntriesFileName)) | ||
957 | { | ||
958 | LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); | ||
959 | } | ||
960 | } | ||
961 | |||
962 | void LLTextureCache::writeEntriesHeader() | ||
1066 | { | 963 | { |
1067 | lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; } | 964 | llassert_always(mHeaderAPRFile == NULL); |
1068 | U32 time; | 965 | if (!mReadOnly) |
1069 | S32 index; | 966 | { |
1070 | LLUUID uuid; | 967 | LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); |
1071 | struct Compare | 968 | } |
1072 | { | 969 | } |
1073 | // lhs < rhs | 970 | |
1074 | typedef const lru_data* lru_data_ptr; | 971 | static S32 mHeaderEntriesMaxWriteIdx = 0; |
1075 | bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const | 972 | |
973 | S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create) | ||
974 | { | ||
975 | S32 idx = -1; | ||
976 | |||
977 | id_map_t::iterator iter1 = mHeaderIDMap.find(id); | ||
978 | if (iter1 != mHeaderIDMap.end()) | ||
979 | { | ||
980 | idx = iter1->second; | ||
981 | } | ||
982 | |||
983 | if (idx < 0) | ||
984 | { | ||
985 | if (create && !mReadOnly) | ||
1076 | { | 986 | { |
1077 | if(a->time == b->time) | 987 | if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) |
1078 | return (a->index < b->index); | 988 | { |
989 | // Add an entry to the end of the list | ||
990 | idx = mHeaderEntriesInfo.mEntries++; | ||
991 | |||
992 | } | ||
993 | else if (!mFreeList.empty()) | ||
994 | { | ||
995 | idx = *(mFreeList.begin()); | ||
996 | mFreeList.erase(mFreeList.begin()); | ||
997 | } | ||
1079 | else | 998 | else |
1080 | return (a->time >= b->time); | 999 | { |
1000 | // Look for a still valid entry in the LRU | ||
1001 | for (std::set<LLUUID>::iterator iter2 = mLRU.begin(); iter2 != mLRU.end();) | ||
1002 | { | ||
1003 | std::set<LLUUID>::iterator curiter2 = iter2++; | ||
1004 | LLUUID oldid = *curiter2; | ||
1005 | // Erase entry from LRU regardless | ||
1006 | mLRU.erase(curiter2); | ||
1007 | // Look up entry and use it if it is valid | ||
1008 | id_map_t::iterator iter3 = mHeaderIDMap.find(oldid); | ||
1009 | if (iter3 != mHeaderIDMap.end() && iter3->second >= 0) | ||
1010 | { | ||
1011 | idx = iter3->second; | ||
1012 | mHeaderIDMap.erase(oldid); | ||
1013 | mTexturesSizeMap.erase(oldid); | ||
1014 | break; | ||
1015 | } | ||
1016 | } | ||
1017 | // if (idx < 0) at this point, we will rebuild the LRU | ||
1018 | // and retry if called from setHeaderCacheEntry(), | ||
1019 | // otherwise this shouldn't happen and will trigger an error | ||
1020 | } | ||
1021 | if (idx >= 0) | ||
1022 | { | ||
1023 | // Set the header index | ||
1024 | mHeaderIDMap[id] = idx; | ||
1025 | llassert_always(mTexturesSizeMap.erase(id) == 0); | ||
1026 | // Initialize the entry (will get written later) | ||
1027 | entry.init(id, time(NULL)); | ||
1028 | // Update Header | ||
1029 | writeEntriesHeader(); | ||
1030 | // Write Entry | ||
1031 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | ||
1032 | LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); | ||
1033 | S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); | ||
1034 | llassert_always(bytes_written == sizeof(Entry)); | ||
1035 | mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); | ||
1036 | closeHeaderEntriesFile(); | ||
1037 | } | ||
1081 | } | 1038 | } |
1082 | }; | 1039 | } |
1083 | }; | 1040 | else |
1041 | { | ||
1042 | // Remove this entry from the LRU if it exists | ||
1043 | mLRU.erase(id); | ||
1044 | // Read the entry | ||
1045 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | ||
1046 | LLAPRFile* aprfile = openHeaderEntriesFile(true, offset); | ||
1047 | S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry)); | ||
1048 | llassert_always(bytes_read == sizeof(Entry)); | ||
1049 | llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); | ||
1050 | closeHeaderEntriesFile(); | ||
1051 | } | ||
1052 | return idx; | ||
1053 | } | ||
1054 | |||
1055 | void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry) | ||
1056 | { | ||
1057 | if (idx >= 0) | ||
1058 | { | ||
1059 | if (!mReadOnly) | ||
1060 | { | ||
1061 | entry.mTime = time(NULL); | ||
1062 | llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); | ||
1063 | if (entry.mBodySize > 0) | ||
1064 | { | ||
1065 | mTexturesSizeMap[entry.mID] = entry.mBodySize; | ||
1066 | } | ||
1067 | // llinfos << "Updating TE: " << idx << ": " << id << " Size: " << entry.mBodySize << " Time: " << entry.mTime << llendl; | ||
1068 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | ||
1069 | LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); | ||
1070 | S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); | ||
1071 | llassert_always(bytes_written == sizeof(Entry)); | ||
1072 | mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); | ||
1073 | closeHeaderEntriesFile(); | ||
1074 | } | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries) | ||
1079 | { | ||
1080 | U32 num_entries = mHeaderEntriesInfo.mEntries; | ||
1081 | |||
1082 | mHeaderIDMap.clear(); | ||
1083 | mTexturesSizeMap.clear(); | ||
1084 | mFreeList.clear(); | ||
1085 | mTexturesSizeTotal = 0; | ||
1086 | |||
1087 | LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); | ||
1088 | for (U32 idx=0; idx<num_entries; idx++) | ||
1089 | { | ||
1090 | Entry entry; | ||
1091 | S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry)); | ||
1092 | if (bytes_read < sizeof(Entry)) | ||
1093 | { | ||
1094 | llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl; | ||
1095 | closeHeaderEntriesFile(); | ||
1096 | purgeAllTextures(false); | ||
1097 | return 0; | ||
1098 | } | ||
1099 | entries.push_back(entry); | ||
1100 | // llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl; | ||
1101 | if (entry.mImageSize < 0) | ||
1102 | { | ||
1103 | mFreeList.insert(idx); | ||
1104 | } | ||
1105 | else | ||
1106 | { | ||
1107 | mHeaderIDMap[entry.mID] = idx; | ||
1108 | if (entry.mBodySize > 0) | ||
1109 | { | ||
1110 | mTexturesSizeMap[entry.mID] = entry.mBodySize; | ||
1111 | mTexturesSizeTotal += entry.mBodySize; | ||
1112 | } | ||
1113 | llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize); | ||
1114 | } | ||
1115 | } | ||
1116 | closeHeaderEntriesFile(); | ||
1117 | return num_entries; | ||
1118 | } | ||
1119 | |||
1120 | void LLTextureCache::writeEntriesAndClose(const std::vector<Entry>& entries) | ||
1121 | { | ||
1122 | S32 num_entries = entries.size(); | ||
1123 | llassert_always(num_entries == mHeaderEntriesInfo.mEntries); | ||
1124 | |||
1125 | if (!mReadOnly) | ||
1126 | { | ||
1127 | LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); | ||
1128 | for (S32 idx=0; idx<num_entries; idx++) | ||
1129 | { | ||
1130 | S32 bytes_written = aprfile->write((void*)(&entries[idx]), (S32)sizeof(Entry)); | ||
1131 | llassert_always(bytes_written == sizeof(Entry)); | ||
1132 | } | ||
1133 | mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1); | ||
1134 | closeHeaderEntriesFile(); | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | //---------------------------------------------------------------------------- | ||
1084 | 1139 | ||
1085 | // Called from either the main thread or the worker thread | 1140 | // Called from either the main thread or the worker thread |
1086 | void LLTextureCache::readHeaderCache() | 1141 | void LLTextureCache::readHeaderCache() |
1087 | { | 1142 | { |
1088 | LLMutexLock lock(&mHeaderMutex); | 1143 | LLMutexLock lock(&mHeaderMutex); |
1089 | mHeaderEntriesInfo.mVersion = 0.f; | 1144 | |
1090 | mHeaderEntriesInfo.mEntries = 0; | 1145 | mLRU.clear(); // always clear the LRU |
1091 | if (LLAPRFile::isExist(mHeaderEntriesFileName)) | 1146 | |
1092 | { | 1147 | readEntriesHeader(); |
1093 | LLAPRFile::readEx(mHeaderEntriesFileName, | 1148 | |
1094 | (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); | ||
1095 | } | ||
1096 | if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion) | 1149 | if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion) |
1097 | { | 1150 | { |
1098 | if (!mReadOnly) | 1151 | if (!mReadOnly) |
1099 | { | 1152 | { |
1100 | // Info with 0 entries | 1153 | purgeAllTextures(false); |
1101 | mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; | ||
1102 | |||
1103 | LLAPRFile::writeEx(mHeaderEntriesFileName, | ||
1104 | (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); | ||
1105 | } | 1154 | } |
1106 | } | 1155 | } |
1107 | else | 1156 | else |
1108 | { | 1157 | { |
1109 | S32 num_entries = mHeaderEntriesInfo.mEntries; | 1158 | std::vector<Entry> entries; |
1159 | U32 num_entries = openAndReadEntries(entries); | ||
1110 | if (num_entries) | 1160 | if (num_entries) |
1111 | { | 1161 | { |
1112 | Entry* entries = new Entry[num_entries]; | 1162 | U32 empty_entries = 0; |
1163 | typedef std::pair<U32, LLUUID> lru_data_t; | ||
1164 | std::set<lru_data_t> lru; | ||
1165 | std::vector<LLUUID> purge_list; | ||
1166 | for (U32 i=0; i<num_entries; i++) | ||
1113 | { | 1167 | { |
1114 | LLAPRFile::readEx(mHeaderEntriesFileName, | 1168 | Entry& entry = entries[i]; |
1115 | (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry)); | 1169 | const LLUUID& id = entry.mID; |
1170 | if (entry.mImageSize < 0) | ||
1171 | { | ||
1172 | // This will be in the Free List, don't put it in the LRY | ||
1173 | ++empty_entries; | ||
1174 | } | ||
1175 | else | ||
1176 | { | ||
1177 | lru.insert(std::make_pair(entry.mTime, id)); | ||
1178 | if (entry.mBodySize > 0) | ||
1179 | { | ||
1180 | if (entry.mBodySize > entry.mImageSize) | ||
1181 | { | ||
1182 | // Shouldn't happen, failsafe only | ||
1183 | llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl; | ||
1184 | purge_list.push_back(id); | ||
1185 | } | ||
1186 | } | ||
1187 | } | ||
1116 | } | 1188 | } |
1117 | typedef std::set<lru_data*, lru_data::Compare> lru_set_t; | 1189 | if (num_entries > sCacheMaxEntries) |
1118 | lru_set_t lru; | ||
1119 | for (S32 i=0; i<num_entries; i++) | ||
1120 | { | 1190 | { |
1121 | if (entries[i].mSize >= 0) // -1 indicates erased entry, skip | 1191 | // Special case: cache size was reduced, need to remove entries |
1192 | // Note: After we prune entries, we will call this again and create the LRU | ||
1193 | U32 entries_to_purge = (num_entries-empty_entries) - sCacheMaxEntries; | ||
1194 | if (entries_to_purge > 0) | ||
1122 | { | 1195 | { |
1123 | const LLUUID& id = entries[i].mID; | 1196 | for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter) |
1124 | lru.insert(new lru_data(entries[i].mTime, i, id)); | 1197 | { |
1125 | mHeaderIDMap[id] = i; | 1198 | purge_list.push_back(iter->second); |
1199 | if (--entries_to_purge <= 0) | ||
1200 | break; | ||
1201 | } | ||
1126 | } | 1202 | } |
1127 | } | 1203 | } |
1128 | mLRU.clear(); | 1204 | else |
1129 | S32 lru_entries = sCacheMaxEntries / 10; | ||
1130 | for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) | ||
1131 | { | 1205 | { |
1132 | lru_data* data = *iter; | 1206 | S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); |
1133 | mLRU[data->index] = data->uuid; | 1207 | for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter) |
1134 | if (--lru_entries <= 0) | 1208 | { |
1135 | break; | 1209 | mLRU.insert(iter->second); |
1210 | // llinfos << "LRU: " << iter->first << " : " << iter->second << llendl; | ||
1211 | if (--lru_entries <= 0) | ||
1212 | break; | ||
1213 | } | ||
1214 | } | ||
1215 | |||
1216 | if (purge_list.size() > 0) | ||
1217 | { | ||
1218 | for (std::vector<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) | ||
1219 | { | ||
1220 | removeFromCache(*iter); | ||
1221 | } | ||
1222 | // If we removed any entries, we need to rebuild the entries list, | ||
1223 | // write the header, and call this again | ||
1224 | std::vector<Entry> new_entries; | ||
1225 | for (U32 i=0; i<num_entries; i++) | ||
1226 | { | ||
1227 | const Entry& entry = entries[i]; | ||
1228 | if (entry.mImageSize >=0) | ||
1229 | { | ||
1230 | new_entries.push_back(entry); | ||
1231 | } | ||
1232 | } | ||
1233 | llassert_always(new_entries.size() <= sCacheMaxEntries); | ||
1234 | mHeaderEntriesInfo.mEntries = new_entries.size(); | ||
1235 | writeEntriesAndClose(new_entries); | ||
1236 | readHeaderCache(); // repeat with new entries file | ||
1237 | } | ||
1238 | else | ||
1239 | { | ||
1240 | writeEntriesAndClose(entries); | ||
1136 | } | 1241 | } |
1137 | for_each(lru.begin(), lru.end(), DeletePointer()); | ||
1138 | delete[] entries; | ||
1139 | } | 1242 | } |
1140 | } | 1243 | } |
1141 | } | 1244 | } |
@@ -1158,13 +1261,21 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) | |||
1158 | LLFile::rmdir(dirname); | 1261 | LLFile::rmdir(dirname); |
1159 | } | 1262 | } |
1160 | } | 1263 | } |
1161 | LLAPRFile::remove(mTexturesDirEntriesFileName); | ||
1162 | if (purge_directories) | 1264 | if (purge_directories) |
1163 | { | 1265 | { |
1164 | LLFile::rmdir(mTexturesDirName); | 1266 | LLFile::rmdir(mTexturesDirName); |
1165 | } | 1267 | } |
1166 | } | 1268 | } |
1269 | mHeaderIDMap.clear(); | ||
1167 | mTexturesSizeMap.clear(); | 1270 | mTexturesSizeMap.clear(); |
1271 | mTexturesSizeTotal = 0; | ||
1272 | mFreeList.clear(); | ||
1273 | mTexturesSizeTotal = 0; | ||
1274 | |||
1275 | // Info with 0 entries | ||
1276 | mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; | ||
1277 | mHeaderEntriesInfo.mEntries = 0; | ||
1278 | writeEntriesHeader(); | ||
1168 | } | 1279 | } |
1169 | 1280 | ||
1170 | void LLTextureCache::purgeTextures(bool validate) | 1281 | void LLTextureCache::purgeTextures(bool validate) |
@@ -1178,50 +1289,37 @@ void LLTextureCache::purgeTextures(bool validate) | |||
1178 | LLAppViewer::instance()->pauseMainloopTimeout(); | 1289 | LLAppViewer::instance()->pauseMainloopTimeout(); |
1179 | 1290 | ||
1180 | LLMutexLock lock(&mHeaderMutex); | 1291 | LLMutexLock lock(&mHeaderMutex); |
1181 | 1292 | ||
1182 | S32 filesize = LLAPRFile::size(mTexturesDirEntriesFileName); | 1293 | llinfos << "TEXTURE CACHE: Purging." << llendl; |
1183 | S32 num_entries = filesize / sizeof(Entry); | 1294 | |
1184 | if (num_entries * (S32)sizeof(Entry) != filesize) | 1295 | // Read the entries list |
1185 | { | 1296 | std::vector<Entry> entries; |
1186 | LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; | 1297 | U32 num_entries = openAndReadEntries(entries); |
1187 | purgeAllTextures(false); | 1298 | if (!num_entries) |
1188 | return; | ||
1189 | } | ||
1190 | if (num_entries == 0) | ||
1191 | { | 1299 | { |
1192 | return; // nothing to do | 1300 | writeEntriesAndClose(entries); |
1301 | return; // nothing to purge | ||
1193 | } | 1302 | } |
1194 | 1303 | ||
1195 | Entry* entries = new Entry[num_entries]; | 1304 | // Use mTexturesSizeMap to collect UUIDs of textures with bodies |
1196 | S32 bytes_read = LLAPRFile::readEx(mTexturesDirEntriesFileName, | 1305 | typedef std::set<std::pair<U32,S32> > time_idx_set_t; |
1197 | (U8*)entries, 0, num_entries*sizeof(Entry)); | 1306 | std::set<std::pair<U32,S32> > time_idx_set; |
1198 | if (bytes_read != filesize) | 1307 | for (size_map_t::iterator iter1 = mTexturesSizeMap.begin(); |
1308 | iter1 != mTexturesSizeMap.end(); ++iter1) | ||
1199 | { | 1309 | { |
1200 | LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; | 1310 | if (iter1->second > 0) |
1201 | purgeAllTextures(false); | ||
1202 | return; | ||
1203 | } | ||
1204 | |||
1205 | LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading " << num_entries << " Entries from " << mTexturesDirEntriesFileName << LL_ENDL; | ||
1206 | |||
1207 | std::map<LLUUID, S32> entry_idx_map; | ||
1208 | S64 total_size = 0; | ||
1209 | for (S32 idx=0; idx<num_entries; idx++) | ||
1210 | { | ||
1211 | const LLUUID& id = entries[idx].mID; | ||
1212 | LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL; | ||
1213 | std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id); | ||
1214 | if (iter != entry_idx_map.end()) | ||
1215 | { | 1311 | { |
1216 | // Newer entry replacing older entry | 1312 | id_map_t::iterator iter2 = mHeaderIDMap.find(iter1->first); |
1217 | S32 pidx = iter->second; | 1313 | if (iter2 != mHeaderIDMap.end()) |
1218 | total_size -= entries[pidx].mSize; | 1314 | { |
1219 | entries[pidx].mSize = 0; // flag: skip older entry | 1315 | S32 idx = iter2->second; |
1316 | time_idx_set.insert(std::make_pair(entries[idx].mTime, idx)); | ||
1317 | // llinfos << "TIME: " << entries[idx].mTime << " TEX: " << entries[idx].mID << " IDX: " << idx << " Size: " << entries[idx].mImageSize << llendl; | ||
1318 | } | ||
1220 | } | 1319 | } |
1221 | entry_idx_map[id] = idx; | ||
1222 | total_size += entries[idx].mSize; | ||
1223 | } | 1320 | } |
1224 | 1321 | ||
1322 | // Validate 1/256th of the files on startup | ||
1225 | U32 validate_idx = 0; | 1323 | U32 validate_idx = 0; |
1226 | if (validate) | 1324 | if (validate) |
1227 | { | 1325 | { |
@@ -1230,19 +1328,17 @@ void LLTextureCache::purgeTextures(bool validate) | |||
1230 | gSavedSettings.setU32("CacheValidateCounter", next_idx); | 1328 | gSavedSettings.setU32("CacheValidateCounter", next_idx); |
1231 | LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL; | 1329 | LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL; |
1232 | } | 1330 | } |
1233 | 1331 | ||
1234 | S64 min_cache_size = sCacheMaxTexturesSize / 100 * 95; | 1332 | S64 cache_size = mTexturesSizeTotal; |
1333 | S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100; | ||
1235 | S32 purge_count = 0; | 1334 | S32 purge_count = 0; |
1236 | S32 next_idx = 0; | 1335 | for (time_idx_set_t::iterator iter = time_idx_set.begin(); |
1237 | for (S32 idx=0; idx<num_entries; idx++) | 1336 | iter != time_idx_set.end(); ++iter) |
1238 | { | 1337 | { |
1239 | if (entries[idx].mSize == 0) | 1338 | S32 idx = iter->second; |
1240 | { | ||
1241 | continue; | ||
1242 | } | ||
1243 | bool purge_entry = false; | 1339 | bool purge_entry = false; |
1244 | std::string filename = getTextureFileName(entries[idx].mID); | 1340 | std::string filename = getTextureFileName(entries[idx].mID); |
1245 | if (total_size >= min_cache_size) | 1341 | if (cache_size >= purged_cache_size) |
1246 | { | 1342 | { |
1247 | purge_entry = true; | 1343 | purge_entry = true; |
1248 | } | 1344 | } |
@@ -1252,112 +1348,47 @@ void LLTextureCache::purgeTextures(bool validate) | |||
1252 | S32 uuididx = entries[idx].mID.mData[0]; | 1348 | S32 uuididx = entries[idx].mID.mData[0]; |
1253 | if (uuididx == validate_idx) | 1349 | if (uuididx == validate_idx) |
1254 | { | 1350 | { |
1255 | LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL; | 1351 | LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; |
1256 | S32 bodysize = LLAPRFile::size(filename); | 1352 | S32 bodysize = LLAPRFile::size(filename); |
1257 | if (bodysize != entries[idx].mSize) | 1353 | if (bodysize != entries[idx].mBodySize) |
1258 | { | 1354 | { |
1259 | LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize | 1355 | LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize |
1260 | << filename << LL_ENDL; | 1356 | << filename << LL_ENDL; |
1261 | purge_entry = true; | 1357 | purge_entry = true; |
1262 | } | 1358 | } |
1263 | } | 1359 | } |
1264 | } | 1360 | } |
1265 | if (purge_entry) | 1361 | else |
1266 | { | 1362 | { |
1267 | purge_count++; | 1363 | break; |
1268 | LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; | ||
1269 | mFilesToDelete.push_back(filename); | ||
1270 | total_size -= entries[idx].mSize; | ||
1271 | entries[idx].mSize = 0; | ||
1272 | } | 1364 | } |
1273 | else | 1365 | |
1366 | if (purge_entry) | ||
1274 | { | 1367 | { |
1275 | if (next_idx != idx) | 1368 | purge_count++; |
1276 | { | 1369 | LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; |
1277 | entries[next_idx] = entries[idx]; | 1370 | LLAPRFile::remove(filename); |
1278 | } | 1371 | cache_size -= entries[idx].mBodySize; |
1279 | ++next_idx; | 1372 | mTexturesSizeTotal -= entries[idx].mBodySize; |
1373 | entries[idx].mBodySize = 0; | ||
1374 | mTexturesSizeMap.erase(entries[idx].mID); | ||
1280 | } | 1375 | } |
1281 | } | 1376 | } |
1282 | num_entries = next_idx; | ||
1283 | 1377 | ||
1284 | mTimeLastFileDelete.reset(); | 1378 | LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL; |
1285 | 1379 | ||
1286 | LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " | 1380 | writeEntriesAndClose(entries); |
1287 | << num_entries << " (" << num_entries*sizeof(Entry)/1024 << "KB)" | ||
1288 | << LL_ENDL; | ||
1289 | |||
1290 | LLAPRFile::remove(mTexturesDirEntriesFileName); | ||
1291 | LLAPRFile::writeEx(mTexturesDirEntriesFileName, | ||
1292 | (U8*)&entries[0], 0, num_entries*sizeof(Entry)); | ||
1293 | |||
1294 | mTexturesSizeTotal = 0; | ||
1295 | mTexturesSizeMap.clear(); | ||
1296 | for (S32 idx=0; idx<num_entries; idx++) | ||
1297 | { | ||
1298 | mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize; | ||
1299 | mTexturesSizeTotal += entries[idx].mSize; | ||
1300 | } | ||
1301 | llassert(mTexturesSizeTotal == total_size); | ||
1302 | 1381 | ||
1303 | delete[] entries; | ||
1304 | |||
1305 | // *FIX:Mani - watchdog back on. | 1382 | // *FIX:Mani - watchdog back on. |
1306 | LLAppViewer::instance()->resumeMainloopTimeout(); | 1383 | LLAppViewer::instance()->resumeMainloopTimeout(); |
1307 | 1384 | ||
1308 | LL_INFOS("TextureCache") << "TEXTURE CACHE:" | 1385 | LL_INFOS("TextureCache") << "TEXTURE CACHE:" |
1309 | << " PURGED: " << purge_count | 1386 | << " PURGED: " << purge_count |
1310 | << " ENTRIES: " << num_entries | 1387 | << " ENTRIES: " << num_entries |
1311 | << " CACHE SIZE: " << total_size/1024/1024 << " MB" | 1388 | << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB" |
1312 | << llendl; | 1389 | << llendl; |
1313 | } | 1390 | } |
1314 | 1391 | ||
1315 | |||
1316 | void LLTextureCache::purgeTextureFilesTimeSliced(BOOL force_all) | ||
1317 | { | ||
1318 | LLMutexLock lock(&mHeaderMutex); | ||
1319 | |||
1320 | F32 delay_between_passes = 1.0f; // seconds | ||
1321 | F32 max_time_per_pass = 0.1f; // seconds | ||
1322 | |||
1323 | if (!force_all && mTimeLastFileDelete.getElapsedTimeF32() <= delay_between_passes) | ||
1324 | { | ||
1325 | return; | ||
1326 | } | ||
1327 | |||
1328 | LLTimer timer; | ||
1329 | S32 howmany = 0; | ||
1330 | |||
1331 | if (mFilesToDelete.size() > 0) | ||
1332 | { | ||
1333 | llinfos << "TEXTURE CACHE: " << mFilesToDelete.size() << " files scheduled for deletion" << llendl; | ||
1334 | } | ||
1335 | |||
1336 | for (LLTextureCache::filename_list_t::iterator iter = mFilesToDelete.begin(); iter!=mFilesToDelete.end(); ) | ||
1337 | { | ||
1338 | LLTextureCache::filename_list_t::iterator iter2 = iter++; | ||
1339 | LLAPRFile::remove(*iter2); | ||
1340 | mFilesToDelete.erase(iter2); | ||
1341 | howmany++; | ||
1342 | |||
1343 | if (!force_all && timer.getElapsedTimeF32() > max_time_per_pass) | ||
1344 | { | ||
1345 | break; | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | if (!mFilesToDelete.empty()) | ||
1350 | { | ||
1351 | llinfos << "TEXTURE CACHE: "<< howmany << " files deleted (" | ||
1352 | << mFilesToDelete.size() << " files left for next pass)" | ||
1353 | << llendl; | ||
1354 | } | ||
1355 | |||
1356 | mTimeLastFileDelete.reset(); | ||
1357 | } | ||
1358 | |||
1359 | |||
1360 | |||
1361 | ////////////////////////////////////////////////////////////////////////////// | 1392 | ////////////////////////////////////////////////////////////////////////////// |
1362 | 1393 | ||
1363 | // call lockWorkers() first! | 1394 | // call lockWorkers() first! |
@@ -1384,75 +1415,39 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle) | |||
1384 | } | 1415 | } |
1385 | 1416 | ||
1386 | ////////////////////////////////////////////////////////////////////////////// | 1417 | ////////////////////////////////////////////////////////////////////////////// |
1387 | |||
1388 | // Called from work thread | 1418 | // Called from work thread |
1389 | S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize) | ||
1390 | { | ||
1391 | bool retry = false; | ||
1392 | S32 idx = -1; | ||
1393 | 1419 | ||
1420 | // Reads imagesize from the header, updates timestamp | ||
1421 | S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize) | ||
1422 | { | ||
1423 | LLMutexLock lock(&mHeaderMutex); | ||
1424 | Entry entry; | ||
1425 | S32 idx = openAndReadEntry(id, entry, false); | ||
1426 | if (idx >= 0) | ||
1394 | { | 1427 | { |
1395 | LLMutexLock lock(&mHeaderMutex); | 1428 | imagesize = entry.mImageSize; |
1396 | id_map_t::iterator iter = mHeaderIDMap.find(id); | 1429 | writeEntryAndClose(idx, entry); // updates time |
1397 | if (iter != mHeaderIDMap.end()) | 1430 | } |
1398 | { | 1431 | return idx; |
1399 | idx = iter->second; | 1432 | } |
1400 | } | ||
1401 | else if (touch && !mReadOnly) | ||
1402 | { | ||
1403 | if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) | ||
1404 | { | ||
1405 | // Add an entry | ||
1406 | idx = mHeaderEntriesInfo.mEntries++; | ||
1407 | mHeaderIDMap[id] = idx; | ||
1408 | // Update Info | ||
1409 | LLAPRFile::writeEx(mHeaderEntriesFileName, | ||
1410 | (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo)); | ||
1411 | } | ||
1412 | else if (!mLRU.empty()) | ||
1413 | { | ||
1414 | idx = mLRU.begin()->first; // will be erased below | ||
1415 | const LLUUID& oldid = mLRU.begin()->second; | ||
1416 | mHeaderIDMap.erase(oldid); | ||
1417 | mTexturesSizeMap.erase(oldid); | ||
1418 | mHeaderIDMap[id] = idx; | ||
1419 | } | ||
1420 | else | ||
1421 | { | ||
1422 | idx = -1; | ||
1423 | retry = true; | ||
1424 | } | ||
1425 | } | ||
1426 | if (idx >= 0) | ||
1427 | { | ||
1428 | if (touch && !mReadOnly) | ||
1429 | { | ||
1430 | // Update the lru entry | ||
1431 | mLRU.erase(idx); | ||
1432 | llassert_always(imagesize && *imagesize > 0); | ||
1433 | Entry* entry = new Entry(id, *imagesize, time(NULL)); | ||
1434 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | ||
1435 | LLAPRFile::writeEx(mHeaderEntriesFileName, | ||
1436 | (U8*)entry, offset, sizeof(Entry)); | ||
1437 | delete entry; | ||
1438 | } | ||
1439 | else if (imagesize) | ||
1440 | { | ||
1441 | // Get the image size | ||
1442 | Entry entry; | ||
1443 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | ||
1444 | 1433 | ||
1445 | LLAPRFile::readEx(mHeaderEntriesFileName, | 1434 | // Writes imagesize to the header, updates timestamp |
1446 | (U8*)&entry, offset, sizeof(Entry)); | 1435 | S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize) |
1447 | *imagesize = entry.mSize; | 1436 | { |
1448 | } | 1437 | LLMutexLock lock(&mHeaderMutex); |
1449 | } | 1438 | llassert_always(imagesize >= 0); |
1439 | Entry entry; | ||
1440 | S32 idx = openAndReadEntry(id, entry, true); | ||
1441 | if (idx >= 0) | ||
1442 | { | ||
1443 | entry.mImageSize = imagesize; | ||
1444 | writeEntryAndClose(idx, entry); | ||
1450 | } | 1445 | } |
1451 | if (retry) | 1446 | else // retry |
1452 | { | 1447 | { |
1453 | readHeaderCache(); // updates the lru | 1448 | readHeaderCache(); // We couldn't write an entry, so refresh the LRU |
1454 | llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries); | 1449 | llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries); |
1455 | idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion | 1450 | idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion |
1456 | } | 1451 | } |
1457 | return idx; | 1452 | return idx; |
1458 | } | 1453 | } |
@@ -1468,8 +1463,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const std::string& filena | |||
1468 | // so let the thread handle it | 1463 | // so let the thread handle it |
1469 | LLMutexLock lock(&mWorkersMutex); | 1464 | LLMutexLock lock(&mWorkersMutex); |
1470 | LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id, | 1465 | LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id, |
1471 | NULL, size, offset, 0, | 1466 | NULL, size, offset, 0, |
1472 | responder); | 1467 | responder); |
1473 | handle_t handle = worker->read(); | 1468 | handle_t handle = worker->read(); |
1474 | mReaders[handle] = worker; | 1469 | mReaders[handle] = worker; |
1475 | return handle; | 1470 | return handle; |
@@ -1482,8 +1477,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri | |||
1482 | // so let the thread handle it | 1477 | // so let the thread handle it |
1483 | LLMutexLock lock(&mWorkersMutex); | 1478 | LLMutexLock lock(&mWorkersMutex); |
1484 | LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, | 1479 | LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, |
1485 | NULL, size, offset, 0, | 1480 | NULL, size, offset, |
1486 | responder); | 1481 | 0, responder); |
1487 | handle_t handle = worker->read(); | 1482 | handle_t handle = worker->read(); |
1488 | mReaders[handle] = worker; | 1483 | mReaders[handle] = worker; |
1489 | return handle; | 1484 | return handle; |
@@ -1494,7 +1489,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) | |||
1494 | { | 1489 | { |
1495 | lockWorkers(); | 1490 | lockWorkers(); |
1496 | handle_map_t::iterator iter = mReaders.find(handle); | 1491 | handle_map_t::iterator iter = mReaders.find(handle); |
1497 | llassert_always(iter != mReaders.end()); | 1492 | llassert_always(iter != mReaders.end() || abort); |
1498 | LLTextureCacheWorker* worker = iter->second; | 1493 | LLTextureCacheWorker* worker = iter->second; |
1499 | bool res = worker->complete(); | 1494 | bool res = worker->complete(); |
1500 | if (res || abort) | 1495 | if (res || abort) |
@@ -1528,14 +1523,10 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio | |||
1528 | purgeTextures(false); | 1523 | purgeTextures(false); |
1529 | mDoPurge = FALSE; | 1524 | mDoPurge = FALSE; |
1530 | } | 1525 | } |
1531 | |||
1532 | purgeTextureFilesTimeSliced(); // purge textures from cache in a non-hiccup-way | ||
1533 | |||
1534 | |||
1535 | LLMutexLock lock(&mWorkersMutex); | 1526 | LLMutexLock lock(&mWorkersMutex); |
1536 | LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, | 1527 | LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, |
1537 | data, datasize, 0, | 1528 | data, datasize, 0, |
1538 | imagesize, responder); | 1529 | imagesize, responder); |
1539 | handle_t handle = worker->write(); | 1530 | handle_t handle = worker->write(); |
1540 | mWriters[handle] = worker; | 1531 | mWriters[handle] = worker; |
1541 | return handle; | 1532 | return handle; |
@@ -1581,24 +1572,17 @@ void LLTextureCache::addCompleted(Responder* responder, bool success) | |||
1581 | 1572 | ||
1582 | bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id) | 1573 | bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id) |
1583 | { | 1574 | { |
1584 | if (mReadOnly) | 1575 | if (!mReadOnly) |
1585 | { | ||
1586 | return false; | ||
1587 | } | ||
1588 | LLMutexLock lock(&mHeaderMutex); | ||
1589 | id_map_t::iterator iter = mHeaderIDMap.find(id); | ||
1590 | if (iter != mHeaderIDMap.end()) | ||
1591 | { | 1576 | { |
1592 | S32 idx = iter->second; | 1577 | LLMutexLock lock(&mHeaderMutex); |
1578 | Entry entry; | ||
1579 | S32 idx = openAndReadEntry(id, entry, false); | ||
1593 | if (idx >= 0) | 1580 | if (idx >= 0) |
1594 | { | 1581 | { |
1595 | Entry* entry = new Entry(id, -1, time(NULL)); | 1582 | entry.mImageSize = -1; |
1596 | S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); | 1583 | entry.mBodySize = 0; |
1597 | 1584 | writeEntryAndClose(idx, entry); | |
1598 | LLAPRFile::writeEx(mHeaderEntriesFileName, | 1585 | mFreeList.insert(idx); |
1599 | (U8*)entry, offset, sizeof(Entry)); | ||
1600 | delete entry; | ||
1601 | mLRU[idx] = id; | ||
1602 | mHeaderIDMap.erase(id); | 1586 | mHeaderIDMap.erase(id); |
1603 | mTexturesSizeMap.erase(id); | 1587 | mTexturesSizeMap.erase(id); |
1604 | return true; | 1588 | return true; |