PageRenderTime 211ms CodeModel.GetById 15ms app.highlight 176ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llviewertexturelist.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1647 lines | 1238 code | 221 blank | 188 comment | 209 complexity | 69061b83d189e269569fc28f9d2eb5f8 MD5 | raw file
   1/** 
   2 * @file llviewertexturelist.cpp
   3 * @brief Object for managing the list of images within a region
   4 *
   5 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include <sys/stat.h>
  30
  31#include "llviewertexturelist.h"
  32
  33#include "imageids.h"
  34#include "llgl.h" // fot gathering stats from GL
  35#include "llimagegl.h"
  36#include "llimagebmp.h"
  37#include "llimagej2c.h"
  38#include "llimagetga.h"
  39#include "llimagejpeg.h"
  40#include "llimagepng.h"
  41#include "llimageworker.h"
  42
  43#include "llsdserialize.h"
  44#include "llsys.h"
  45#include "llvfs.h"
  46#include "llvfile.h"
  47#include "llvfsthread.h"
  48#include "llxmltree.h"
  49#include "message.h"
  50
  51#include "lltexturecache.h"
  52#include "lltexturefetch.h"
  53#include "llviewercontrol.h"
  54#include "llviewertexture.h"
  55#include "llviewermedia.h"
  56#include "llviewerregion.h"
  57#include "llviewerstats.h"
  58#include "pipeline.h"
  59#include "llappviewer.h"
  60#include "llxuiparser.h"
  61
  62////////////////////////////////////////////////////////////////////////////
  63
  64void (*LLViewerTextureList::sUUIDCallback)(void **, const LLUUID&) = NULL;
  65
  66U32 LLViewerTextureList::sTextureBits = 0;
  67U32 LLViewerTextureList::sTexturePackets = 0;
  68S32 LLViewerTextureList::sNumImages = 0;
  69LLStat LLViewerTextureList::sNumImagesStat(32, TRUE);
  70LLStat LLViewerTextureList::sNumRawImagesStat(32, TRUE);
  71LLStat LLViewerTextureList::sGLTexMemStat(32, TRUE);
  72LLStat LLViewerTextureList::sGLBoundMemStat(32, TRUE);
  73LLStat LLViewerTextureList::sRawMemStat(32, TRUE);
  74LLStat LLViewerTextureList::sFormattedMemStat(32, TRUE);
  75
  76LLViewerTextureList gTextureList;
  77static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images");
  78
  79///////////////////////////////////////////////////////////////////////////////
  80
  81LLViewerTextureList::LLViewerTextureList() 
  82	: mForceResetTextureStats(FALSE),
  83	mUpdateStats(FALSE),
  84	mMaxResidentTexMemInMegaBytes(0),
  85	mMaxTotalTextureMemInMegaBytes(0),
  86	mInitialized(FALSE)
  87{
  88}
  89
  90void LLViewerTextureList::init()
  91{			
  92	mInitialized = TRUE ;
  93	sNumImages = 0;
  94	mUpdateStats = TRUE;
  95	mMaxResidentTexMemInMegaBytes = 0;
  96	mMaxTotalTextureMemInMegaBytes = 0 ;
  97	
  98	// Update how much texture RAM we're allowed to use.
  99	updateMaxResidentTexMem(0); // 0 = use current
 100	
 101	doPreloadImages();
 102}
 103
 104
 105void LLViewerTextureList::doPreloadImages()
 106{
 107	LL_DEBUGS("ViewerImages") << "Preloading images..." << LL_ENDL;
 108	
 109	llassert_always(mInitialized) ;
 110	llassert_always(mImageList.empty()) ;
 111	llassert_always(mUUIDMap.empty()) ;
 112
 113	// Set the "missing asset" image
 114	LLViewerFetchedTexture::sMissingAssetImagep = LLViewerTextureManager::getFetchedTextureFromFile("missing_asset.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI);
 115	
 116	// Set the "white" image
 117	LLViewerFetchedTexture::sWhiteImagep = LLViewerTextureManager::getFetchedTextureFromFile("white.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI);
 118	LLTexUnit::sWhiteTexture = LLViewerFetchedTexture::sWhiteImagep->getTexName();
 119	LLUIImageList* image_list = LLUIImageList::getInstance();
 120
 121	image_list->initFromFile();
 122	
 123	// turn off clamping and bilinear filtering for uv picking images
 124	//LLViewerFetchedTexture* uv_test = preloadUIImage("uv_test1.tga", LLUUID::null, FALSE);
 125	//uv_test->setClamp(FALSE, FALSE);
 126	//uv_test->setMipFilterNearest(TRUE, TRUE);
 127	//uv_test = preloadUIImage("uv_test2.tga", LLUUID::null, FALSE);
 128	//uv_test->setClamp(FALSE, FALSE);
 129	//uv_test->setMipFilterNearest(TRUE, TRUE);
 130
 131	// prefetch specific UUIDs
 132	LLViewerTextureManager::getFetchedTexture(IMG_SHOT, TRUE);
 133	LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF, TRUE);
 134	LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTextureFromFile("silhouette.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI);
 135	if (image) 
 136	{
 137		image->setAddressMode(LLTexUnit::TAM_WRAP);
 138		mImagePreloads.insert(image);
 139	}
 140	image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryLines.png", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI);
 141	if (image) 
 142	{
 143		image->setAddressMode(LLTexUnit::TAM_WRAP);	
 144		mImagePreloads.insert(image);
 145	}
 146	image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI);
 147	if (image) 
 148	{
 149		image->setAddressMode(LLTexUnit::TAM_WRAP);
 150		mImagePreloads.insert(image);
 151	}
 152	image = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI);
 153	if (image) 
 154	{
 155		image->setAddressMode(LLTexUnit::TAM_WRAP);	
 156		mImagePreloads.insert(image);
 157	}
 158	image = LLViewerTextureManager::getFetchedTextureFromFile("transparent.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI, LLViewerTexture::FETCHED_TEXTURE,
 159		0,0,LLUUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"));
 160	if (image) 
 161	{
 162		image->setAddressMode(LLTexUnit::TAM_WRAP);
 163		mImagePreloads.insert(image);
 164	}
 165	
 166}
 167
 168static std::string get_texture_list_name()
 169{
 170	return std::string("texture_list_") + gSavedSettings.getString("LoginLocation") + ".xml";
 171}
 172
 173void LLViewerTextureList::doPrefetchImages()
 174{
 175	if (LLAppViewer::instance()->getPurgeCache())
 176	{
 177		// cache was purged, no point
 178		return;
 179	}
 180	
 181	// Pre-fetch textures from last logout
 182	LLSD imagelist;
 183	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name());
 184	llifstream file;
 185	file.open(filename);
 186	if (file.is_open())
 187	{
 188		LLSDSerialize::fromXML(imagelist, file);
 189	}
 190	for (LLSD::array_iterator iter = imagelist.beginArray();
 191		 iter != imagelist.endArray(); ++iter)
 192	{
 193		LLSD imagesd = *iter;
 194		LLUUID uuid = imagesd["uuid"];
 195		S32 pixel_area = imagesd["area"];
 196		S32 texture_type = imagesd["type"];
 197
 198		if(LLViewerTexture::FETCHED_TEXTURE == texture_type || LLViewerTexture::LOD_TEXTURE == texture_type)
 199		{
 200			LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(uuid, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, texture_type);
 201			if (image)
 202			{
 203				image->addTextureStats((F32)pixel_area);
 204			}
 205		}
 206	}
 207}
 208
 209///////////////////////////////////////////////////////////////////////////////
 210
 211LLViewerTextureList::~LLViewerTextureList()
 212{
 213}
 214
 215void LLViewerTextureList::shutdown()
 216{
 217	// clear out preloads
 218	mImagePreloads.clear();
 219
 220	// Write out list of currently loaded textures for precaching on startup
 221	typedef std::set<std::pair<S32,LLViewerFetchedTexture*> > image_area_list_t;
 222	image_area_list_t image_area_list;
 223	for (image_priority_list_t::iterator iter = mImageList.begin();
 224		 iter != mImageList.end(); ++iter)
 225	{
 226		LLViewerFetchedTexture* image = *iter;
 227		if (!image->hasGLTexture() ||
 228			!image->getUseDiscard() ||
 229			image->needsAux() ||
 230			image->getTargetHost() != LLHost::invalid)
 231		{
 232			continue; // avoid UI, baked, and other special images
 233		}
 234		if(!image->getBoundRecently())
 235		{
 236			continue ;
 237		}
 238		S32 desired = image->getDesiredDiscardLevel();
 239		if (desired >= 0 && desired < MAX_DISCARD_LEVEL)
 240		{
 241			S32 pixel_area = image->getWidth(desired) * image->getHeight(desired);
 242			image_area_list.insert(std::make_pair(pixel_area, image));
 243		}
 244	}
 245	
 246	LLSD imagelist;
 247	const S32 max_count = 1000;
 248	S32 count = 0;
 249	S32 image_type ;
 250	for (image_area_list_t::reverse_iterator riter = image_area_list.rbegin();
 251		 riter != image_area_list.rend(); ++riter)
 252	{
 253		LLViewerFetchedTexture* image = riter->second;
 254		image_type = (S32)image->getType() ;
 255		imagelist[count]["area"] = riter->first;
 256		imagelist[count]["uuid"] = image->getID();
 257		imagelist[count]["type"] = image_type;
 258		if (++count >= max_count)
 259			break;
 260	}
 261	
 262	if (count > 0 && !gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "").empty())
 263	{
 264		std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name());
 265		llofstream file;
 266		file.open(filename);
 267		LLSDSerialize::toPrettyXML(imagelist, file);
 268	}
 269	
 270	//
 271	// Clean up "loaded" callbacks.
 272	//
 273	mCallbackList.clear();
 274	
 275	// Flush all of the references
 276	mLoadingStreamList.clear();
 277	mCreateTextureList.clear();
 278	
 279	mUUIDMap.clear();
 280	
 281	mImageList.clear();
 282
 283	mInitialized = FALSE ; //prevent loading textures again.
 284}
 285
 286void LLViewerTextureList::dump()
 287{
 288	llinfos << "LLViewerTextureList::dump()" << llendl;
 289	for (image_priority_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it)
 290	{
 291		LLViewerFetchedTexture* image = *it;
 292
 293		llinfos << "priority " << image->getDecodePriority()
 294		<< " boost " << image->getBoostLevel()
 295		<< " size " << image->getWidth() << "x" << image->getHeight()
 296		<< " discard " << image->getDiscardLevel()
 297		<< " desired " << image->getDesiredDiscardLevel()
 298		<< " http://asset.siva.lindenlab.com/" << image->getID() << ".texture"
 299		<< llendl;
 300	}
 301}
 302
 303void LLViewerTextureList::destroyGL(BOOL save_state)
 304{
 305	LLImageGL::destroyGL(save_state);
 306}
 307
 308void LLViewerTextureList::restoreGL()
 309{
 310	llassert_always(mInitialized) ;
 311	LLImageGL::restoreGL();
 312}
 313
 314/* Vertical tab container button image IDs
 315 Seem to not decode when running app in debug.
 316 
 317 const LLUUID BAD_IMG_ONE("1097dcb3-aef9-8152-f471-431d840ea89e");
 318 const LLUUID BAD_IMG_TWO("bea77041-5835-1661-f298-47e2d32b7a70");
 319 */
 320
 321///////////////////////////////////////////////////////////////////////////////
 322
 323LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename,												   
 324												   BOOL usemipmaps,
 325												   LLViewerTexture::EBoostLevel boost_priority,
 326												   S8 texture_type,
 327												   LLGLint internal_format,
 328												   LLGLenum primary_format, 
 329												   const LLUUID& force_id)
 330{
 331	if(!mInitialized)
 332	{
 333		return NULL ;
 334	}
 335
 336	std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename);
 337	if (full_path.empty())
 338	{
 339		llwarns << "Failed to find local image file: " << filename << llendl;
 340		return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI);
 341	}
 342
 343	std::string url = "file://" + full_path;
 344
 345	return getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
 346}
 347
 348LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url,
 349												   BOOL usemipmaps,
 350												   LLViewerTexture::EBoostLevel boost_priority,
 351												   S8 texture_type,
 352												   LLGLint internal_format,
 353												   LLGLenum primary_format, 
 354												   const LLUUID& force_id)
 355{
 356	if(!mInitialized)
 357	{
 358		return NULL ;
 359	}
 360
 361	// generate UUID based on hash of filename
 362	LLUUID new_id;
 363	if (force_id.notNull())
 364	{
 365		new_id = force_id;
 366	}
 367	else
 368	{
 369		new_id.generate(url);
 370	}
 371
 372	LLPointer<LLViewerFetchedTexture> imagep = findImage(new_id);
 373	
 374	if (imagep.isNull())
 375	{
 376		switch(texture_type)
 377		{
 378		case LLViewerTexture::FETCHED_TEXTURE:
 379			imagep = new LLViewerFetchedTexture(url, new_id, usemipmaps);
 380			break ;
 381		case LLViewerTexture::LOD_TEXTURE:
 382			imagep = new LLViewerLODTexture(url, new_id, usemipmaps);
 383			break ;
 384		default:
 385			llerrs << "Invalid texture type " << texture_type << llendl ;
 386		}		
 387		
 388		if (internal_format && primary_format)
 389		{
 390			imagep->setExplicitFormat(internal_format, primary_format);
 391		}
 392
 393		addImage(imagep);
 394		
 395		if (boost_priority != 0)
 396		{
 397			if (boost_priority == LLViewerFetchedTexture::BOOST_UI ||
 398				boost_priority == LLViewerFetchedTexture::BOOST_ICON)
 399			{
 400				imagep->dontDiscard();
 401			}
 402			imagep->setBoostLevel(boost_priority);
 403		}
 404	}
 405
 406	imagep->setGLTextureCreated(true);
 407
 408	return imagep;
 409}
 410
 411
 412LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,											       
 413												   BOOL usemipmaps,
 414												   LLViewerTexture::EBoostLevel boost_priority,
 415												   S8 texture_type,
 416												   LLGLint internal_format,
 417												   LLGLenum primary_format,
 418												   LLHost request_from_host)
 419{
 420	if(!mInitialized)
 421	{
 422		return NULL ;
 423	}
 424
 425	// Return the image with ID image_id
 426	// If the image is not found, creates new image and
 427	// enqueues a request for transmission
 428	
 429	if ((&image_id == NULL) || image_id.isNull())
 430	{
 431		return (LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI));
 432	}
 433	
 434	LLPointer<LLViewerFetchedTexture> imagep = findImage(image_id);
 435	
 436	if (imagep.isNull())
 437	{
 438		imagep = createImage(image_id, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ;
 439	}
 440
 441	imagep->setGLTextureCreated(true);
 442	
 443	return imagep;
 444}
 445
 446//when this function is called, there is no such texture in the gTextureList with image_id.
 447LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,											       
 448												   BOOL usemipmaps,
 449												   LLViewerTexture::EBoostLevel boost_priority,
 450												   S8 texture_type,
 451												   LLGLint internal_format,
 452												   LLGLenum primary_format,
 453												   LLHost request_from_host)
 454{
 455	LLPointer<LLViewerFetchedTexture> imagep ;
 456	switch(texture_type)
 457	{
 458	case LLViewerTexture::FETCHED_TEXTURE:
 459		imagep = new LLViewerFetchedTexture(image_id, request_from_host, usemipmaps);
 460		break ;
 461	case LLViewerTexture::LOD_TEXTURE:
 462		imagep = new LLViewerLODTexture(image_id, request_from_host, usemipmaps);
 463		break ;
 464	default:
 465		llerrs << "Invalid texture type " << texture_type << llendl ;
 466	}
 467	
 468	if (internal_format && primary_format)
 469	{
 470		imagep->setExplicitFormat(internal_format, primary_format);
 471	}
 472	
 473	addImage(imagep);
 474	
 475	if (boost_priority != 0)
 476	{
 477		if (boost_priority == LLViewerFetchedTexture::BOOST_UI ||
 478			boost_priority == LLViewerFetchedTexture::BOOST_ICON)
 479		{
 480			imagep->dontDiscard();
 481		}
 482		imagep->setBoostLevel(boost_priority);
 483	}
 484	else
 485	{
 486		//by default, the texture can not be removed from memory even if it is not used.
 487		//here turn this off
 488		//if this texture should be set to NO_DELETE, call setNoDelete() afterwards.
 489		imagep->forceActive() ;
 490	}
 491
 492	return imagep ;
 493}
 494
 495LLViewerFetchedTexture *LLViewerTextureList::findImage(const LLUUID &image_id)
 496{
 497	uuid_map_t::iterator iter = mUUIDMap.find(image_id);
 498	if(iter == mUUIDMap.end())
 499		return NULL;
 500	return iter->second;
 501}
 502
 503void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image)
 504{
 505	llassert_always(mInitialized) ;
 506	llassert(image);
 507	if (image->isInImageList())
 508	{
 509		llerrs << "LLViewerTextureList::addImageToList - Image already in list" << llendl;
 510	}
 511	if((mImageList.insert(image)).second != true) 
 512	{
 513		llerrs << "Error happens when insert image to mImageList!" << llendl ;
 514	}
 515	
 516	image->setInImageList(TRUE) ;
 517}
 518
 519void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image)
 520{
 521	llassert_always(mInitialized) ;
 522	llassert(image);
 523	if (!image->isInImageList())
 524	{
 525		llinfos << "RefCount: " << image->getNumRefs() << llendl ;
 526		uuid_map_t::iterator iter = mUUIDMap.find(image->getID());
 527		if(iter == mUUIDMap.end() || iter->second != image)
 528		{
 529			llinfos << "Image is not in mUUIDMap!" << llendl ;
 530		}
 531		llerrs << "LLViewerTextureList::removeImageFromList - Image not in list" << llendl;
 532	}
 533
 534	S32 count = mImageList.erase(image) ;
 535	if(count != 1) 
 536	{
 537		llinfos << image->getID() << llendl ;
 538		llerrs << "Error happens when remove image from mImageList: " << count << llendl ;
 539	}
 540      
 541	image->setInImageList(FALSE) ;
 542}
 543
 544void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image)
 545{
 546	if (!new_image)
 547	{
 548		llwarning("No image to add to image list", 0);
 549		return;
 550	}
 551	LLUUID image_id = new_image->getID();
 552	
 553	LLViewerFetchedTexture *image = findImage(image_id);
 554	if (image)
 555	{
 556		llwarns << "Image with ID " << image_id << " already in list" << llendl;
 557	}
 558	sNumImages++;
 559	
 560	addImageToList(new_image);
 561	mUUIDMap[image_id] = new_image;
 562}
 563
 564
 565void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image)
 566{
 567	if( image)
 568	{
 569		if (image->hasCallbacks())
 570		{
 571			mCallbackList.erase(image);
 572		}
 573
 574		llverify(mUUIDMap.erase(image->getID()) == 1);
 575		sNumImages--;
 576		removeImageFromList(image);
 577	}
 578}
 579
 580///////////////////////////////////////////////////////////////////////////////
 581
 582
 583////////////////////////////////////////////////////////////////////////////
 584
 585void LLViewerTextureList::dirtyImage(LLViewerFetchedTexture *image)
 586{
 587	mDirtyTextureList.insert(image);
 588}
 589
 590////////////////////////////////////////////////////////////////////////////
 591static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images");
 592static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_PRIORITIES("Prioritize");
 593static LLFastTimer::DeclareTimer FTM_IMAGE_CALLBACKS("Callbacks");
 594static LLFastTimer::DeclareTimer FTM_IMAGE_FETCH("Fetch");
 595static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create");
 596static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats");
 597
 598void LLViewerTextureList::updateImages(F32 max_time)
 599{
 600	LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec());
 601
 602	LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages);
 603	LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount);
 604	LLViewerStats::getInstance()->mGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes));
 605	LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes));
 606	LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory));
 607	LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory));
 608
 609
 610	{
 611		LLFastTimer t(FTM_IMAGE_UPDATE_PRIORITIES);
 612		updateImagesDecodePriorities();
 613	}
 614
 615	F32 total_max_time = max_time;
 616
 617	{
 618		LLFastTimer t(FTM_IMAGE_FETCH);
 619		max_time -= updateImagesFetchTextures(max_time);
 620	}
 621	
 622	{
 623		LLFastTimer t(FTM_IMAGE_CREATE);
 624		max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time
 625		max_time -= updateImagesCreateTextures(max_time);
 626	}
 627	
 628	if (!mDirtyTextureList.empty())
 629	{
 630		LLFastTimer t(FTM_IMAGE_MARK_DIRTY);
 631		gPipeline.dirtyPoolObjectTextures(mDirtyTextureList);
 632		mDirtyTextureList.clear();
 633	}
 634
 635	{
 636		LLFastTimer t(FTM_IMAGE_CALLBACKS);
 637		bool didone = false;
 638		for (image_list_t::iterator iter = mCallbackList.begin();
 639			iter != mCallbackList.end(); )
 640		{
 641			//trigger loaded callbacks on local textures immediately
 642			LLViewerFetchedTexture* image = *iter++;
 643			if (!image->getUrl().empty())
 644			{
 645				// Do stuff to handle callbacks, update priorities, etc.
 646				didone = image->doLoadedCallbacks();
 647			}
 648			else if (!didone)
 649			{
 650				// Do stuff to handle callbacks, update priorities, etc.
 651				didone = image->doLoadedCallbacks();
 652			}
 653		}
 654	}
 655
 656	{
 657		LLFastTimer t(FTM_IMAGE_STATS);
 658		updateImagesUpdateStats();
 659	}
 660}
 661
 662void LLViewerTextureList::updateImagesDecodePriorities()
 663{
 664	// Update the decode priority for N images each frame
 665	{
 666		const size_t max_update_count = llmin((S32) (1024*gFrameIntervalSeconds) + 1, 32); //target 1024 textures per second
 667		S32 update_counter = llmin(max_update_count, mUUIDMap.size()/10);
 668		uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID);
 669		while(update_counter > 0 && !mUUIDMap.empty())
 670		{
 671			if (iter == mUUIDMap.end())
 672			{
 673				iter = mUUIDMap.begin();
 674			}
 675			mLastUpdateUUID = iter->first;
 676			LLPointer<LLViewerFetchedTexture> imagep = iter->second;
 677			++iter; // safe to incrament now
 678
 679			//
 680			// Flush formatted images using a lazy flush
 681			//
 682			const F32 LAZY_FLUSH_TIMEOUT = 30.f; // stop decoding
 683			const F32 MAX_INACTIVE_TIME  = 50.f; // actually delete
 684			S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference
 685			
 686			S32 num_refs = imagep->getNumRefs();
 687			if (num_refs == min_refs)
 688			{
 689				if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > LAZY_FLUSH_TIMEOUT)
 690				{
 691					// Remove the unused image from the image list
 692					deleteImage(imagep);
 693					imagep = NULL; // should destroy the image								
 694				}
 695				continue;
 696			}
 697			else
 698			{
 699				if(imagep->hasSavedRawImage())
 700				{
 701					if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME)
 702					{
 703						imagep->destroySavedRawImage() ;
 704					}
 705				}
 706
 707				if(imagep->isDeleted())
 708				{
 709					continue ;
 710				}
 711				else if(imagep->isDeletionCandidate())
 712				{
 713					imagep->destroyTexture() ;																
 714					continue ;
 715				}
 716				else if(imagep->isInactive())
 717				{
 718					if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME)
 719					{
 720						imagep->setDeletionCandidate() ;
 721					}
 722					continue ;
 723				}
 724				else
 725				{
 726					imagep->getLastReferencedTimer()->reset();
 727
 728					//reset texture state.
 729					imagep->setInactive() ;										
 730				}
 731			}
 732			
 733			imagep->processTextureStats();
 734			F32 old_priority = imagep->getDecodePriority();
 735			F32 old_priority_test = llmax(old_priority, 0.0f);
 736			F32 decode_priority = imagep->calcDecodePriority();
 737			F32 decode_priority_test = llmax(decode_priority, 0.0f);
 738			// Ignore < 20% difference
 739			if ((decode_priority_test < old_priority_test * .8f) ||
 740				(decode_priority_test > old_priority_test * 1.25f))
 741			{
 742				removeImageFromList(imagep);
 743				imagep->setDecodePriority(decode_priority);
 744				addImageToList(imagep);
 745			}
 746			update_counter--;
 747		}
 748	}
 749}
 750
 751/*
 752 static U8 get_image_type(LLViewerFetchedTexture* imagep, LLHost target_host)
 753 {
 754 // Having a target host implies this is a baked image.  I don't
 755 // believe that boost level has been set at this point. JC
 756 U8 type_from_host = (target_host.isOk() 
 757 ? LLImageBase::TYPE_AVATAR_BAKE 
 758 : LLImageBase::TYPE_NORMAL);
 759 S32 boost_level = imagep->getBoostLevel();
 760 U8 type_from_boost = ( (boost_level == LLViewerFetchedTexture::BOOST_AVATAR_BAKED 
 761 || boost_level == LLViewerFetchedTexture::BOOST_AVATAR_BAKED_SELF)
 762 ? LLImageBase::TYPE_AVATAR_BAKE 
 763 : LLImageBase::TYPE_NORMAL);
 764 if (type_from_host == LLImageBase::TYPE_NORMAL
 765 && type_from_boost == LLImageBase::TYPE_AVATAR_BAKE)
 766 {
 767 llwarns << "TAT: get_image_type() type_from_host doesn't match type_from_boost"
 768 << " host " << target_host
 769 << " boost " << imagep->getBoostLevel()
 770 << " imageid " << imagep->getID()
 771 << llendl;
 772 imagep->dump();
 773 }
 774 return type_from_host;
 775 }
 776 */
 777
 778F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
 779{
 780	if (gGLManager.mIsDisabled) return 0.0f;
 781	
 782	//
 783	// Create GL textures for all textures that need them (images which have been
 784	// decoded, but haven't been pushed into GL).
 785	//
 786		
 787	LLTimer create_timer;
 788	image_list_t::iterator enditer = mCreateTextureList.begin();
 789	for (image_list_t::iterator iter = mCreateTextureList.begin();
 790		 iter != mCreateTextureList.end();)
 791	{
 792		image_list_t::iterator curiter = iter++;
 793		enditer = iter;
 794		LLViewerFetchedTexture *imagep = *curiter;
 795		imagep->createTexture();
 796		if (create_timer.getElapsedTimeF32() > max_time)
 797		{
 798			break;
 799		}
 800	}
 801	mCreateTextureList.erase(mCreateTextureList.begin(), enditer);
 802	return create_timer.getElapsedTimeF32();
 803}
 804
 805void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep)
 806{
 807	if(!imagep)
 808	{
 809		return ;
 810	}
 811	if(imagep->isInImageList())
 812	{
 813		removeImageFromList(imagep);
 814	}
 815
 816	imagep->processTextureStats();
 817	F32 decode_priority = LLViewerFetchedTexture::maxDecodePriority() ;
 818	imagep->setDecodePriority(decode_priority);
 819	addImageToList(imagep);
 820	
 821	return ;
 822}
 823
 824F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
 825{
 826	LLTimer image_op_timer;
 827	
 828	// Update the decode priority for N images each frame
 829	// Make a list with 32 high priority entries + 256 cycled entries
 830	const size_t max_priority_count = llmin((S32) (256*10.f*gFrameIntervalSeconds)+1, 32);
 831	const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256);
 832	
 833	// 32 high priority entries
 834	typedef std::vector<LLViewerFetchedTexture*> entries_list_t;
 835	entries_list_t entries;
 836	size_t update_counter = llmin(max_priority_count, mImageList.size());
 837	image_priority_list_t::iterator iter1 = mImageList.begin();
 838	while(update_counter > 0)
 839	{
 840		entries.push_back(*iter1);
 841		
 842		++iter1;
 843		update_counter--;
 844	}
 845	
 846	// 256 cycled entries
 847	update_counter = llmin(max_update_count, mUUIDMap.size());	
 848	if(update_counter > 0)
 849	{
 850		uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID);
 851		uuid_map_t::iterator iter2p = iter2;
 852		while(update_counter > 0)
 853		{
 854			if (iter2 == mUUIDMap.end())
 855			{
 856				iter2 = mUUIDMap.begin();
 857			}
 858			entries.push_back(iter2->second);
 859			iter2p = iter2++;
 860			update_counter--;
 861		}
 862
 863		mLastFetchUUID = iter2p->first;
 864	}
 865	
 866	S32 fetch_count = 0;
 867	S32 min_count = max_priority_count + max_update_count/4;
 868	for (entries_list_t::iterator iter3 = entries.begin();
 869		 iter3 != entries.end(); )
 870	{
 871		LLViewerFetchedTexture* imagep = *iter3++;
 872		
 873		bool fetching = imagep->updateFetch();
 874		if (fetching)
 875		{
 876			fetch_count++;
 877		}
 878		if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time)
 879		{
 880			break;
 881		}
 882		min_count--;
 883	}
 884	//if (fetch_count == 0)
 885	//{
 886	//	gDebugTimers[0].pause();
 887	//}
 888	//else
 889	//{
 890	//	gDebugTimers[0].unpause();
 891	//}
 892	return image_op_timer.getElapsedTimeF32();
 893}
 894
 895void LLViewerTextureList::updateImagesUpdateStats()
 896{
 897	if (mUpdateStats && mForceResetTextureStats)
 898	{
 899		for (image_priority_list_t::iterator iter = mImageList.begin();
 900			 iter != mImageList.end(); )
 901		{
 902			LLViewerFetchedTexture* imagep = *iter++;
 903			imagep->resetTextureStats();
 904		}
 905		mUpdateStats = FALSE;
 906		mForceResetTextureStats = FALSE;
 907	}
 908}
 909
 910void LLViewerTextureList::decodeAllImages(F32 max_time)
 911{
 912	LLTimer timer;
 913
 914	// Update texture stats and priorities
 915	std::vector<LLPointer<LLViewerFetchedTexture> > image_list;
 916	for (image_priority_list_t::iterator iter = mImageList.begin();
 917		 iter != mImageList.end(); )
 918	{
 919		LLViewerFetchedTexture* imagep = *iter++;
 920		image_list.push_back(imagep);
 921		imagep->setInImageList(FALSE) ;
 922	}
 923
 924	llassert_always(image_list.size() == mImageList.size()) ;
 925	mImageList.clear();
 926	for (std::vector<LLPointer<LLViewerFetchedTexture> >::iterator iter = image_list.begin();
 927		 iter != image_list.end(); ++iter)
 928	{
 929		LLViewerFetchedTexture* imagep = *iter;
 930		imagep->processTextureStats();
 931		F32 decode_priority = imagep->calcDecodePriority();
 932		imagep->setDecodePriority(decode_priority);
 933		addImageToList(imagep);
 934	}
 935	image_list.clear();
 936	
 937	// Update fetch (decode)
 938	for (image_priority_list_t::iterator iter = mImageList.begin();
 939		 iter != mImageList.end(); )
 940	{
 941		LLViewerFetchedTexture* imagep = *iter++;
 942		imagep->updateFetch();
 943	}
 944	// Run threads
 945	S32 fetch_pending = 0;
 946	while (1)
 947	{
 948		LLAppViewer::instance()->getTextureCache()->update(1); // unpauses the texture cache thread
 949		LLAppViewer::instance()->getImageDecodeThread()->update(1); // unpauses the image thread
 950		fetch_pending = LLAppViewer::instance()->getTextureFetch()->update(1); // unpauses the texture fetch thread
 951		if (fetch_pending == 0 || timer.getElapsedTimeF32() > max_time)
 952		{
 953			break;
 954		}
 955	}
 956	// Update fetch again
 957	for (image_priority_list_t::iterator iter = mImageList.begin();
 958		 iter != mImageList.end(); )
 959	{
 960		LLViewerFetchedTexture* imagep = *iter++;
 961		imagep->updateFetch();
 962	}
 963	max_time -= timer.getElapsedTimeF32();
 964	max_time = llmax(max_time, .001f);
 965	F32 create_time = updateImagesCreateTextures(max_time);
 966	
 967	LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " 
 968	<< " fetch_pending " << fetch_pending
 969	<< " create_time " << create_time
 970	<< LL_ENDL;
 971}
 972
 973
 974BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
 975										 const std::string& out_filename,
 976										 const U8 codec)
 977{	
 978	// Load the image
 979	LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
 980	if (image.isNull())
 981	{
 982		image->setLastError("Couldn't open the image to be uploaded.");
 983		return FALSE;
 984	}	
 985	if (!image->load(filename))
 986	{
 987		image->setLastError("Couldn't load the image to be uploaded.");
 988		return FALSE;
 989	}
 990	// Decompress or expand it in a raw image structure
 991	LLPointer<LLImageRaw> raw_image = new LLImageRaw;
 992	if (!image->decode(raw_image, 0.0f))
 993	{
 994		image->setLastError("Couldn't decode the image to be uploaded.");
 995		return FALSE;
 996	}
 997	// Check the image constraints
 998	if ((image->getComponents() != 3) && (image->getComponents() != 4))
 999	{
1000		image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
1001		return FALSE;
1002	}
1003	// Convert to j2c (JPEG2000) and save the file locally
1004	LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image);	
1005	if (compressedImage.isNull())
1006	{
1007		image->setLastError("Couldn't convert the image to jpeg2000.");
1008		llinfos << "Couldn't convert to j2c, file : " << filename << llendl;
1009		return FALSE;
1010	}
1011	if (!compressedImage->save(out_filename))
1012	{
1013		image->setLastError("Couldn't create the jpeg2000 image for upload.");
1014		llinfos << "Couldn't create output file : " << out_filename << llendl;
1015		return FALSE;
1016	}
1017	// Test to see if the encode and save worked
1018	LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
1019	if (!integrity_test->loadAndValidate( out_filename ))
1020	{
1021		image->setLastError("The created jpeg2000 image is corrupt.");
1022		llinfos << "Image file : " << out_filename << " is corrupt" << llendl;
1023		return FALSE;
1024	}
1025	return TRUE;
1026}
1027
1028// note: modifies the argument raw_image!!!!
1029LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image)
1030{
1031	raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
1032	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
1033	compressedImage->setRate(0.f);
1034	
1035	if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
1036		(raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF))
1037		compressedImage->setReversible(TRUE);
1038	
1039
1040	if (gSavedSettings.getBOOL("Jpeg2000AdvancedCompression"))
1041	{
1042		// This test option will create jpeg2000 images with precincts for each level, RPCL ordering
1043		// and PLT markers. The block size is also optionally modifiable.
1044		// Note: the images hence created are compatible with older versions of the viewer.
1045		// Read the blocks and precincts size settings
1046		S32 block_size = gSavedSettings.getS32("Jpeg2000BlocksSize");
1047		S32 precinct_size = gSavedSettings.getS32("Jpeg2000PrecinctsSize");
1048		llinfos << "Advanced JPEG2000 Compression: precinct = " << precinct_size << ", block = " << block_size << llendl;
1049		compressedImage->initEncode(*raw_image, block_size, precinct_size, 0);
1050	}
1051	
1052	if (!compressedImage->encode(raw_image, 0.0f))
1053	{
1054		llinfos << "convertToUploadFile : encode returns with error!!" << llendl;
1055		// Clear up the pointer so we don't leak that one
1056		compressedImage = NULL;
1057	}
1058	
1059	return compressedImage;
1060}
1061
1062const S32 MIN_VIDEO_RAM = 32;
1063const S32 MAX_VIDEO_RAM = 512; // 512MB max for performance reasons.
1064
1065// Returns min setting for TextureMemory (in MB)
1066S32 LLViewerTextureList::getMinVideoRamSetting()
1067{
1068	S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped());
1069	//min texture mem sets to 64M if total physical mem is more than 1.5GB
1070	return (system_ram > 1500) ? 64 : MIN_VIDEO_RAM_IN_MEGA_BYTES ;
1071}
1072
1073//static
1074// Returns max setting for TextureMemory (in MB)
1075S32 LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended)
1076{
1077	S32 max_texmem;
1078	if (gGLManager.mVRAM != 0)
1079	{
1080		// Treat any card with < 32 MB (shudder) as having 32 MB
1081		//  - it's going to be swapping constantly regardless
1082		S32 max_vram = gGLManager.mVRAM;
1083
1084		if(gGLManager.mIsATI)
1085		{
1086			//shrink the availabe vram for ATI cards because some of them do not handel texture swapping well.
1087			max_vram = (S32)(max_vram * 0.75f);  
1088		}
1089
1090		max_vram = llmax(max_vram, getMinVideoRamSetting());
1091		max_texmem = max_vram;
1092		if (!get_recommended)
1093			max_texmem *= 2;
1094	}
1095	else
1096	{
1097		if (!get_recommended)
1098		{
1099			max_texmem = 512;
1100		}
1101		else if (gSavedSettings.getBOOL("NoHardwareProbe")) //did not do hardware detection at startup
1102		{
1103			max_texmem = 512;
1104		}
1105		else
1106		{
1107			max_texmem = 128;
1108		}
1109
1110		llwarns << "VRAM amount not detected, defaulting to " << max_texmem << " MB" << llendl;
1111	}
1112
1113	S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB
1114	//llinfos << "*** DETECTED " << system_ram << " MB of system memory." << llendl;
1115	if (get_recommended)
1116		max_texmem = llmin(max_texmem, (S32)(system_ram/2));
1117	else
1118		max_texmem = llmin(max_texmem, (S32)(system_ram));
1119		
1120	max_texmem = llclamp(max_texmem, getMinVideoRamSetting(), MAX_VIDEO_RAM_IN_MEGA_BYTES); 
1121	
1122	return max_texmem;
1123}
1124
1125const S32 VIDEO_CARD_FRAMEBUFFER_MEM = 12; // MB
1126const S32 MIN_MEM_FOR_NON_TEXTURE = 512 ; //MB
1127void LLViewerTextureList::updateMaxResidentTexMem(S32 mem)
1128{
1129	// Initialize the image pipeline VRAM settings
1130	S32 cur_mem = gSavedSettings.getS32("TextureMemory");
1131	F32 mem_multiplier = gSavedSettings.getF32("RenderTextureMemoryMultiple");
1132	S32 default_mem = getMaxVideoRamSetting(true); // recommended default
1133	if (mem == 0)
1134	{
1135		mem = cur_mem > 0 ? cur_mem : default_mem;
1136	}
1137	else if (mem < 0)
1138	{
1139		mem = default_mem;
1140	}
1141
1142	// limit the texture memory to a multiple of the default if we've found some cards to behave poorly otherwise
1143	mem = llmin(mem, (S32) (mem_multiplier * (F32) default_mem));
1144
1145	mem = llclamp(mem, getMinVideoRamSetting(), getMaxVideoRamSetting());
1146	if (mem != cur_mem)
1147	{
1148		gSavedSettings.setS32("TextureMemory", mem);
1149		return; //listener will re-enter this function
1150	}
1151
1152	// TODO: set available resident texture mem based on use by other subsystems
1153	// currently max(12MB, VRAM/4) assumed...
1154	
1155	S32 vb_mem = mem;
1156	S32 fb_mem = llmax(VIDEO_CARD_FRAMEBUFFER_MEM, vb_mem/4);
1157	mMaxResidentTexMemInMegaBytes = (vb_mem - fb_mem) ; //in MB
1158	
1159	mMaxTotalTextureMemInMegaBytes = mMaxResidentTexMemInMegaBytes * 2;
1160	if (mMaxResidentTexMemInMegaBytes > 640)
1161	{
1162		mMaxTotalTextureMemInMegaBytes -= (mMaxResidentTexMemInMegaBytes >> 2);
1163	}
1164
1165	//system mem
1166	S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB
1167
1168	//minimum memory reserved for non-texture use.
1169	//if system_raw >= 1GB, reserve at least 512MB for non-texture use;
1170	//otherwise reserve half of the system_ram for non-texture use.
1171	S32 min_non_texture_mem = llmin(system_ram / 2, MIN_MEM_FOR_NON_TEXTURE) ; 
1172
1173	if (mMaxTotalTextureMemInMegaBytes > system_ram - min_non_texture_mem)
1174	{
1175		mMaxTotalTextureMemInMegaBytes = system_ram - min_non_texture_mem ;
1176	}
1177	
1178	llinfos << "Total Video Memory set to: " << vb_mem << " MB" << llendl;
1179	llinfos << "Available Texture Memory set to: " << (vb_mem - fb_mem) << " MB" << llendl;
1180}
1181
1182///////////////////////////////////////////////////////////////////////////////
1183
1184// static
1185void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_data)
1186{
1187	static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
1188
1189	LLFastTimer t(FTM_PROCESS_IMAGES);
1190	
1191	// Receive image header, copy into image object and decompresses 
1192	// if this is a one-packet image. 
1193	
1194	LLUUID id;
1195	
1196	char ip_string[256];
1197	u32_to_ip_string(msg->getSenderIP(),ip_string);
1198	
1199	U32 received_size ;
1200	if (msg->getReceiveCompressedSize())
1201	{
1202		received_size = msg->getReceiveCompressedSize() ;		
1203	}
1204	else
1205	{
1206		received_size = msg->getReceiveSize() ;		
1207	}
1208	gTextureList.sTextureBits += received_size * 8;
1209	gTextureList.sTexturePackets++;
1210	
1211	U8 codec;
1212	U16 packets;
1213	U32 totalbytes;
1214	msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
1215	msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, codec);
1216	msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, packets);
1217	msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, totalbytes);
1218	
1219	S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
1220	if (!data_size)
1221	{
1222		return;
1223	}
1224	if (data_size < 0)
1225	{
1226		// msg->getSizeFast() is probably trying to tell us there
1227		// was an error.
1228		llerrs << "image header chunk size was negative: "
1229		<< data_size << llendl;
1230		return;
1231	}
1232	
1233	// this buffer gets saved off in the packet list
1234	U8 *data = new U8[data_size];
1235	msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
1236	
1237	LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
1238	if (!image)
1239	{
1240		delete [] data;
1241		return;
1242	}
1243	if(log_texture_traffic)
1244	{
1245		gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ;
1246	}
1247
1248	//image->getLastPacketTimer()->reset();
1249	bool res = LLAppViewer::getTextureFetch()->receiveImageHeader(msg->getSender(), id, codec, packets, totalbytes, data_size, data);
1250	if (!res)
1251	{
1252		delete[] data;
1253	}
1254}
1255
1256// static
1257void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_data)
1258{
1259	static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
1260
1261	LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
1262	LLFastTimer t(FTM_PROCESS_IMAGES);
1263	
1264	// Receives image packet, copy into image object,
1265	// checks if all packets received, decompresses if so. 
1266	
1267	LLUUID id;
1268	U16 packet_num;
1269	
1270	char ip_string[256];
1271	u32_to_ip_string(msg->getSenderIP(),ip_string);
1272	
1273	U32 received_size ;
1274	if (msg->getReceiveCompressedSize())
1275	{
1276		received_size = msg->getReceiveCompressedSize() ;
1277	}
1278	else
1279	{
1280		received_size = msg->getReceiveSize() ;		
1281	}
1282	gTextureList.sTextureBits += received_size * 8;
1283	gTextureList.sTexturePackets++;
1284	
1285	//llprintline("Start decode, image header...");
1286	msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
1287	msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num);
1288	S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
1289	
1290	if (!data_size)
1291	{
1292		return;
1293	}
1294	if (data_size < 0)
1295	{
1296		// msg->getSizeFast() is probably trying to tell us there
1297		// was an error.
1298		llerrs << "image data chunk size was negative: "
1299		<< data_size << llendl;
1300		return;
1301	}
1302	if (data_size > MTUBYTES)
1303	{
1304		llerrs << "image data chunk too large: " << data_size << " bytes" << llendl;
1305		return;
1306	}
1307	U8 *data = new U8[data_size];
1308	msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
1309	
1310	LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
1311	if (!image)
1312	{
1313		delete [] data;
1314		return;
1315	}
1316	if(log_texture_traffic)
1317	{
1318		gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ;
1319	}
1320
1321	//image->getLastPacketTimer()->reset();
1322	bool res = LLAppViewer::getTextureFetch()->receiveImagePacket(msg->getSender(), id, packet_num, data_size, data);
1323	if (!res)
1324	{
1325		delete[] data;
1326	}
1327}
1328
1329
1330// We've been that the asset server does not contain the requested image id.
1331// static
1332void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void **user_data)
1333{
1334	LLFastTimer t(FTM_PROCESS_IMAGES);
1335	LLUUID image_id;
1336	msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, image_id);
1337	
1338	LLViewerFetchedTexture* image = gTextureList.findImage( image_id );
1339	if( image )
1340	{
1341		image->setIsMissingAsset();
1342	}
1343}
1344
1345///////////////////////////////////////////////////////////////////////////////
1346
1347//static
1348const U32 SIXTEEN_MEG = 0x1000000;
1349S32 LLViewerTextureList::calcMaxTextureRAM()
1350{
1351	// Decide the maximum amount of RAM we should allow the user to allocate to texture cache
1352	LLMemoryInfo memory_info;
1353	U32 available_memory = memory_info.getPhysicalMemoryClamped();
1354	
1355	clamp_rescale((F32)available_memory,
1356				  (F32)(SIXTEEN_MEG * 16),
1357				  (F32)U32_MAX,
1358				  (F32)(SIXTEEN_MEG * 4),
1359				  (F32)(U32_MAX >> 1));
1360	return available_memory;
1361}
1362
1363///////////////////////////////////////////////////////////////////////////////
1364
1365// explicitly cleanup resources, as this is a singleton class with process
1366// lifetime so ability to perform std::map operations in destructor is not
1367// guaranteed.
1368void LLUIImageList::cleanUp()
1369{
1370	mUIImages.clear();
1371	mUITextureList.clear() ;
1372}
1373
1374LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id, S32 priority)
1375{
1376	// use id as image name
1377	std::string image_name = image_id.asString();
1378
1379	// look for existing image
1380	uuid_ui_image_map_t::iterator found_it = mUIImages.find(image_name);
1381	if (found_it != mUIImages.end())
1382	{
1383		return found_it->second;
1384	}
1385
1386	const BOOL use_mips = FALSE;
1387	const LLRect scale_rect = LLRect::null;
1388	const LLRect clip_rect = LLRect::null;
1389	return loadUIImageByID(image_id, use_mips, scale_rect, clip_rect, (LLViewerTexture::EBoostLevel)priority);
1390}
1391
1392LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priority)
1393{
1394	// look for existing image
1395	uuid_ui_image_map_t::iterator found_it = mUIImages.find(image_name);
1396	if (found_it != mUIImages.end())
1397	{
1398		return found_it->second;
1399	}
1400
1401	const BOOL use_mips = FALSE;
1402	const LLRect scale_rect = LLRect::null;
1403	const LLRect clip_rect = LLRect::null;
1404	return loadUIImageByName(image_name, image_name, use_mips, scale_rect, clip_rect, (LLViewerTexture::EBoostLevel)priority);
1405}
1406
1407LLUIImagePtr LLUIImageList::loadUIImageByName(const std::string& name, const std::string& filename,
1408											  BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority )
1409{
1410	if (boost_priority == LLViewerTexture::BOOST_NONE)
1411	{
1412		boost_priority = LLViewerTexture::BOOST_UI;
1413	}
1414	LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTextureFromFile(filename, MIPMAP_NO, boost_priority);
1415	return loadUIImage(imagep, name, use_mips, scale_rect, clip_rect);
1416}
1417
1418LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id,
1419											BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority)
1420{
1421	if (boost_priority == LLViewerTexture::BOOST_NONE)
1422	{
1423		boost_priority = LLViewerTexture::BOOST_UI;
1424	}
1425	LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(id, MIPMAP_NO, boost_priority);
1426	return loadUIImage(imagep, id.asString(), use_mips, scale_rect, clip_rect);
1427}
1428
1429LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const std::string& name, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect)
1430{
1431	if (!imagep) return NULL;
1432
1433	imagep->setAddressMode(LLTexUnit::TAM_CLAMP);
1434
1435	//all UI images are non-deletable
1436	imagep->setNoDelete();
1437
1438	LLUIImagePtr new_imagep = new LLUIImage(name, imagep);
1439	mUIImages.insert(std::make_pair(name, new_imagep));
1440	mUITextureList.push_back(imagep);
1441
1442	//Note:
1443	//Some other textures such as ICON also through this flow to be fetched.
1444	//But only UI textures need to set this callback.
1445	if(imagep->getBoostLevel() == LLViewerTexture::BOOST_UI)
1446	{
1447		LLUIImageLoadData* datap = new LLUIImageLoadData;
1448		datap->mImageName = name;
1449		datap->mImageScaleRegion = scale_rect;
1450		datap->mImageClipRegion = clip_rect;
1451
1452		imagep->setLoadedCallback(onUIImageLoaded, 0, FALSE, FALSE, datap, NULL);
1453	}
1454	return new_imagep;
1455}
1456
1457LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::string& filename, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect)
1458{
1459	// look for existing image
1460	uuid_ui_image_map_t::iterator found_it = mUIImages.find(name);
1461	if (found_it != mUIImages.end())
1462	{
1463		// image already loaded!
1464		llerrs << "UI Image " << name << " already loaded." << llendl;
1465	}
1466
1467	return loadUIImageByName(name, filename, use_mips, scale_rect, clip_rect);
1468}
1469
1470//static 
1471void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* user_data )
1472{
1473	if(!success || !user_data) 
1474	{
1475		return;
1476	}
1477
1478	LLUIImageLoadData* image_datap = (LLUIImageLoadData*)user_data;
1479	std::string ui_image_name = image_datap->mImageName;
1480	LLRect scale_rect = image_datap->mImageScaleRegion;
1481	LLRect clip_rect = image_datap->mImageClipRegion;
1482	if (final)
1483	{
1484		delete image_datap;
1485	}
1486
1487	LLUIImageList* instance = getInstance();
1488
1489	uuid_ui_image_map_t::iterator found_it = instance->mUIImages.find(ui_image_name);
1490	if (found_it != instance->mUIImages.end())
1491	{
1492		LLUIImagePtr imagep = found_it->second;
1493
1494		// for images grabbed from local files, apply clipping rectangle to restore original dimensions
1495		// from power-of-2 gl image
1496		if (success && imagep.notNull() && src_vi && (src_vi->getUrl().compare(0, 7, "file://")==0))
1497		{
1498			F32 full_width = (F32)src_vi->getFullWidth();
1499			F32 full_height = (F32)src_vi->getFullHeight();
1500			F32 clip_x = (F32)src_vi->getOriginalWidth() / full_width;
1501			F32 clip_y = (F32)src_vi->getOriginalHeight() / full_height;
1502			if (clip_rect != LLRect::null)
1503			{
1504				imagep->setClipRegion(LLRectf(llclamp((F32)clip_rect.mLeft / full_width, 0.f, 1.f),
1505											llclamp((F32)clip_rect.mTop / full_height, 0.f, 1.f),
1506											llclamp((F32)clip_rect.mRight / full_width, 0.f, 1.f),
1507											llclamp((F32)clip_rect.mBottom / full_height, 0.f, 1.f)));
1508			}
1509			else
1510			{
1511				imagep->setClipRegion(LLRectf(0.f, clip_y, clip_x, 0.f));
1512			}
1513			if (scale_rect != LLRect::null)
1514			{
1515				imagep->setScaleRegion(
1516					LLRectf(llclamp((F32)scale_rect.mLeft / (F32)imagep->getWidth(), 0.f, 1.f),
1517						llclamp((F32)scale_rect.mTop / (F32)imagep->getHeight(), 0.f, 1.f),
1518						llclamp((F32)scale_rect.mRight / (F32)imagep->getWidth(), 0.f, 1.f),
1519						llclamp((F32)scale_rect.mBottom / (F32)imagep->getHeight(), 0.f, 1.f)));
1520			}
1521
1522			imagep->onImageLoaded();
1523		}
1524	}
1525}
1526
1527struct UIImageDeclaration : public LLInitParam::Block<UIImageDeclaration>
1528{
1529	Mandatory<std::string>	name;
1530	Optional<std::string>	file_name;
1531	Optional<bool>			preload;
1532	Optional<LLRect>		scale;
1533	Optional<LLRect>		clip;
1534	Optional<bool>			use_mips;
1535
1536	UIImageDeclaration()
1537	:	name("name"),
1538		file_name("file_name"),
1539		preload("preload", false),
1540		scale("scale"),
1541		clip("clip"),
1542		use_mips("use_mips", false)
1543	{}
1544};
1545
1546struct UIImageDeclarations : public LLInitParam::Block<UIImageDeclarations>
1547{
1548	Mandatory<S32>	version;
1549	Multiple<UIImageDeclaration> textures;
1550
1551	UIImageDeclarations()
1552	:	version("version"),
1553		textures("texture")
1554	{}
1555};
1556
1557bool LLUIImageList::initFromFile()
1558{
1559	// construct path to canonical textures.xml in default skin dir
1560	std::string base_file_path = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "default", "textures", "textures.xml");
1561
1562	LLXMLNodePtr root;
1563
1564	if (!LLXMLNode::parseFile(base_file_path, root, NULL))
1565	{
1566		llwarns << "Unable to parse UI image list file " << base_file_path << llendl;
1567		return false;
1568	}
1569	if (!root->hasAttribute("version"))
1570	{
1571		llwarns << "No valid version number in UI image list file " << base_file_path << llendl;
1572		return false;
1573	}
1574
1575	UIImageDeclarations images;
1576	LLXUIParser parser;
1577	parser.readXUI(root, images, base_file_path);
1578
1579	// add components defined in current skin
1580	std::string skin_update_path = gDirUtilp->getSkinDir() 
1581									+ gDirUtilp->getDirDelimiter() 
1582									+ "textures"
1583									+ gDirUtilp->getDirDelimiter()
1584									+ "textures.xml";
1585	LLXMLNodePtr update_root;
1586	if (skin_update_path != base_file_path
1587		&& LLXMLNode::parseFile(skin_update_path, update_root, NULL))
1588	{
1589		parser.readXUI(update_root, images, skin_update_path);
1590	}
1591
1592	// add components defined in user override of current skin
1593	skin_update_path = gDirUtilp->getUserSkinDir() 
1594						+ gDirUtilp->getDirDelimiter() 
1595						+ "textures"
1596						+ gDirUtilp->getDirDelimiter()
1597						+ "textures.xml";
1598	if (skin_update_path != base_file_path
1599		&& LLXMLNode::parseFile(skin_update_path, update_root, NULL))
1600	{
1601		parser.readXUI(update_root, images, skin_update_path);
1602	}
1603
1604	if (!images.validateBlock()) return false;
1605
1606	std::map<std::string, UIImageDeclaration> merged_declarations;
1607	for (LLInitParam::ParamIterator<UIImageDeclaration>::const_iterator image_it = images.textures.begin();
1608		image_it != images.textures.end();
1609		++image_it)
1610	{
1611		merged_declarations[image_it->name].overwriteFrom(*image_it);
1612	}
1613
1614	enum e_decode_pass
1615	{
1616		PASS_DECODE_NOW,
1617		PASS_DECODE_LATER,
1618		NUM_PASSES
1619	};
1620
1621	for (S32 cur_pass = PASS_DECODE_NOW; cur_pass < NUM_PASSES; cur_pass++)
1622	{
1623		for (std::map<std::string, UIImageDeclaration>::const_iterator image_it = merged_declarations.begin();
1624			image_it != merged_declarations.end();
1625			++image_it)
1626		{
1627			const UIImageDeclaration& image = image_it->second;
1628			std::string file_name = image.file_name.isProvided() ? image.file_name() : image.name();
1629
1630			// load high priority textures on first pass (to kick off decode)
1631			enum e_decode_pass decode_pass = image.preload ? PASS_DECODE_NOW : PASS_DECODE_LATER;
1632			if (decode_pass != cur_pass)
1633			{
1634				continue;
1635			}
1636			preloadUIImage(image.name, file_name, image.use_mips, image.scale, image.clip);
1637		}
1638
1639		if (cur_pass == PASS_DECODE_NOW && !gSavedSettings.getBOOL("NoPreload"))
1640		{
1641			gTextureList.decodeAllImages(10.f); // decode preloaded images
1642		}
1643	}
1644	return true;
1645}
1646
1647