Beispiel #1
0
void LLTextureCache::purgeCache(ELLPath location)
{
	if (!mReadOnly)
	{
		setDirNames(location);
	
		ll_apr_file_remove(mHeaderEntriesFileName, NULL);
		ll_apr_file_remove(mHeaderDataFileName, NULL);
	}
	purgeAllTextures(true);
}
Beispiel #2
0
void LLTextureCache::purgeCache(ELLPath location)
{
	LLMutexLock lock(&mHeaderMutex);

	if (!mReadOnly)
	{
		setDirNames(location);
		llassert_always(mHeaderAPRFile == NULL);
		LLAPRFile::remove(mHeaderEntriesFileName);
		LLAPRFile::remove(mHeaderDataFileName);
	}
	purgeAllTextures(true);
}
Beispiel #3
0
U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries)
{
	U32 num_entries = mHeaderEntriesInfo.mEntries;

	mHeaderIDMap.clear();
	mTexturesSizeMap.clear();
	mFreeList.clear();
	mTexturesSizeTotal = 0;

	LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo));
	for (U32 idx=0; idx<num_entries; idx++)
	{
		Entry entry;
		S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry));
		if (bytes_read < sizeof(Entry))
		{
			llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl;
			closeHeaderEntriesFile();
			purgeAllTextures(false);
			return 0;
		}
		entries.push_back(entry);
// 		llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl;
		if (entry.mImageSize < 0)
		{
			mFreeList.insert(idx);
		}
		else
		{
			mHeaderIDMap[entry.mID] = idx;
			if (entry.mBodySize > 0)
			{
				mTexturesSizeMap[entry.mID] = entry.mBodySize;
				mTexturesSizeTotal += entry.mBodySize;
			}
			llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize);
		}
	}
	closeHeaderEntriesFile();
	return num_entries;
}
Beispiel #4
0
void LLTextureCache::purgeTextures(bool validate)
{
	if (mReadOnly)
	{
		return;
	}

	// *FIX:Mani - watchdog off.
	LLAppViewer::instance()->pauseMainloopTimeout();

	LLMutexLock lock(&mHeaderMutex);
	
	S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL);
	S32 num_entries = filesize / sizeof(Entry);
	if (num_entries * (S32)sizeof(Entry) != filesize)
	{
		LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
		purgeAllTextures(false);
		return;
	}
	if (num_entries == 0)
	{
		return; // nothing to do
	}
	
	Entry* entries = new Entry[num_entries];
	S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL,
										 (U8*)entries, 0, num_entries*sizeof(Entry));
	if (bytes_read != filesize)
	{
		LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL;
		purgeAllTextures(false);
		return;
	}
	
	LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading " << num_entries << " Entries from " << mTexturesDirEntriesFileName << LL_ENDL;
	
	std::map<LLUUID, S32> entry_idx_map;
	S64 total_size = 0;
	for (S32 idx=0; idx<num_entries; idx++)
	{
		const LLUUID& id = entries[idx].mID;
 		LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL;
		std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
		if (iter != entry_idx_map.end())
		{
			// Newer entry replacing older entry
			S32 pidx = iter->second;
			total_size -= entries[pidx].mSize;
			entries[pidx].mSize = 0; // flag: skip older entry
		}
		entry_idx_map[id] = idx;
		total_size += entries[idx].mSize;
	}

	U32 validate_idx = 0;
	if (validate)
	{
		validate_idx = gSavedSettings.getU32("CacheValidateCounter");
		U32 next_idx = (++validate_idx) % 256;
		gSavedSettings.setU32("CacheValidateCounter", next_idx);
		LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL;
	}
	
	S64 min_cache_size = sCacheMaxTexturesSize / 100 * 95;
	S32 purge_count = 0;
	S32 next_idx = 0;
	for (S32 idx=0; idx<num_entries; idx++)
	{
		if (entries[idx].mSize == 0)
		{
			continue;
		}
		bool purge_entry = false;
		std::string filename = getTextureFileName(entries[idx].mID);
		if (total_size >= min_cache_size)
		{
			purge_entry = true;
		}
		else if (validate)
		{
			// make sure file exists and is the correct size
			S32 uuididx = entries[idx].mID.mData[0];
			if (uuididx == validate_idx)
			{
 				LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL;
				S32 bodysize = ll_apr_file_size(filename, NULL);
				if (bodysize != entries[idx].mSize)
				{
					LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
							<< filename << LL_ENDL;
					purge_entry = true;
				}
			}
		}
		if (purge_entry)
		{
			purge_count++;
			LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL;
			mFilesToDelete.push_back(filename);
			total_size -= entries[idx].mSize;
			entries[idx].mSize = 0;
		}
		else
		{
			if (next_idx != idx)
			{
				entries[next_idx] = entries[idx];
			}
			++next_idx;
		}
	}
	num_entries = next_idx;

	mTimeLastFileDelete.reset();

	LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " 
		<< num_entries << " (" << num_entries*sizeof(Entry)/1024 << "KB)"
		<< LL_ENDL;
	
	ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
	ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL,
						 (U8*)&entries[0], 0, num_entries*sizeof(Entry));
	
	mTexturesSizeTotal = 0;
	mTexturesSizeMap.clear();
	for (S32 idx=0; idx<num_entries; idx++)
	{
		mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
		mTexturesSizeTotal += entries[idx].mSize;
	}
	llassert(mTexturesSizeTotal == total_size);
	
	delete[] entries;

	// *FIX:Mani - watchdog back on.
	LLAppViewer::instance()->resumeMainloopTimeout();
	
	LL_INFOS("TextureCache") << "TEXTURE CACHE:"
			<< " PURGED: " << purge_count
			<< " ENTRIES: " << num_entries
			<< " CACHE SIZE: " << total_size/1024/1024 << " MB"
			<< llendl;
}
Beispiel #5
0
// Called from either the main thread or the worker thread
void LLTextureCache::readHeaderCache()
{
	mHeaderMutex.lock();

	mLRU.clear(); // always clear the LRU

	readEntriesHeader();
	
	if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
	{
		if (!mReadOnly)
		{
			purgeAllTextures(false);
		}
	}
	else
	{
		std::vector<Entry> entries;
		U32 num_entries = openAndReadEntries(entries);
		if (num_entries)
		{
			U32 empty_entries = 0;
			typedef std::pair<U32, LLUUID> lru_data_t;
			std::set<lru_data_t> lru;
			std::set<LLUUID> purge_list;
			for (U32 i=0; i<num_entries; i++)
			{
				Entry& entry = entries[i];
				const LLUUID& id = entry.mID;
				if (entry.mImageSize <= 0)
				{
					// This will be in the Free List, don't put it in the LRU
					++empty_entries;
				}
				else
				{
					lru.insert(std::make_pair(entry.mTime, id));
					if (entry.mBodySize > 0)
					{
						if (entry.mBodySize > entry.mImageSize)
						{
							// Shouldn't happen, failsafe only
							llwarns << "Bad entry: " << i << ": " << entry.mID << ": BodySize: " << entry.mBodySize << llendl;
							purge_list.insert(id);
						}
					}
				}
			}
			if (num_entries - empty_entries > sCacheMaxEntries)
			{
				// Special case: cache size was reduced, need to remove entries
				// Note: After we prune entries, we will call this again and create the LRU
				U32 entries_to_purge = (num_entries - empty_entries) - sCacheMaxEntries;
				llinfos << "Texture Cache Entries: " << num_entries << " Max: " << sCacheMaxEntries << " Empty: " << empty_entries << " Purging: " << entries_to_purge << llendl;
				// We can exit the following loop with the given condition, since if we'd reach the end of the lru set we'd have:
				// purge_list.size() = lru.size() = num_entries - empty_entries = entries_to_purge + sCacheMaxEntries >= entries_to_purge
				for (std::set<lru_data_t>::iterator iter = lru.begin(); purge_list.size() < entries_to_purge; ++iter)
				{
					purge_list.insert(iter->second);
				}
				llassert_always(purge_list.size() >= entries_to_purge);
			}
			else
			{
				S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE);
				for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter)
				{
					mLRU.insert(iter->second);
// 					llinfos << "LRU: " << iter->first << " : " << iter->second << llendl;
					if (--lru_entries <= 0)
						break;
				}
			}
			
			if (purge_list.size() > 0)
			{
				for (std::set<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter)
				{
					removeFromCacheLocked(*iter);
				}
				// If we removed any entries, we need to rebuild the entries list,
				// write the header, and call this again
				std::vector<Entry> new_entries;
				for (U32 i=0; i<num_entries; i++)
				{
					const Entry& entry = entries[i];
					if (entry.mImageSize > 0)
					{
						new_entries.push_back(entry);
					}
				}
				llassert_always(new_entries.size() <= sCacheMaxEntries);
				mHeaderEntriesInfo.mEntries = new_entries.size();
				writeEntriesHeader();
				writeEntriesAndClose(new_entries);
				mHeaderMutex.unlock(); // unlock the mutex before calling again
				readHeaderCache(); // repeat with new entries file
				return;
			}
			else
			{
				//entries are not changed, nothing here.
			}
		}
	}
	mHeaderMutex.unlock();
}